2 NEC-HE PC Engine Emulator 'ePCEngine'
3 SHARP X1twin Emulator 'eX1twin'
5 Origin : Ootake (joypad)
8 Author : Takeda.Toshiya
16 #include "../huc6280.h"
23 /* Bits in the VDC status register */
25 #define VDC_BSY 0x40 /* Set when the VDC accesses VRAM */
26 #define VDC_VD 0x20 /* Set when in the vertical blanking period */
27 #define VDC_DV 0x10 /* Set when a VRAM > VRAM DMA transfer is done */
28 #define VDC_DS 0x08 /* Set when a VRAM > SATB DMA transfer is done */
29 #define VDC_RR 0x04 /* Set when the current scanline equals the RCR register */
30 #define VDC_OR 0x02 /* Set when there are more than 16 sprites on a line */
31 #define VDC_CR 0x01 /* Set when sprite #0 overlaps with another sprite */
33 /* Bits in the CR register */
35 #define CR_BB 0x80 /* Background blanking */
36 #define CR_SB 0x40 /* Object blanking */
37 #define CR_VR 0x08 /* Interrupt on vertical blank enable */
38 #define CR_RC 0x04 /* Interrupt on line compare enable */
39 #define CR_OV 0x02 /* Interrupt on sprite overflow enable */
40 #define CR_CC 0x01 /* Interrupt on sprite #0 collision enable */
42 /* Bits in the DCR regsiter */
44 #define DCR_DSR 0x10 /* VRAM > SATB auto-transfer enable */
45 #define DCR_DID 0x08 /* Destination diretion */
46 #define DCR_SID 0x04 /* Source direction */
47 #define DCR_DVC 0x02 /* VRAM > VRAM EOT interrupt enable */
48 #define DCR_DSC 0x01 /* VRAM > SATB EOT interrupt enable */
50 /* just to keep things simple... */
51 enum vdc_regs {MAWR = 0, MARR, VxR, reg3, reg4, CR, RCR, BXR, BYR, MWR, HSR, HDR, VPR, VDW, VCR, DCR, SOUR, DESR, LENR, DVSSR };
55 CLEAR_LINE = 0, // clear (a fired or held) line
56 ASSERT_LINE, // assert an interrupt immediately
57 HOLD_LINE, // hold interrupt line until acknowledged
58 PULSE_LINE // pulse interrupt line instantaneously (only for NMI, RESET)
69 void PCE::initialize()
72 joy_stat = emu->joy_buffer();
73 key_stat = emu->key_buffer();
76 register_vline_event(this);
78 #ifdef SUPPORT_BACKUP_RAM
79 static const uint8 image[8] = {0x48, 0x55, 0x42, 0x4d, 0x00, 0x88, 0x10, 0x80};
80 memset(backup, 0, sizeof(backup));
81 memcpy(backup, image, sizeof(image));
83 FILEIO* fio = new FILEIO();
84 if(fio->Fopen(create_local_path(_T("BACKUP.BIN")), FILEIO_READ_BINARY)) {
85 fio->Fread(backup, sizeof(backup), 1);
90 backup_crc32 = getcrc32(backup, sizeof(backup));
97 #ifdef SUPPORT_BACKUP_RAM
98 if(backup_crc32 != getcrc32(backup, sizeof(backup))) {
99 FILEIO* fio = new FILEIO();
100 if(fio->Fopen(create_local_path(_T("BACKUP.BIN")), FILEIO_WRITE_BINARY)) {
101 fio->Fwrite(backup, sizeof(backup), 1);
112 memset(ram, 0, sizeof(ram));
114 buffer = 0xff; // ???
124 void PCE::event_vline(int v, int clock)
126 #ifdef SUPPORT_SUPER_GFX
134 void PCE::write_data8(uint32 addr, uint32 data)
136 uint8 mpr = (addr >> 13) & 0xff;
137 uint16 ofs = addr & 0x1fff;
145 cart[addr & 0xfffff] = data;
147 #ifdef SUPPORT_BACKUP_RAM
157 #ifdef SUPPORT_SUPER_GFX
162 ram[addr & 0x7fff] = data;
167 switch(addr & 0x1c00) {
169 #ifdef SUPPORT_SUPER_GFX
171 switch(addr & 0x18) {
173 vdc_w(0, addr, data);
179 vdc_w(1, addr, data);
184 vdc_w(0, addr, data);
191 psg_write(addr, data);
193 case 0x0c00: // timer
195 d_cpu->timer_w(addr, data);
197 case 0x1000: // joypad
199 joy_write(addr, data);
201 case 0x1400: // interrupt control
203 d_cpu->irq_status_w(addr, data);
208 // bank switch for sf2d
209 if((addr & 0x1ffc) == 0x1ff0) {
210 bank = 0x80000 * ((addr & 3) + 1);
214 uint32 PCE::read_data8(uint32 addr)
216 uint8 mpr = (addr >> 13) & 0xff;
217 uint16 ofs = addr & 0x1fff;
220 return cart[addr & 0x7ffff];
223 return cart[bank | (addr & 0x7ffff)];
226 #ifdef SUPPORT_BACKUP_RAM
232 #ifdef SUPPORT_SUPER_GFX
237 return ram[addr & 0x7fff];
242 switch (addr & 0x1c00) {
244 #ifdef SUPPORT_SUPER_GFX
246 switch(addr & 0x18) {
248 return vdc_r(0, addr);
252 return vdc_r(1, addr);
257 return vdc_r(0, addr);
261 // return psg_read(addr);
263 case 0x0c00: // timer
264 buffer = (buffer & 0x80) | (d_cpu->timer_r(addr) & 0x7f);
266 case 0x1000: // joypad
267 buffer = (buffer & 0xb0) | (joy_read(addr) & 0x0f);
269 case 0x1400: // interrupt control
271 buffer = (buffer & 0xf8) | (d_cpu->irq_status_r(addr) & 0x07);
280 void PCE::write_io8(uint32 addr, uint32 data)
282 #ifdef SUPPORT_SUPER_GFX
284 sgx_vdc_w(addr, data);
287 vdc_w(0, addr, data);
290 uint32 PCE::read_io8(uint32 addr)
292 #ifdef SUPPORT_SUPER_GFX
294 return sgx_vdc_r(addr);
297 return vdc_r(0, addr);
300 void PCE::draw_screen()
302 int dx = (SCREEN_WIDTH - vdc[0].physical_width) / 2, sx = 0;
303 int dy = (SCREEN_HEIGHT - 238) / 2;
310 if(prev_width != vdc[0].physical_width) {
311 for(int y = 0; y < SCREEN_HEIGHT; y++) {
312 memset(emu->screen_buffer(y), 0, sizeof(scrntype) * SCREEN_WIDTH);
314 prev_width = vdc[0].physical_width;
317 for(int y = 0; y < 238; y++, dy++) {
318 scrntype* src = &vce.bmp[y + 17][86];
319 scrntype* dst = emu->screen_buffer(dy);
320 for(int x = sx, x2 = dx; x < vdc[0].physical_width && x2 < SCREEN_WIDTH; x++, x2++) {
326 void PCE::open_cart(const _TCHAR* file_path)
328 FILEIO* fio = new FILEIO();
330 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
331 memset(cart, 0xff, sizeof(cart));
332 fio->Fseek(0, FILEIO_SEEK_END);
333 int size = fio->Ftell();
334 int head = size % 1024;
336 fio->Fseek(head, FILEIO_SEEK_SET);
337 fio->Fread(cart, size, 1);
340 if(size == 512 * 1024) {
341 bool overdump = true;
342 for(int i = 0x40000; i < 0x60000; i++) {
343 if(cart[i] != cart[i + 0x20000]) {
352 if(size == 384 * 1024) {
353 memcpy(cart + 0x060000, cart + 0x040000, 0x020000); /* Set up 060000 - 07FFFF mirror */
354 memcpy(cart + 0x080000, cart + 0x040000, 0x040000); /* Set up 080000 - 0BFFFF region */
355 memcpy(cart + 0x0C0000, cart + 0x040000, 0x040000); /* Set up 0C0000 - 0FFFFF region */
356 memcpy(cart + 0x040000, cart, 0x040000); /* Set up 040000 - 07FFFF region */
359 /* mirror 256KB rom data */
360 if (size <= 0x040000)
361 memcpy(cart + 0x040000, cart, 0x040000);
362 /* mirror 512KB rom data */
363 if (size <= 0x080000)
364 memcpy(cart + 0x080000, cart, 0x080000);
366 uint32 cart_crc32 = getcrc32(cart,size);
367 support_sgfx = (size == 0x100000 && cart_crc32 == 0x8c4588e2) // 1941 Counter Attack
368 || (size == 0x100000 && cart_crc32 == 0x4c2126b0) // Aldynes
369 || (size == 0x080000 && cart_crc32 == 0x3b13af61) // Battle Ace
370 || (size == 0x100000 && cart_crc32 == 0xb486a8ed) // Daimakaimura
371 || (size == 0x0c0000 && cart_crc32 == 0xbebfe042) // Darius Plus
372 || (size == 0x100000 && cart_crc32 == 0x1e1d0319) // Darius Plus (1024K)
373 || (size == 0x080000 && cart_crc32 == 0x1f041166); // Grandzort
374 support_6btn = (size == 0x280000 && cart_crc32 == 0xd15cb6bb); // Street Fighter II
380 void PCE::close_cart()
382 memset(cart, 0xff, sizeof(cart));
388 void PCE::pce_interrupt()
390 /* Draw the last scanline */
391 if ( vce.current_bitmap_line >= 14 && vce.current_bitmap_line < 14 + 242 )
393 /* We are in the active display area */
394 /* First fill the line with the overscan color */
395 draw_overscan_line(vce.current_bitmap_line );
397 /* Check if we need to draw more just the overscan color */
398 if ( vdc[0].current_segment == STATE_VDW )
400 /* 0 - no sprite and background pixels drawn
401 1 - background pixel drawn
402 otherwise is 2 + sprite# */
403 uint8 drawn[VDC_WPF];
404 /* our line buffer */
405 scrntype *line_buffer = &vce.bmp[vce.current_bitmap_line][86];
407 /* clear our priority/sprite collision detection buffer. */
408 memset(drawn, 0, VDC_WPF);
410 vdc[0].y_scroll = ( vdc[0].current_segment_line == 0 ) ? vdc[0].vdc_data[BYR].w.l : ( vdc[0].y_scroll + 1 );
412 /* Draw VDC #0 background layer */
413 pce_refresh_line(0, vdc[0].current_segment_line, 0, drawn, line_buffer);
415 /* Draw VDC #0 sprite layer */
416 if(vdc[0].vdc_data[CR].w.l & CR_SB)
418 pce_refresh_sprites(0, vdc[0].current_segment_line, drawn, line_buffer);
424 /* We are in one of the blanking areas */
425 draw_black_line(vce.current_bitmap_line );
428 /* bump current scanline */
429 vce.current_bitmap_line = ( vce.current_bitmap_line + 1 ) % VDC_LPF;
433 void PCE::sgx_interrupt()
435 /* Draw the last scanline */
436 if ( vce.current_bitmap_line >= 14 && vce.current_bitmap_line < 14 + 242 )
438 /* We are in the active display area */
439 /* First fill the line with the overscan color */
440 draw_sgx_overscan_line(vce.current_bitmap_line );
442 /* Check if we need to draw more just the overscan color */
443 if ( vdc[0].current_segment == STATE_VDW )
445 /* 0 - no sprite and background pixels drawn
446 1 - background pixel drawn
447 otherwise is 2 + sprite# */
449 scrntype *line_buffer;
450 scrntype temp_buffer[2][512];
453 /* clear our priority/sprite collision detection buffer. */
454 memset( drawn, 0, sizeof(drawn) );
456 vdc[0].y_scroll = ( vdc[0].current_segment_line == 0 ) ? vdc[0].vdc_data[BYR].w.l : ( vdc[0].y_scroll + 1 );
457 vdc[1].y_scroll = ( vdc[1].current_segment_line == 0 ) ? vdc[1].vdc_data[BYR].w.l : ( vdc[1].y_scroll + 1 );
459 /* Draw VDC #0 background layer */
460 pce_refresh_line( 0, vdc[0].current_segment_line, 0, drawn[0], temp_buffer[0]);
462 /* Draw VDC #0 sprite layer */
463 if(vdc[0].vdc_data[CR].w.l & CR_SB)
465 pce_refresh_sprites(0, vdc[0].current_segment_line, drawn[0], temp_buffer[0]);
468 /* Draw VDC #1 background layer */
469 pce_refresh_line( 1, vdc[1].current_segment_line, 1, drawn[1], temp_buffer[1]);
471 /* Draw VDC #1 sprite layer */
472 if ( vdc[1].vdc_data[CR].w.l & CR_SB )
474 pce_refresh_sprites(1, vdc[1].current_segment_line, drawn[1], temp_buffer[1]);
477 line_buffer = &vce.bmp[vce.current_bitmap_line][86];
478 /* Combine the output of both VDCs */
479 for( i = 0; i < 512; i++ )
481 int cur_prio = vpc.prio_map[i];
483 if ( vpc.vpc_prio[cur_prio].vdc0_enabled )
485 if ( vpc.vpc_prio[cur_prio].vdc1_enabled )
487 switch( vpc.vpc_prio[cur_prio].prio )
489 case 0: /* BG1 SP1 BG0 SP0 */
492 line_buffer[i] = temp_buffer[0][i];
494 else if ( drawn[1][i] )
496 line_buffer[i] = temp_buffer[1][i];
499 case 1: /* BG1 BG0 SP1 SP0 */
502 if ( drawn[0][i] > 1 )
504 line_buffer[i] = temp_buffer[0][i];
508 if ( drawn[1][i] > 1 )
510 line_buffer[i] = temp_buffer[1][i];
514 line_buffer[i] = temp_buffer[0][i];
518 else if ( drawn[1][i] )
520 line_buffer[i] = temp_buffer[1][i];
523 case 2: /* BG1 + SP1 => SP1
531 if ( drawn[0][i] > 1 )
533 if ( drawn[1][i] == 1 )
535 line_buffer[i] = temp_buffer[1][i];
539 line_buffer[i] = temp_buffer[0][i];
544 line_buffer[i] = temp_buffer[0][i];
547 else if ( drawn[1][i] )
549 line_buffer[i] = temp_buffer[1][i];
558 line_buffer[i] = temp_buffer[0][i];
564 if ( vpc.vpc_prio[cur_prio].vdc1_enabled )
568 line_buffer[i] = temp_buffer[1][i];
577 /* We are in one of the blanking areas */
578 draw_black_line(vce.current_bitmap_line );
581 /* bump current scanline */
582 vce.current_bitmap_line = ( vce.current_bitmap_line + 1 ) % VDC_LPF;
587 void PCE::vdc_advance_line(int which)
591 vdc[which].curline += 1;
592 vdc[which].current_segment_line += 1;
593 vdc[which].raster_count += 1;
595 if ( vdc[which].satb_countdown )
597 vdc[which].satb_countdown -= 1;
598 if ( vdc[which].satb_countdown == 0 )
600 if ( vdc[which].vdc_data[DCR].w.l & DCR_DSC )
602 vdc[which].status |= VDC_DS; /* set satb done flag */
608 if ( vce.current_bitmap_line == 0 )
610 vdc[which].current_segment = STATE_VSW;
611 vdc[which].current_segment_line = 0;
612 vdc[which].vblank_triggered = 0;
613 vdc[which].curline = 0;
616 if ( STATE_VSW == vdc[which].current_segment && vdc[which].current_segment_line >= ( vdc[which].vdc_data[VPR].b.l & 0x1F ) )
618 vdc[which].current_segment = STATE_VDS;
619 vdc[which].current_segment_line = 0;
622 if ( STATE_VDS == vdc[which].current_segment && vdc[which].current_segment_line >= vdc[which].vdc_data[VPR].b.h )
624 vdc[which].current_segment = STATE_VDW;
625 vdc[which].current_segment_line = 0;
626 vdc[which].raster_count = 0x40;
629 if ( STATE_VDW == vdc[which].current_segment && vdc[which].current_segment_line > ( vdc[which].vdc_data[VDW].w.l & 0x01FF ) )
631 vdc[which].current_segment = STATE_VCR;
632 vdc[which].current_segment_line = 0;
634 /* Generate VBlank interrupt, sprite DMA */
635 vdc[which].vblank_triggered = 1;
636 if ( vdc[which].vdc_data[CR].w.l & CR_VR )
638 vdc[which].status |= VDC_VD;
642 /* do VRAM > SATB DMA if the enable bit is set or the DVSSR reg. was written to */
643 if( ( vdc[which].vdc_data[DCR].w.l & DCR_DSR ) || vdc[which].dvssr_write )
647 vdc[which].dvssr_write = 0;
649 for( i = 0; i < 256; i++ )
651 vdc[which].sprite_ram[i] = ( vdc[which].vram[ ( vdc[which].vdc_data[DVSSR].w.l << 1 ) + i * 2 + 1 ] << 8 ) | vdc[which].vram[ ( vdc[which].vdc_data[DVSSR].w.l << 1 ) + i * 2 ];
654 /* generate interrupt if needed */
655 if ( vdc[which].vdc_data[DCR].w.l & DCR_DSC )
657 vdc[which].satb_countdown = 4;
662 if ( STATE_VCR == vdc[which].current_segment )
664 if ( vdc[which].current_segment_line >= 3 && vdc[which].current_segment_line >= vdc[which].vdc_data[VCR].b.l )
666 vdc[which].current_segment = STATE_VSW;
667 vdc[which].current_segment_line = 0;
668 vdc[which].curline = 0;
672 /* generate interrupt on line compare if necessary */
673 if ( vdc[which].raster_count == vdc[which].vdc_data[RCR].w.l && vdc[which].vdc_data[CR].w.l & CR_RC )
675 vdc[which].status |= VDC_RR;
679 /* handle frame events */
680 if(vdc[which].curline == 261 && ! vdc[which].vblank_triggered )
683 vdc[which].vblank_triggered = 1;
684 if(vdc[which].vdc_data[CR].w.l & CR_VR)
685 { /* generate IRQ1 if enabled */
686 vdc[which].status |= VDC_VD; /* set vblank flag */
690 /* do VRAM > SATB DMA if the enable bit is set or the DVSSR reg. was written to */
691 if ( ( vdc[which].vdc_data[DCR].w.l & DCR_DSR ) || vdc[which].dvssr_write )
695 vdc[which].dvssr_write = 0;
696 for( i = 0; i < 256; i++ )
698 vdc[which].sprite_ram[i] = ( vdc[which].vram[ ( vdc[which].vdc_data[DVSSR].w.l << 1 ) + i * 2 + 1 ] << 8 ) | vdc[which].vram[ ( vdc[which].vdc_data[DVSSR].w.l << 1 ) + i * 2 ];
701 /* generate interrupt if needed */
702 if(vdc[which].vdc_data[DCR].w.l & DCR_DSC)
704 vdc[which].satb_countdown = 4;
710 d_cpu->write_signal(INPUT_LINE_IRQ1, HOLD_LINE, 0);
713 void PCE::vdc_reset()
716 memset(&vdc, 0, sizeof(vdc));
717 memset(&vce, 0, sizeof(vce));
718 memset(&vpc, 0, sizeof(vpc));
723 /* initialize palette */
726 for( i = 0; i < 512; i++ )
728 int r = (( i >> 3) & 7) << 5;
729 int g = (( i >> 6) & 7) << 5;
730 int b = (( i ) & 7) << 5;
731 int y = ( ( 66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
732 vce.palette[i] = RGB_COLOR(r, g, b);
733 vce.palette[512+i] = RGB_COLOR(y, y, y);
743 void PCE::draw_black_line(int line)
747 /* our line buffer */
748 scrntype *line_buffer = vce.bmp[line];
750 for( i=0; i< VDC_WPF; i++ )
754 void PCE::draw_overscan_line(int line)
758 /* Are we in greyscale mode or in color mode? */
759 scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
761 /* our line buffer */
762 scrntype *line_buffer = vce.bmp[line];
764 for ( i = 0; i < VDC_WPF; i++ )
765 line_buffer[i] = color_base[vce.vce_data[0x100].w.l];
768 void PCE::draw_sgx_overscan_line(int line)
772 /* Are we in greyscale mode or in color mode? */
773 scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
775 /* our line buffer */
776 scrntype *line_buffer = vce.bmp[line];
778 for ( i = 0; i < VDC_WPF; i++ )
779 line_buffer[i] = color_base[vce.vce_data[0].w.l];
782 void PCE::vram_write(int which, uint32 offset, uint8 data)
790 vdc[which].vram[offset] = data;
794 uint8 PCE::vram_read(int which, uint32 offset)
800 temp = vdc[which].vram[offset & 0xFFFF];
804 temp = vdc[which].vram[offset];
810 void PCE::vdc_w(int which, uint16 offset, uint8 data)
814 case 0x00: /* VDC register select */
815 vdc[which].vdc_register = (data & 0x1F);
818 case 0x02: /* VDC data (LSB) */
819 vdc[which].vdc_data[vdc[which].vdc_register].b.l = data;
820 switch(vdc[which].vdc_register)
822 case VxR: /* LSB of data to write to VRAM */
823 vdc[which].vdc_latch = data;
827 vdc[which].y_scroll=vdc[which].vdc_data[BYR].w.l;
831 vdc[which].physical_width = ((data & 0x003F) + 1) << 3;
835 vdc[which].physical_height &= 0xFF00;
836 vdc[which].physical_height |= (data & 0xFF);
837 vdc[which].physical_height &= 0x01FF;
849 case 0x03: /* VDC data (MSB) */
850 vdc[which].vdc_data[vdc[which].vdc_register].b.h = data;
851 switch(vdc[which].vdc_register)
853 case VxR: /* MSB of data to write to VRAM */
854 vram_write(which, vdc[which].vdc_data[MAWR].w.l*2+0, vdc[which].vdc_latch);
855 vram_write(which, vdc[which].vdc_data[MAWR].w.l*2+1, data);
856 vdc[which].vdc_data[MAWR].w.l += vdc[which].inc;
861 static const unsigned char inctab[] = {1, 32, 64, 128};
862 vdc[which].inc = inctab[(data >> 3) & 3];
867 vdc[which].physical_height &= 0x00FF;
868 vdc[which].physical_height |= (data << 8);
869 vdc[which].physical_height &= 0x01FF;
873 /* Force VRAM <> SATB DMA for this frame */
874 vdc[which].dvssr_write = 1;
878 vdc[which].y_scroll=vdc[which].vdc_data[BYR].w.l;
893 uint8 PCE::vdc_r(int which, uint16 offset)
899 temp = vdc[which].status;
900 vdc[which].status &= ~(VDC_VD | VDC_DV | VDC_DS | VDC_RR | VDC_OR | VDC_CR);
901 d_cpu->write_signal(INPUT_LINE_IRQ1, CLEAR_LINE, 0);
905 temp = vram_read(which, vdc[which].vdc_data[MARR].w.l * 2 + 0);
909 temp = vram_read(which, vdc[which].vdc_data[MARR].w.l * 2 + 1);
910 if ( vdc[which].vdc_register == VxR )
912 vdc[which].vdc_data[MARR].w.l += vdc[which].inc;
919 uint8 PCE::vce_r(uint16 offset)
924 case 0x04: /* color table data (LSB) */
925 temp = vce.vce_data[vce.vce_address.w.l].b.l;
928 case 0x05: /* color table data (MSB) */
929 temp = vce.vce_data[vce.vce_address.w.l].b.h;
931 vce.vce_address.w.l = (vce.vce_address.w.l + 1) & 0x01FF;
937 void PCE::vce_w(uint16 offset, uint8 data)
941 case 0x00: /* control reg. */
942 vce.vce_control = data;
945 case 0x02: /* color table address (LSB) */
946 vce.vce_address.b.l = data;
947 vce.vce_address.w.l &= 0x1FF;
950 case 0x03: /* color table address (MSB) */
951 vce.vce_address.b.h = data;
952 vce.vce_address.w.l &= 0x1FF;
955 case 0x04: /* color table data (LSB) */
956 vce.vce_data[vce.vce_address.w.l].b.l = data;
959 case 0x05: /* color table data (MSB) */
960 vce.vce_data[vce.vce_address.w.l].b.h = data & 0x01;
962 /* bump internal address */
963 vce.vce_address.w.l = (vce.vce_address.w.l + 1) & 0x01FF;
968 void PCE::pce_refresh_line(int which, int line, int external_input, uint8 *drawn, scrntype *line_buffer)
970 static const int width_table[4] = {5, 6, 7, 7};
972 int scroll_y = ( vdc[which].y_scroll & 0x01FF);
973 int scroll_x = (vdc[which].vdc_data[BXR].w.l & 0x03FF);
976 /* is virtual map 32 or 64 characters tall ? (256 or 512 pixels) */
977 int v_line = (scroll_y) & (vdc[which].vdc_data[MWR].w.l & 0x0040 ? 0x1FF : 0x0FF);
979 /* row within character */
980 int v_row = (v_line & 7);
982 /* row of characters in BAT */
983 int nt_row = (v_line >> 3);
985 /* virtual X size (# bits to shift) */
986 int v_width = width_table[(vdc[which].vdc_data[MWR].w.l >> 4) & 3];
988 /* pointer to the name table (Background Attribute Table) in VRAM */
989 uint8 *bat = &(vdc[which].vram[nt_row << (v_width+1)]);
991 /* Are we in greyscale mode or in color mode? */
992 scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
996 int cell_pattern_index;
1000 /* character blanking bit */
1001 if(!(vdc[which].vdc_data[CR].w.l & CR_BB))
1008 int phys_x = - ( scroll_x & 0x07 );
1010 for(i=0;i<(vdc[which].physical_width >> 3) + 1;i++)
1012 nt_index = (i + (scroll_x >> 3)) & ((2 << (v_width-1))-1);
1015 /* get name table data: */
1017 /* palette # = index from 0-15 */
1018 cell_palette = ( bat[nt_index + 1] >> 4 ) & 0x0F;
1020 /* This is the 'character number', from 0-0x0FFF */
1021 /* then it is shifted left 4 bits to form a VRAM address */
1022 /* and one more bit to convert VRAM word offset to a */
1023 /* byte-offset within the VRAM space */
1024 cell_pattern_index = ( ( ( bat[nt_index + 1] << 8 ) | bat[nt_index] ) & 0x0FFF) << 5;
1026 b0 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x00);
1027 b1 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x01);
1028 b2 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x10);
1029 b3 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x11);
1033 i0 = (b0 >> (7-x)) & 1;
1034 i1 = (b1 >> (7-x)) & 1;
1035 i2 = (b2 >> (7-x)) & 1;
1036 i3 = (b3 >> (7-x)) & 1;
1037 c = (cell_palette << 4 | i3 << 3 | i2 << 2 | i1 << 1 | i0);
1039 /* colour #0 always comes from palette #0 */
1040 if ( ! ( c & 0x0F ) )
1043 if ( phys_x >= 0 && phys_x < vdc[which].physical_width )
1045 drawn[ pixel ] = c ? 1 : 0;
1046 if ( c || ! external_input )
1047 line_buffer[ pixel ] = color_base[vce.vce_data[c].w.l];
1049 // if ( vdc[which].physical_width != 512 )
1051 // while ( pixel < ( ( ( phys_x + 1 ) * 512 ) / vdc[which].physical_width ) )
1053 // drawn[ pixel ] = c ? 1 : 0;
1054 // if ( c || ! external_input )
1055 // line_buffer[ pixel ] = color_base[vce.vce_data[c].w.l];
1066 void PCE::conv_obj(int which, int i, int l, int hf, int vf, char *buf)
1068 int b0, b1, b2, b3, i0, i1, i2, i3, x;
1073 if(vf) l = (15 - l);
1075 tmp = l + ( i << 5);
1077 b0 = vram_read(which, (tmp + 0x00)<<1);
1078 b0 |= vram_read(which, ((tmp + 0x00)<<1)+1)<<8;
1079 b1 = vram_read(which, (tmp + 0x10)<<1);
1080 b1 |= vram_read(which, ((tmp + 0x10)<<1)+1)<<8;
1081 b2 = vram_read(which, (tmp + 0x20)<<1);
1082 b2 |= vram_read(which, ((tmp + 0x20)<<1)+1)<<8;
1083 b3 = vram_read(which, (tmp + 0x30)<<1);
1084 b3 |= vram_read(which, ((tmp + 0x30)<<1)+1)<<8;
1088 if(hf) xi = x; else xi = (15 - x);
1089 i0 = (b0 >> xi) & 1;
1090 i1 = (b1 >> xi) & 1;
1091 i2 = (b2 >> xi) & 1;
1092 i3 = (b3 >> xi) & 1;
1093 buf[x] = (i3 << 3 | i2 << 2 | i1 << 1 | i0);
1097 void PCE::pce_refresh_sprites(int which, int line, uint8 *drawn, scrntype *line_buffer)
1100 uint8 sprites_drawn = 0;
1102 /* Are we in greyscale mode or in color mode? */
1103 scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
1105 /* count up: Highest priority is Sprite 0 */
1106 for(i = 0; i < 64; i++)
1108 static const int cgy_table[] = {16, 32, 64, 64};
1110 int obj_y = (vdc[which].sprite_ram[(i << 2) + 0] & 0x03FF) - 64;
1111 int obj_x = (vdc[which].sprite_ram[(i << 2) + 1] & 0x03FF) - 32;
1112 int obj_i = (vdc[which].sprite_ram[(i << 2) + 2] & 0x07FE);
1113 int obj_a = (vdc[which].sprite_ram[(i << 2) + 3]);
1114 int cgx = (obj_a >> 8) & 1; /* sprite width */
1115 int cgy = (obj_a >> 12) & 3; /* sprite height */
1116 int hf = (obj_a >> 11) & 1; /* horizontal flip */
1117 int vf = (obj_a >> 15) & 1; /* vertical flip */
1118 int palette = (obj_a & 0x000F);
1119 int priority = (obj_a >> 7) & 1;
1120 int obj_h = cgy_table[cgy];
1121 int obj_l = (line - obj_y);
1125 if ((obj_y == -64) || (obj_y > line)) continue;
1126 if ((obj_x == -32) || (obj_x >= vdc[which].physical_width)) continue;
1128 /* no need to draw an object that's ABOVE where we are. */
1129 if((obj_y + obj_h) < line) continue;
1131 /* If CGX is set, bit 0 of sprite pattern index is forced to 0 */
1135 /* If CGY is set to 1, bit 1 of the sprite pattern index is forced to 0. */
1139 /* If CGY is set to 2 or 3, bit 1 and 2 of the sprite pattern index are forced to 0. */
1147 if(sprites_drawn > 16)
1149 if(vdc[which].vdc_data[CR].w.l & CR_OV)
1151 /* note: flag is set only if irq is taken, Mizubaku Daibouken relies on this behaviour */
1152 vdc[which].status |= VDC_OR;
1153 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1155 continue; /* Should cause an interrupt */
1158 cgypos = (obj_l >> 4);
1159 if(vf) cgypos = ((obj_h - 1) >> 4) - cgypos;
1164 int pixel_x = obj_x;//( ( obj_x * 512 ) / vdc[which].physical_width );
1166 conv_obj(which, obj_i + (cgypos << 2), obj_l, hf, vf, buf);
1168 for(x = 0; x < 16; x++)
1170 if(((obj_x + x) < (vdc[which].physical_width)) && ((obj_x + x) >= 0))
1174 if( drawn[pixel_x] < 2 )
1176 if( priority || drawn[pixel_x] == 0 )
1178 line_buffer[pixel_x] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1179 // if ( vdc[which].physical_width != 512 )
1182 // while ( pixel_x + dp < ( ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width ) )
1184 // drawn[pixel_x + dp] = i + 2;
1185 // line_buffer[pixel_x + dp] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1190 drawn[pixel_x] = i + 2;
1192 /* Check for sprite #0 collision */
1193 else if (drawn[pixel_x] == 2)
1195 if(vdc[which].vdc_data[CR].w.l & CR_CC)
1196 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1197 vdc[which].status |= VDC_CR;
1201 // if ( vdc[which].physical_width != 512 )
1203 // pixel_x = ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width;
1214 int pixel_x = obj_x;//( ( obj_x * 512 ) / vdc[which].physical_width );
1216 conv_obj(which, obj_i + (cgypos << 2) + (hf ? 2 : 0), obj_l, hf, vf, buf);
1218 for(x = 0; x < 16; x++)
1220 if(((obj_x + x) < (vdc[which].physical_width)) && ((obj_x + x) >= 0))
1224 if( drawn[pixel_x] < 2 )
1226 if ( priority || drawn[pixel_x] == 0 )
1228 line_buffer[pixel_x] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1229 // if ( vdc[which].physical_width != 512 )
1232 // while ( pixel_x + dp < ( ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width ) )
1234 // drawn[pixel_x + dp] = i + 2;
1235 // line_buffer[pixel_x + dp] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1240 drawn[pixel_x] = i + 2;
1242 /* Check for sprite #0 collision */
1243 else if ( drawn[pixel_x] == 2 )
1245 if(vdc[which].vdc_data[CR].w.l & CR_CC)
1246 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1247 vdc[which].status |= VDC_CR;
1251 // if ( vdc[which].physical_width != 512 )
1253 // pixel_x = ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width;
1261 /* 32 pixel wide sprites are counted as 2 sprites and the right half
1262 is only drawn if there are 2 open slots.
1265 if( sprites_drawn > 16 )
1267 if(vdc[which].vdc_data[CR].w.l&CR_OV)
1269 /* note: flag is set only if irq is taken, Mizubaku Daibouken relies on this behaviour */
1270 vdc[which].status |= VDC_OR;
1271 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1276 conv_obj(which, obj_i + (cgypos << 2) + (hf ? 0 : 2), obj_l, hf, vf, buf);
1277 for(x = 0; x < 16; x++)
1279 if(((obj_x + 0x10 + x) < (vdc[which].physical_width)) && ((obj_x + 0x10 + x) >= 0))
1283 if( drawn[pixel_x] < 2 )
1285 if( priority || drawn[pixel_x] == 0 )
1287 line_buffer[pixel_x] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1288 // if ( vdc[which].physical_width != 512 )
1291 // while ( pixel_x + dp < ( ( ( obj_x + x + 17 ) * 512 ) / vdc[which].physical_width ) )
1293 // drawn[pixel_x + dp] = i + 2;
1294 // line_buffer[pixel_x + dp] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1299 drawn[pixel_x] = i + 2;
1301 /* Check for sprite #0 collision */
1302 else if ( drawn[pixel_x] == 2 )
1304 if(vdc[which].vdc_data[CR].w.l & CR_CC)
1305 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1306 vdc[which].status |= VDC_CR;
1310 // if ( vdc[which].physical_width != 512 )
1312 // pixel_x = ( ( obj_x + x + 17 ) * 512 ) / vdc[which].physical_width;
1325 void PCE::vdc_do_dma(int which)
1327 int src = vdc[which].vdc_data[SOUR].w.l;
1328 int dst = vdc[which].vdc_data[DESR].w.l;
1329 int len = vdc[which].vdc_data[LENR].w.l;
1331 int did = (vdc[which].vdc_data[DCR].w.l >> 3) & 1;
1332 int sid = (vdc[which].vdc_data[DCR].w.l >> 2) & 1;
1333 int dvc = (vdc[which].vdc_data[DCR].w.l >> 1) & 1;
1338 l = vram_read(which, src<<1);
1339 h = vram_read(which, (src<<1) + 1);
1341 vram_write(which, dst<<1,l);
1342 vram_write(which, 1+(dst<<1),h);
1344 if(sid) src = (src - 1) & 0xFFFF;
1345 else src = (src + 1) & 0xFFFF;
1347 if(did) dst = (dst - 1) & 0xFFFF;
1348 else dst = (dst + 1) & 0xFFFF;
1350 len = (len - 1) & 0xFFFF;
1352 } while (len != 0xFFFF);
1354 vdc[which].status |= VDC_DV;
1355 vdc[which].vdc_data[SOUR].w.l = src;
1356 vdc[which].vdc_data[DESR].w.l = dst;
1357 vdc[which].vdc_data[LENR].w.l = len;
1360 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1365 void PCE::vpc_update_prio_map()
1369 for( i = 0; i < 512; i++ )
1371 vpc.prio_map[i] = 0;
1372 if ( vpc.window1.w.l < 0x40 || i > vpc.window1.w.l )
1374 vpc.prio_map[i] |= 1;
1376 if ( vpc.window2.w.l < 0x40 || i > vpc.window2.w.l )
1378 vpc.prio_map[i] |= 2;
1383 void PCE::vpc_w(uint16 offset, uint8 data)
1385 switch( offset & 0x07 )
1387 case 0x00: /* Priority register #0 */
1388 vpc.priority.b.l = data;
1389 vpc.vpc_prio[0].prio = ( data >> 2 ) & 3;
1390 vpc.vpc_prio[0].vdc0_enabled = data & 1;
1391 vpc.vpc_prio[0].vdc1_enabled = data & 2;
1392 vpc.vpc_prio[1].prio = ( data >> 6 ) & 3;
1393 vpc.vpc_prio[1].vdc0_enabled = data & 0x10;
1394 vpc.vpc_prio[1].vdc1_enabled = data & 0x20;
1396 case 0x01: /* Priority register #1 */
1397 vpc.priority.b.h = data;
1398 vpc.vpc_prio[2].prio = ( data >> 2 ) & 3;
1399 vpc.vpc_prio[2].vdc0_enabled = data & 1;
1400 vpc.vpc_prio[2].vdc1_enabled = data & 2;
1401 vpc.vpc_prio[3].prio = ( data >> 6 ) & 3;
1402 vpc.vpc_prio[3].vdc0_enabled = data & 0x10;
1403 vpc.vpc_prio[3].vdc1_enabled = data & 0x20;
1405 case 0x02: /* Window 1 LSB */
1406 vpc.window1.b.l = data;
1407 vpc_update_prio_map();
1409 case 0x03: /* Window 1 MSB */
1410 vpc.window1.b.h = data & 3;
1411 vpc_update_prio_map();
1413 case 0x04: /* Window 2 LSB */
1414 vpc.window2.b.l = data;
1415 vpc_update_prio_map();
1417 case 0x05: /* Window 2 MSB */
1418 vpc.window2.b.h = data & 3;
1419 vpc_update_prio_map();
1421 case 0x06: /* VDC I/O select */
1422 vpc.vdc_select = data & 1;
1427 uint8 PCE::vpc_r(uint16 offset)
1430 switch( offset & 0x07 )
1432 case 0x00: /* Priority register #0 */
1433 data = vpc.priority.b.l;
1435 case 0x01: /* Priority register #1 */
1436 data = vpc.priority.b.h;
1438 case 0x02: /* Window 1 LSB */
1439 data = vpc.window1.b.l;
1441 case 0x03: /* Window 1 MSB; high bits are 0 or 1? */
1442 data = vpc.window1.b.h;
1444 case 0x04: /* Window 2 LSB */
1445 data = vpc.window2.b.l;
1447 case 0x05: /* Window 2 MSB; high bits are 0 or 1? */
1448 data = vpc.window2.b.h;
1454 void PCE::sgx_vdc_w(uint16 offset, uint8 data)
1456 if ( vpc.vdc_select )
1458 vdc_w( 1, offset, data );
1462 vdc_w( 0, offset, data );
1466 uint8 PCE::sgx_vdc_r(uint16 offset)
1468 return ( vpc.vdc_select ) ? vdc_r( 1, offset ) : vdc_r( 0, offset );
1473 void PCE::psg_reset()
1475 memset(psg, 0, sizeof(psg));
1476 for (int i = 0; i < 6; i++) {
1477 psg[i].regs[4] = 0x80;
1479 psg[4].randval = psg[5].randval = 0x51f631e4;
1482 psg_vol = psg_lfo_freq = psg_lfo_ctrl = 0;
1485 void PCE::psg_write(uint16 addr, uint8 data)
1487 switch(addr & 0x1f) {
1495 psg[psg_ch].regs[2] = data;
1498 // psg[psg_ch].regs[3] = data & 0x1f;
1499 psg[psg_ch].regs[3] = data & 0xf;
1502 psg[psg_ch].regs[4] = data;
1505 psg[psg_ch].regs[5] = data;
1508 if(psg[psg_ch].regs[4] & 0x40) {
1509 psg[psg_ch].wav[0] =data & 0x1f;
1512 psg[psg_ch].wav[psg[psg_ch].wavptr] = data & 0x1f;
1513 psg[psg_ch].wavptr = (psg[psg_ch].wavptr + 1) & 0x1f;
1517 psg[psg_ch].regs[7] = data;
1520 psg_lfo_freq = data;
1523 psg_lfo_ctrl = data;
1528 uint8 PCE::psg_read(uint16 addr)
1532 switch(addr & 0x1f) {
1538 return psg[psg_ch].regs[2];
1540 return psg[psg_ch].regs[3];
1542 return psg[psg_ch].regs[4];
1544 return psg[psg_ch].regs[5];
1546 ptr = psg[psg_ch].wavptr;
1547 psg[psg_ch].wavptr = (psg[psg_ch].wavptr + 1) & 0x1f;
1548 return psg[psg_ch].wav[ptr];
1550 return psg[psg_ch].regs[7];
1552 return psg_lfo_freq;
1554 return psg_lfo_ctrl;
1559 void PCE::mix(int32* buffer, int cnt)
1562 100, 451, 508, 573, 646, 728, 821, 925,1043,1175,1325, 1493, 1683, 1898, 2139, 2411,
1563 2718,3064,3454,3893,4388,4947,5576,6285,7085,7986,9002,10148,11439,12894,14535,16384
1569 for(int ch = 0; ch < 6; ch++) {
1570 if(!(psg[ch].regs[4] & 0x80)) {
1572 psg[ch].genptr = psg[ch].remain = 0;
1574 else if(psg[ch].regs[4] & 0x40) {
1576 int32 wav = ((int32)psg[ch].wav[0] - 16) * 702;
1577 int32 vol = max((psg_vol >> 3) & 0x1e, (psg_vol << 1) & 0x1e) + (psg[ch].regs[4] & 0x1f) + max((psg[ch].regs[5] >> 3) & 0x1e, (psg[ch].regs[5] << 1) & 0x1e) - 60;
1578 vol = (vol < 0) ? 0 : (vol > 31) ? 31 : vol;
1579 vol = wav * vol_tbl[vol] / 16384;
1580 for(int i = 0, j = 0; i < cnt; i++, j += 2) {
1581 buffer[j ] += vol; // L
1582 buffer[j + 1] += vol; // R
1585 else if(ch >= 4 && (psg[ch].regs[7] & 0x80)) {
1587 uint16 freq = (psg[ch].regs[7] & 0x1f);
1588 int32 vol = max((psg_vol >> 3) & 0x1e, (psg_vol << 1) & 0x1e) + (psg[ch].regs[4] & 0x1f) + max((psg[ch].regs[5] >> 3) & 0x1e, (psg[ch].regs[5] << 1) & 0x1e) - 60;
1589 vol = (vol < 0) ? 0 : (vol > 31) ? 31 : vol;
1591 for(int i = 0, j = 0; i < cnt; i++, j += 2) {
1592 psg[ch].remain += 3000 + freq * 512;
1593 uint32 t = psg[ch].remain / sample_rate;
1595 if(psg[ch].randval & 0x80000) {
1596 psg[ch].randval = ((psg[ch].randval ^ 4) << 1) + 1;
1597 psg[ch].noise = true;
1600 psg[ch].randval <<= 1;
1601 psg[ch].noise = false;
1603 psg[ch].remain -= sample_rate * t;
1605 int32 outvol = (int32)((psg[ch].noise ? 10 * 702 : -10 * 702) * vol / 16384);
1606 buffer[j ] += outvol; // L
1607 buffer[j + 1] += outvol; // R
1612 for(int i = 0; i < 32; i++) {
1613 wav[i] = ((int32)psg[ch].wav[i] - 16) * 702;
1615 uint32 freq = psg[ch].regs[2] + ((uint32)psg[ch].regs[3] << 8);
1617 int32 vol = max((psg_vol >> 3) & 0x1e, (psg_vol << 1) & 0x1e) + (psg[ch].regs[4] & 0x1f) + max((psg[ch].regs[5] >> 3) & 0x1e, (psg[ch].regs[5] << 1) & 0x1e) - 60;
1618 vol = (vol < 0) ? 0 : (vol > 31) ? 31 : vol;
1620 for(int i = 0, j = 0; i < cnt; i++, j += 2) {
1621 int32 outvol = wav[psg[ch].genptr] * vol / 16384;
1622 buffer[j ] += outvol; // L
1623 buffer[j + 1] += outvol; // R
1624 psg[ch].remain += 32 * 1118608 / freq;
1625 uint32 t = psg[ch].remain / (10 * sample_rate);
1626 psg[ch].genptr = (psg[ch].genptr + t) & 0x1f;
1627 psg[ch].remain -= 10 * sample_rate * t;
1634 // joypad (non multipad)
1636 void PCE::joy_reset()
1638 joy_sel = joy_bank = 1;
1639 joy_clr = joy_count = 0;
1642 void PCE::joy_write(uint16 addr, uint8 data)
1644 uint8 new_sel = data & 1;
1645 uint8 new_clr = data & 2;
1647 if(joy_sel && new_sel) {
1648 if(joy_clr && !new_clr) {
1653 else if(!joy_sel && new_sel) {
1654 joy_count = (joy_count + 1) & 15;
1660 uint8 PCE::joy_read(uint16 addr)
1665 if(joy_count == 0) {
1667 if(key_stat[0x26]) stat |= 0x001; // up
1668 if(key_stat[0x28]) stat |= 0x002; // down
1669 if(key_stat[0x25]) stat |= 0x004; // left
1670 if(key_stat[0x27]) stat |= 0x008; // right
1671 if(key_stat[0x44]) stat |= 0x010; // d (1)
1672 if(key_stat[0x53]) stat |= 0x020; // s (2)
1673 if(key_stat[0x20]) stat |= 0x040; // space (select)
1674 if(key_stat[0x0d]) stat |= 0x080; // enter (run)
1675 if(key_stat[0x41]) stat |= 0x100; // a (3)
1676 if(key_stat[0x51]) stat |= 0x200; // q (4)
1677 if(key_stat[0x57]) stat |= 0x400; // w (5)
1678 if(key_stat[0x45]) stat |= 0x800; // e (6)
1679 } else if(joy_count == 1) {
1682 if(support_6btn && joy_bank) {
1686 if(stat & 0x100) val &= ~1; // b3
1687 if(stat & 0x200) val &= ~2; // b4
1688 if(stat & 0x400) val &= ~4; // b5
1689 if(stat & 0x800) val &= ~8; // b6
1693 if(stat & 0x001) val &= ~1; // up
1694 if(stat & 0x008) val &= ~2; // right
1695 if(stat & 0x002) val &= ~4; // down
1696 if(stat & 0x004) val &= ~8; // left
1698 if(stat & 0x010) val &= ~1; // b1
1699 if(stat & 0x020) val &= ~2; // b2
1700 if(stat & 0x040) val &= ~4; // sel
1701 if(stat & 0x080) val &= ~8; // run
1704 if(joy_count == 4) {
1710 #define STATE_VERSION 2
1712 void PCE::save_state(FILEIO* state_fio)
1714 state_fio->FputUint32(STATE_VERSION);
1715 state_fio->FputInt32(this_device_id);
1717 state_fio->FputBool(support_6btn);
1718 state_fio->FputBool(support_sgfx);
1719 state_fio->Fwrite(ram, sizeof(ram), 1);
1720 state_fio->Fwrite(cart + 0x80000, 0x80000, 1);
1721 #ifdef SUPPORT_BACKUP_RAM
1722 state_fio->Fwrite(backup, sizeof(backup), 1);
1723 state_fio->FputUint32(backup_crc32);
1725 state_fio->FputUint32(bank);
1726 state_fio->FputUint8(buffer);
1727 state_fio->FputInt32(prev_width);
1728 state_fio->FputBool(inserted);
1729 state_fio->Fwrite(vdc, sizeof(vdc), 1);
1730 state_fio->Fwrite(&vce, sizeof(vce), 1);
1731 state_fio->Fwrite(&vpc, sizeof(vpc), 1);
1732 state_fio->Fwrite(psg, sizeof(psg), 1);
1733 state_fio->FputUint8(psg_ch);
1734 state_fio->FputUint8(psg_vol);
1735 state_fio->FputUint8(psg_lfo_freq);
1736 state_fio->FputUint8(psg_lfo_ctrl);
1737 state_fio->FputUint8(joy_sel);
1738 state_fio->FputUint8(joy_clr);
1739 state_fio->FputUint8(joy_count);
1740 state_fio->FputUint8(joy_bank);
1741 state_fio->FputBool(joy_6btn);
1744 bool PCE::load_state(FILEIO* state_fio)
1746 if(state_fio->FgetUint32() != STATE_VERSION) {
1749 if(state_fio->FgetInt32() != this_device_id) {
1752 support_6btn = state_fio->FgetBool();
1753 support_sgfx = state_fio->FgetBool();
1754 state_fio->Fread(ram, sizeof(ram), 1);
1755 state_fio->Fread(cart + 0x80000, 0x80000, 1);
1756 #ifdef SUPPORT_BACKUP_RAM
1757 state_fio->Fread(backup, sizeof(backup), 1);
1758 backup_crc32 = state_fio->FgetUint32();
1760 bank = state_fio->FgetUint32();
1761 buffer = state_fio->FgetUint8();
1762 prev_width = state_fio->FgetInt32();
1763 inserted = state_fio->FgetBool();
1764 state_fio->Fread(vdc, sizeof(vdc), 1);
1765 state_fio->Fread(&vce, sizeof(vce), 1);
1766 state_fio->Fread(&vpc, sizeof(vpc), 1);
1767 state_fio->Fread(psg, sizeof(psg), 1);
1768 psg_ch = state_fio->FgetUint8();
1769 psg_vol = state_fio->FgetUint8();
1770 psg_lfo_freq = state_fio->FgetUint8();
1771 psg_lfo_ctrl = state_fio->FgetUint8();
1772 joy_sel = state_fio->FgetUint8();
1773 joy_clr = state_fio->FgetUint8();
1774 joy_count = state_fio->FgetUint8();
1775 joy_bank = state_fio->FgetUint8();
1776 joy_6btn = state_fio->FgetBool();