2 NEC-HE PC Engine Emulator 'ePCEngine'
3 SHARP X1twin Emulator 'eX1twin'
5 Origin : Ootake (joypad)
8 Author : Takeda.Toshiya
16 #include "../huc6280.h"
17 #include "../../fileio.h"
24 /* Bits in the VDC status register */
26 #define VDC_BSY 0x40 /* Set when the VDC accesses VRAM */
27 #define VDC_VD 0x20 /* Set when in the vertical blanking period */
28 #define VDC_DV 0x10 /* Set when a VRAM > VRAM DMA transfer is done */
29 #define VDC_DS 0x08 /* Set when a VRAM > SATB DMA transfer is done */
30 #define VDC_RR 0x04 /* Set when the current scanline equals the RCR register */
31 #define VDC_OR 0x02 /* Set when there are more than 16 sprites on a line */
32 #define VDC_CR 0x01 /* Set when sprite #0 overlaps with another sprite */
34 /* Bits in the CR register */
36 #define CR_BB 0x80 /* Background blanking */
37 #define CR_SB 0x40 /* Object blanking */
38 #define CR_VR 0x08 /* Interrupt on vertical blank enable */
39 #define CR_RC 0x04 /* Interrupt on line compare enable */
40 #define CR_OV 0x02 /* Interrupt on sprite overflow enable */
41 #define CR_CC 0x01 /* Interrupt on sprite #0 collision enable */
43 /* Bits in the DCR regsiter */
45 #define DCR_DSR 0x10 /* VRAM > SATB auto-transfer enable */
46 #define DCR_DID 0x08 /* Destination diretion */
47 #define DCR_SID 0x04 /* Source direction */
48 #define DCR_DVC 0x02 /* VRAM > VRAM EOT interrupt enable */
49 #define DCR_DSC 0x01 /* VRAM > SATB EOT interrupt enable */
51 /* just to keep things simple... */
52 enum vdc_regs {MAWR = 0, MARR, VxR, reg3, reg4, CR, RCR, BXR, BYR, MWR, HSR, HDR, VPR, VDW, VCR, DCR, SOUR, DESR, LENR, DVSSR };
56 CLEAR_LINE = 0, // clear (a fired or held) line
57 ASSERT_LINE, // assert an interrupt immediately
58 HOLD_LINE, // hold interrupt line until acknowledged
59 PULSE_LINE // pulse interrupt line instantaneously (only for NMI, RESET)
70 void PCE::initialize()
73 joy_stat = emu->joy_buffer();
74 key_stat = emu->key_buffer();
77 register_vline_event(this);
79 #ifdef SUPPORT_BACKUP_RAM
80 static const uint8 image[8] = {0x48, 0x55, 0x42, 0x4d, 0x00, 0x88, 0x10, 0x80};
81 memset(backup, 0, sizeof(backup));
82 memcpy(backup, image, sizeof(image));
84 FILEIO* fio = new FILEIO();
85 if(fio->Fopen(emu->bios_path(_T("BACKUP.BIN")), FILEIO_READ_BINARY)) {
86 fio->Fread(backup, sizeof(backup), 1);
91 backup_crc32 = getcrc32(backup, sizeof(backup));
98 #ifdef SUPPORT_BACKUP_RAM
99 if(backup_crc32 != getcrc32(backup, sizeof(backup))) {
100 FILEIO* fio = new FILEIO();
101 if(fio->Fopen(emu->bios_path(_T("BACKUP.BIN")), FILEIO_WRITE_BINARY)) {
102 fio->Fwrite(backup, sizeof(backup), 1);
113 memset(ram, 0, sizeof(ram));
115 buffer = 0xff; // ???
125 void PCE::event_vline(int v, int clock)
127 #ifdef SUPPORT_SUPER_GFX
135 void PCE::write_data8(uint32 addr, uint32 data)
137 uint8 mpr = (addr >> 13) & 0xff;
138 uint16 ofs = addr & 0x1fff;
146 cart[addr & 0xfffff] = data;
148 #ifdef SUPPORT_BACKUP_RAM
158 #ifdef SUPPORT_SUPER_GFX
163 ram[addr & 0x7fff] = data;
168 switch(addr & 0x1c00) {
170 #ifdef SUPPORT_SUPER_GFX
172 switch(addr & 0x18) {
174 vdc_w(0, addr, data);
180 vdc_w(1, addr, data);
185 vdc_w(0, addr, data);
192 psg_write(addr, data);
194 case 0x0c00: // timer
196 d_cpu->timer_w(addr, data);
198 case 0x1000: // joypad
200 joy_write(addr, data);
202 case 0x1400: // interrupt control
204 d_cpu->irq_status_w(addr, data);
209 // bank switch for sf2d
210 if((addr & 0x1ffc) == 0x1ff0) {
211 bank = 0x80000 * ((addr & 3) + 1);
215 uint32 PCE::read_data8(uint32 addr)
217 uint8 mpr = (addr >> 13) & 0xff;
218 uint16 ofs = addr & 0x1fff;
221 return cart[addr & 0x7ffff];
224 return cart[bank | (addr & 0x7ffff)];
227 #ifdef SUPPORT_BACKUP_RAM
233 #ifdef SUPPORT_SUPER_GFX
238 return ram[addr & 0x7fff];
243 switch (addr & 0x1c00) {
245 #ifdef SUPPORT_SUPER_GFX
247 switch(addr & 0x18) {
249 return vdc_r(0, addr);
253 return vdc_r(1, addr);
258 return vdc_r(0, addr);
262 // return psg_read(addr);
264 case 0x0c00: // timer
265 buffer = (buffer & 0x80) | (d_cpu->timer_r(addr) & 0x7f);
267 case 0x1000: // joypad
268 buffer = (buffer & 0xb0) | (joy_read(addr) & 0x0f);
270 case 0x1400: // interrupt control
272 buffer = (buffer & 0xf8) | (d_cpu->irq_status_r(addr) & 0x07);
281 void PCE::write_io8(uint32 addr, uint32 data)
283 #ifdef SUPPORT_SUPER_GFX
285 sgx_vdc_w(addr, data);
288 vdc_w(0, addr, data);
291 uint32 PCE::read_io8(uint32 addr)
293 #ifdef SUPPORT_SUPER_GFX
295 return sgx_vdc_r(addr);
298 return vdc_r(0, addr);
301 void PCE::draw_screen()
303 int dx = (SCREEN_WIDTH - vdc[0].physical_width) / 2, sx = 0;
304 int dy = (SCREEN_HEIGHT - 238) / 2;
311 if(prev_width != vdc[0].physical_width) {
312 for(int y = 0; y < SCREEN_HEIGHT; y++) {
313 memset(emu->screen_buffer(y), 0, sizeof(scrntype) * SCREEN_WIDTH);
315 prev_width = vdc[0].physical_width;
318 for(int y = 0; y < 238; y++, dy++) {
319 scrntype* src = &vce.bmp[y + 17][86];
320 scrntype* dst = emu->screen_buffer(dy);
321 for(int x = sx, x2 = dx; x < vdc[0].physical_width && x2 < SCREEN_WIDTH; x++, x2++) {
327 void PCE::open_cart(_TCHAR* file_path)
329 FILEIO* fio = new FILEIO();
331 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
332 memset(cart, 0xff, sizeof(cart));
333 fio->Fseek(0, FILEIO_SEEK_END);
334 int size = fio->Ftell();
335 int head = size % 1024;
337 fio->Fseek(head, FILEIO_SEEK_SET);
338 fio->Fread(cart, size, 1);
341 if(size == 512 * 1024) {
342 bool overdump = true;
343 for(int i = 0x40000; i < 0x60000; i++) {
344 if(cart[i] != cart[i + 0x20000]) {
353 if(size == 384 * 1024) {
354 memcpy(cart + 0x060000, cart + 0x040000, 0x020000); /* Set up 060000 - 07FFFF mirror */
355 memcpy(cart + 0x080000, cart + 0x040000, 0x040000); /* Set up 080000 - 0BFFFF region */
356 memcpy(cart + 0x0C0000, cart + 0x040000, 0x040000); /* Set up 0C0000 - 0FFFFF region */
357 memcpy(cart + 0x040000, cart, 0x040000); /* Set up 040000 - 07FFFF region */
360 /* mirror 256KB rom data */
361 if (size <= 0x040000)
362 memcpy(cart + 0x040000, cart, 0x040000);
363 /* mirror 512KB rom data */
364 if (size <= 0x080000)
365 memcpy(cart + 0x080000, cart, 0x080000);
367 uint32 cart_crc32 = getcrc32(cart,size);
368 support_sgfx = (size == 0x100000 && cart_crc32 == 0x8c4588e2) // 1941 Counter Attack
369 || (size == 0x100000 && cart_crc32 == 0x4c2126b0) // Aldynes
370 || (size == 0x080000 && cart_crc32 == 0x3b13af61) // Battle Ace
371 || (size == 0x100000 && cart_crc32 == 0xb486a8ed) // Daimakaimura
372 || (size == 0x0c0000 && cart_crc32 == 0xbebfe042) // Darius Plus
373 || (size == 0x100000 && cart_crc32 == 0x1e1d0319) // Darius Plus (1024K)
374 || (size == 0x080000 && cart_crc32 == 0x1f041166); // Grandzort
375 support_6btn = (size == 0x280000 && cart_crc32 == 0xd15cb6bb); // Street Fighter II
381 void PCE::close_cart()
383 memset(cart, 0xff, sizeof(cart));
389 void PCE::pce_interrupt()
391 /* Draw the last scanline */
392 if ( vce.current_bitmap_line >= 14 && vce.current_bitmap_line < 14 + 242 )
394 /* We are in the active display area */
395 /* First fill the line with the overscan color */
396 draw_overscan_line(vce.current_bitmap_line );
398 /* Check if we need to draw more just the overscan color */
399 if ( vdc[0].current_segment == STATE_VDW )
401 /* 0 - no sprite and background pixels drawn
402 1 - background pixel drawn
403 otherwise is 2 + sprite# */
404 uint8 drawn[VDC_WPF];
405 /* our line buffer */
406 scrntype *line_buffer = &vce.bmp[vce.current_bitmap_line][86];
408 /* clear our priority/sprite collision detection buffer. */
409 memset(drawn, 0, VDC_WPF);
411 vdc[0].y_scroll = ( vdc[0].current_segment_line == 0 ) ? vdc[0].vdc_data[BYR].w.l : ( vdc[0].y_scroll + 1 );
413 /* Draw VDC #0 background layer */
414 pce_refresh_line(0, vdc[0].current_segment_line, 0, drawn, line_buffer);
416 /* Draw VDC #0 sprite layer */
417 if(vdc[0].vdc_data[CR].w.l & CR_SB)
419 pce_refresh_sprites(0, vdc[0].current_segment_line, drawn, line_buffer);
425 /* We are in one of the blanking areas */
426 draw_black_line(vce.current_bitmap_line );
429 /* bump current scanline */
430 vce.current_bitmap_line = ( vce.current_bitmap_line + 1 ) % VDC_LPF;
434 void PCE::sgx_interrupt()
436 /* Draw the last scanline */
437 if ( vce.current_bitmap_line >= 14 && vce.current_bitmap_line < 14 + 242 )
439 /* We are in the active display area */
440 /* First fill the line with the overscan color */
441 draw_sgx_overscan_line(vce.current_bitmap_line );
443 /* Check if we need to draw more just the overscan color */
444 if ( vdc[0].current_segment == STATE_VDW )
446 /* 0 - no sprite and background pixels drawn
447 1 - background pixel drawn
448 otherwise is 2 + sprite# */
450 scrntype *line_buffer;
451 scrntype temp_buffer[2][512];
454 /* clear our priority/sprite collision detection buffer. */
455 memset( drawn, 0, sizeof(drawn) );
457 vdc[0].y_scroll = ( vdc[0].current_segment_line == 0 ) ? vdc[0].vdc_data[BYR].w.l : ( vdc[0].y_scroll + 1 );
458 vdc[1].y_scroll = ( vdc[1].current_segment_line == 0 ) ? vdc[1].vdc_data[BYR].w.l : ( vdc[1].y_scroll + 1 );
460 /* Draw VDC #0 background layer */
461 pce_refresh_line( 0, vdc[0].current_segment_line, 0, drawn[0], temp_buffer[0]);
463 /* Draw VDC #0 sprite layer */
464 if(vdc[0].vdc_data[CR].w.l & CR_SB)
466 pce_refresh_sprites(0, vdc[0].current_segment_line, drawn[0], temp_buffer[0]);
469 /* Draw VDC #1 background layer */
470 pce_refresh_line( 1, vdc[1].current_segment_line, 1, drawn[1], temp_buffer[1]);
472 /* Draw VDC #1 sprite layer */
473 if ( vdc[1].vdc_data[CR].w.l & CR_SB )
475 pce_refresh_sprites(1, vdc[1].current_segment_line, drawn[1], temp_buffer[1]);
478 line_buffer = &vce.bmp[vce.current_bitmap_line][86];
479 /* Combine the output of both VDCs */
480 for( i = 0; i < 512; i++ )
482 int cur_prio = vpc.prio_map[i];
484 if ( vpc.vpc_prio[cur_prio].vdc0_enabled )
486 if ( vpc.vpc_prio[cur_prio].vdc1_enabled )
488 switch( vpc.vpc_prio[cur_prio].prio )
490 case 0: /* BG1 SP1 BG0 SP0 */
493 line_buffer[i] = temp_buffer[0][i];
495 else if ( drawn[1][i] )
497 line_buffer[i] = temp_buffer[1][i];
500 case 1: /* BG1 BG0 SP1 SP0 */
503 if ( drawn[0][i] > 1 )
505 line_buffer[i] = temp_buffer[0][i];
509 if ( drawn[1][i] > 1 )
511 line_buffer[i] = temp_buffer[1][i];
515 line_buffer[i] = temp_buffer[0][i];
519 else if ( drawn[1][i] )
521 line_buffer[i] = temp_buffer[1][i];
524 case 2: /* BG1 + SP1 => SP1
532 if ( drawn[0][i] > 1 )
534 if ( drawn[1][i] == 1 )
536 line_buffer[i] = temp_buffer[1][i];
540 line_buffer[i] = temp_buffer[0][i];
545 line_buffer[i] = temp_buffer[0][i];
548 else if ( drawn[1][i] )
550 line_buffer[i] = temp_buffer[1][i];
559 line_buffer[i] = temp_buffer[0][i];
565 if ( vpc.vpc_prio[cur_prio].vdc1_enabled )
569 line_buffer[i] = temp_buffer[1][i];
578 /* We are in one of the blanking areas */
579 draw_black_line(vce.current_bitmap_line );
582 /* bump current scanline */
583 vce.current_bitmap_line = ( vce.current_bitmap_line + 1 ) % VDC_LPF;
588 void PCE::vdc_advance_line(int which)
592 vdc[which].curline += 1;
593 vdc[which].current_segment_line += 1;
594 vdc[which].raster_count += 1;
596 if ( vdc[which].satb_countdown )
598 vdc[which].satb_countdown -= 1;
599 if ( vdc[which].satb_countdown == 0 )
601 if ( vdc[which].vdc_data[DCR].w.l & DCR_DSC )
603 vdc[which].status |= VDC_DS; /* set satb done flag */
609 if ( vce.current_bitmap_line == 0 )
611 vdc[which].current_segment = STATE_VSW;
612 vdc[which].current_segment_line = 0;
613 vdc[which].vblank_triggered = 0;
614 vdc[which].curline = 0;
617 if ( STATE_VSW == vdc[which].current_segment && vdc[which].current_segment_line >= ( vdc[which].vdc_data[VPR].b.l & 0x1F ) )
619 vdc[which].current_segment = STATE_VDS;
620 vdc[which].current_segment_line = 0;
623 if ( STATE_VDS == vdc[which].current_segment && vdc[which].current_segment_line >= vdc[which].vdc_data[VPR].b.h )
625 vdc[which].current_segment = STATE_VDW;
626 vdc[which].current_segment_line = 0;
627 vdc[which].raster_count = 0x40;
630 if ( STATE_VDW == vdc[which].current_segment && vdc[which].current_segment_line > ( vdc[which].vdc_data[VDW].w.l & 0x01FF ) )
632 vdc[which].current_segment = STATE_VCR;
633 vdc[which].current_segment_line = 0;
635 /* Generate VBlank interrupt, sprite DMA */
636 vdc[which].vblank_triggered = 1;
637 if ( vdc[which].vdc_data[CR].w.l & CR_VR )
639 vdc[which].status |= VDC_VD;
643 /* do VRAM > SATB DMA if the enable bit is set or the DVSSR reg. was written to */
644 if( ( vdc[which].vdc_data[DCR].w.l & DCR_DSR ) || vdc[which].dvssr_write )
648 vdc[which].dvssr_write = 0;
650 for( i = 0; i < 256; i++ )
652 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 ];
655 /* generate interrupt if needed */
656 if ( vdc[which].vdc_data[DCR].w.l & DCR_DSC )
658 vdc[which].satb_countdown = 4;
663 if ( STATE_VCR == vdc[which].current_segment )
665 if ( vdc[which].current_segment_line >= 3 && vdc[which].current_segment_line >= vdc[which].vdc_data[VCR].b.l )
667 vdc[which].current_segment = STATE_VSW;
668 vdc[which].current_segment_line = 0;
669 vdc[which].curline = 0;
673 /* generate interrupt on line compare if necessary */
674 if ( vdc[which].raster_count == vdc[which].vdc_data[RCR].w.l && vdc[which].vdc_data[CR].w.l & CR_RC )
676 vdc[which].status |= VDC_RR;
680 /* handle frame events */
681 if(vdc[which].curline == 261 && ! vdc[which].vblank_triggered )
684 vdc[which].vblank_triggered = 1;
685 if(vdc[which].vdc_data[CR].w.l & CR_VR)
686 { /* generate IRQ1 if enabled */
687 vdc[which].status |= VDC_VD; /* set vblank flag */
691 /* do VRAM > SATB DMA if the enable bit is set or the DVSSR reg. was written to */
692 if ( ( vdc[which].vdc_data[DCR].w.l & DCR_DSR ) || vdc[which].dvssr_write )
696 vdc[which].dvssr_write = 0;
697 for( i = 0; i < 256; i++ )
699 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 ];
702 /* generate interrupt if needed */
703 if(vdc[which].vdc_data[DCR].w.l & DCR_DSC)
705 vdc[which].satb_countdown = 4;
711 d_cpu->write_signal(INPUT_LINE_IRQ1, HOLD_LINE, 0);
714 void PCE::vdc_reset()
717 memset(&vdc, 0, sizeof(vdc));
718 memset(&vce, 0, sizeof(vce));
719 memset(&vpc, 0, sizeof(vpc));
724 /* initialize palette */
727 for( i = 0; i < 512; i++ )
729 int r = (( i >> 3) & 7) << 5;
730 int g = (( i >> 6) & 7) << 5;
731 int b = (( i ) & 7) << 5;
732 int y = ( ( 66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
733 vce.palette[i] = RGB_COLOR(r, g, b);
734 vce.palette[512+i] = RGB_COLOR(y, y, y);
744 void PCE::draw_black_line(int line)
748 /* our line buffer */
749 scrntype *line_buffer = vce.bmp[line];
751 for( i=0; i< VDC_WPF; i++ )
755 void PCE::draw_overscan_line(int line)
759 /* Are we in greyscale mode or in color mode? */
760 scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
762 /* our line buffer */
763 scrntype *line_buffer = vce.bmp[line];
765 for ( i = 0; i < VDC_WPF; i++ )
766 line_buffer[i] = color_base[vce.vce_data[0x100].w.l];
769 void PCE::draw_sgx_overscan_line(int line)
773 /* Are we in greyscale mode or in color mode? */
774 scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
776 /* our line buffer */
777 scrntype *line_buffer = vce.bmp[line];
779 for ( i = 0; i < VDC_WPF; i++ )
780 line_buffer[i] = color_base[vce.vce_data[0].w.l];
783 void PCE::vram_write(int which, uint32 offset, uint8 data)
791 vdc[which].vram[offset] = data;
795 uint8 PCE::vram_read(int which, uint32 offset)
801 temp = vdc[which].vram[offset & 0xFFFF];
805 temp = vdc[which].vram[offset];
811 void PCE::vdc_w(int which, uint16 offset, uint8 data)
815 case 0x00: /* VDC register select */
816 vdc[which].vdc_register = (data & 0x1F);
819 case 0x02: /* VDC data (LSB) */
820 vdc[which].vdc_data[vdc[which].vdc_register].b.l = data;
821 switch(vdc[which].vdc_register)
823 case VxR: /* LSB of data to write to VRAM */
824 vdc[which].vdc_latch = data;
828 vdc[which].y_scroll=vdc[which].vdc_data[BYR].w.l;
832 vdc[which].physical_width = ((data & 0x003F) + 1) << 3;
836 vdc[which].physical_height &= 0xFF00;
837 vdc[which].physical_height |= (data & 0xFF);
838 vdc[which].physical_height &= 0x01FF;
850 case 0x03: /* VDC data (MSB) */
851 vdc[which].vdc_data[vdc[which].vdc_register].b.h = data;
852 switch(vdc[which].vdc_register)
854 case VxR: /* MSB of data to write to VRAM */
855 vram_write(which, vdc[which].vdc_data[MAWR].w.l*2+0, vdc[which].vdc_latch);
856 vram_write(which, vdc[which].vdc_data[MAWR].w.l*2+1, data);
857 vdc[which].vdc_data[MAWR].w.l += vdc[which].inc;
862 static const unsigned char inctab[] = {1, 32, 64, 128};
863 vdc[which].inc = inctab[(data >> 3) & 3];
868 vdc[which].physical_height &= 0x00FF;
869 vdc[which].physical_height |= (data << 8);
870 vdc[which].physical_height &= 0x01FF;
874 /* Force VRAM <> SATB DMA for this frame */
875 vdc[which].dvssr_write = 1;
879 vdc[which].y_scroll=vdc[which].vdc_data[BYR].w.l;
894 uint8 PCE::vdc_r(int which, uint16 offset)
900 temp = vdc[which].status;
901 vdc[which].status &= ~(VDC_VD | VDC_DV | VDC_DS | VDC_RR | VDC_OR | VDC_CR);
902 d_cpu->write_signal(INPUT_LINE_IRQ1, CLEAR_LINE, 0);
906 temp = vram_read(which, vdc[which].vdc_data[MARR].w.l * 2 + 0);
910 temp = vram_read(which, vdc[which].vdc_data[MARR].w.l * 2 + 1);
911 if ( vdc[which].vdc_register == VxR )
913 vdc[which].vdc_data[MARR].w.l += vdc[which].inc;
920 uint8 PCE::vce_r(uint16 offset)
925 case 0x04: /* color table data (LSB) */
926 temp = vce.vce_data[vce.vce_address.w.l].b.l;
929 case 0x05: /* color table data (MSB) */
930 temp = vce.vce_data[vce.vce_address.w.l].b.h;
932 vce.vce_address.w.l = (vce.vce_address.w.l + 1) & 0x01FF;
938 void PCE::vce_w(uint16 offset, uint8 data)
942 case 0x00: /* control reg. */
943 vce.vce_control = data;
946 case 0x02: /* color table address (LSB) */
947 vce.vce_address.b.l = data;
948 vce.vce_address.w.l &= 0x1FF;
951 case 0x03: /* color table address (MSB) */
952 vce.vce_address.b.h = data;
953 vce.vce_address.w.l &= 0x1FF;
956 case 0x04: /* color table data (LSB) */
957 vce.vce_data[vce.vce_address.w.l].b.l = data;
960 case 0x05: /* color table data (MSB) */
961 vce.vce_data[vce.vce_address.w.l].b.h = data & 0x01;
963 /* bump internal address */
964 vce.vce_address.w.l = (vce.vce_address.w.l + 1) & 0x01FF;
969 void PCE::pce_refresh_line(int which, int line, int external_input, uint8 *drawn, scrntype *line_buffer)
971 static const int width_table[4] = {5, 6, 7, 7};
973 int scroll_y = ( vdc[which].y_scroll & 0x01FF);
974 int scroll_x = (vdc[which].vdc_data[BXR].w.l & 0x03FF);
977 /* is virtual map 32 or 64 characters tall ? (256 or 512 pixels) */
978 int v_line = (scroll_y) & (vdc[which].vdc_data[MWR].w.l & 0x0040 ? 0x1FF : 0x0FF);
980 /* row within character */
981 int v_row = (v_line & 7);
983 /* row of characters in BAT */
984 int nt_row = (v_line >> 3);
986 /* virtual X size (# bits to shift) */
987 int v_width = width_table[(vdc[which].vdc_data[MWR].w.l >> 4) & 3];
989 /* pointer to the name table (Background Attribute Table) in VRAM */
990 uint8 *bat = &(vdc[which].vram[nt_row << (v_width+1)]);
992 /* Are we in greyscale mode or in color mode? */
993 scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
997 int cell_pattern_index;
1001 /* character blanking bit */
1002 if(!(vdc[which].vdc_data[CR].w.l & CR_BB))
1009 int phys_x = - ( scroll_x & 0x07 );
1011 for(i=0;i<(vdc[which].physical_width >> 3) + 1;i++)
1013 nt_index = (i + (scroll_x >> 3)) & ((2 << (v_width-1))-1);
1016 /* get name table data: */
1018 /* palette # = index from 0-15 */
1019 cell_palette = ( bat[nt_index + 1] >> 4 ) & 0x0F;
1021 /* This is the 'character number', from 0-0x0FFF */
1022 /* then it is shifted left 4 bits to form a VRAM address */
1023 /* and one more bit to convert VRAM word offset to a */
1024 /* byte-offset within the VRAM space */
1025 cell_pattern_index = ( ( ( bat[nt_index + 1] << 8 ) | bat[nt_index] ) & 0x0FFF) << 5;
1027 b0 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x00);
1028 b1 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x01);
1029 b2 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x10);
1030 b3 = vram_read(which, (cell_pattern_index) + (v_row << 1) + 0x11);
1034 i0 = (b0 >> (7-x)) & 1;
1035 i1 = (b1 >> (7-x)) & 1;
1036 i2 = (b2 >> (7-x)) & 1;
1037 i3 = (b3 >> (7-x)) & 1;
1038 c = (cell_palette << 4 | i3 << 3 | i2 << 2 | i1 << 1 | i0);
1040 /* colour #0 always comes from palette #0 */
1041 if ( ! ( c & 0x0F ) )
1044 if ( phys_x >= 0 && phys_x < vdc[which].physical_width )
1046 drawn[ pixel ] = c ? 1 : 0;
1047 if ( c || ! external_input )
1048 line_buffer[ pixel ] = color_base[vce.vce_data[c].w.l];
1050 // if ( vdc[which].physical_width != 512 )
1052 // while ( pixel < ( ( ( phys_x + 1 ) * 512 ) / vdc[which].physical_width ) )
1054 // drawn[ pixel ] = c ? 1 : 0;
1055 // if ( c || ! external_input )
1056 // line_buffer[ pixel ] = color_base[vce.vce_data[c].w.l];
1067 void PCE::conv_obj(int which, int i, int l, int hf, int vf, char *buf)
1069 int b0, b1, b2, b3, i0, i1, i2, i3, x;
1074 if(vf) l = (15 - l);
1076 tmp = l + ( i << 5);
1078 b0 = vram_read(which, (tmp + 0x00)<<1);
1079 b0 |= vram_read(which, ((tmp + 0x00)<<1)+1)<<8;
1080 b1 = vram_read(which, (tmp + 0x10)<<1);
1081 b1 |= vram_read(which, ((tmp + 0x10)<<1)+1)<<8;
1082 b2 = vram_read(which, (tmp + 0x20)<<1);
1083 b2 |= vram_read(which, ((tmp + 0x20)<<1)+1)<<8;
1084 b3 = vram_read(which, (tmp + 0x30)<<1);
1085 b3 |= vram_read(which, ((tmp + 0x30)<<1)+1)<<8;
1089 if(hf) xi = x; else xi = (15 - x);
1090 i0 = (b0 >> xi) & 1;
1091 i1 = (b1 >> xi) & 1;
1092 i2 = (b2 >> xi) & 1;
1093 i3 = (b3 >> xi) & 1;
1094 buf[x] = (i3 << 3 | i2 << 2 | i1 << 1 | i0);
1098 void PCE::pce_refresh_sprites(int which, int line, uint8 *drawn, scrntype *line_buffer)
1101 uint8 sprites_drawn = 0;
1103 /* Are we in greyscale mode or in color mode? */
1104 scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
1106 /* count up: Highest priority is Sprite 0 */
1107 for(i = 0; i < 64; i++)
1109 static const int cgy_table[] = {16, 32, 64, 64};
1111 int obj_y = (vdc[which].sprite_ram[(i << 2) + 0] & 0x03FF) - 64;
1112 int obj_x = (vdc[which].sprite_ram[(i << 2) + 1] & 0x03FF) - 32;
1113 int obj_i = (vdc[which].sprite_ram[(i << 2) + 2] & 0x07FE);
1114 int obj_a = (vdc[which].sprite_ram[(i << 2) + 3]);
1115 int cgx = (obj_a >> 8) & 1; /* sprite width */
1116 int cgy = (obj_a >> 12) & 3; /* sprite height */
1117 int hf = (obj_a >> 11) & 1; /* horizontal flip */
1118 int vf = (obj_a >> 15) & 1; /* vertical flip */
1119 int palette = (obj_a & 0x000F);
1120 int priority = (obj_a >> 7) & 1;
1121 int obj_h = cgy_table[cgy];
1122 int obj_l = (line - obj_y);
1126 if ((obj_y == -64) || (obj_y > line)) continue;
1127 if ((obj_x == -32) || (obj_x >= vdc[which].physical_width)) continue;
1129 /* no need to draw an object that's ABOVE where we are. */
1130 if((obj_y + obj_h) < line) continue;
1132 /* If CGX is set, bit 0 of sprite pattern index is forced to 0 */
1136 /* If CGY is set to 1, bit 1 of the sprite pattern index is forced to 0. */
1140 /* If CGY is set to 2 or 3, bit 1 and 2 of the sprite pattern index are forced to 0. */
1148 if(sprites_drawn > 16)
1150 if(vdc[which].vdc_data[CR].w.l & CR_OV)
1152 /* note: flag is set only if irq is taken, Mizubaku Daibouken relies on this behaviour */
1153 vdc[which].status |= VDC_OR;
1154 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1156 continue; /* Should cause an interrupt */
1159 cgypos = (obj_l >> 4);
1160 if(vf) cgypos = ((obj_h - 1) >> 4) - cgypos;
1165 int pixel_x = obj_x;//( ( obj_x * 512 ) / vdc[which].physical_width );
1167 conv_obj(which, obj_i + (cgypos << 2), obj_l, hf, vf, buf);
1169 for(x = 0; x < 16; x++)
1171 if(((obj_x + x) < (vdc[which].physical_width)) && ((obj_x + x) >= 0))
1175 if( drawn[pixel_x] < 2 )
1177 if( priority || drawn[pixel_x] == 0 )
1179 line_buffer[pixel_x] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1180 // if ( vdc[which].physical_width != 512 )
1183 // while ( pixel_x + dp < ( ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width ) )
1185 // drawn[pixel_x + dp] = i + 2;
1186 // line_buffer[pixel_x + dp] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1191 drawn[pixel_x] = i + 2;
1193 /* Check for sprite #0 collision */
1194 else if (drawn[pixel_x] == 2)
1196 if(vdc[which].vdc_data[CR].w.l & CR_CC)
1197 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1198 vdc[which].status |= VDC_CR;
1202 // if ( vdc[which].physical_width != 512 )
1204 // pixel_x = ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width;
1215 int pixel_x = obj_x;//( ( obj_x * 512 ) / vdc[which].physical_width );
1217 conv_obj(which, obj_i + (cgypos << 2) + (hf ? 2 : 0), obj_l, hf, vf, buf);
1219 for(x = 0; x < 16; x++)
1221 if(((obj_x + x) < (vdc[which].physical_width)) && ((obj_x + x) >= 0))
1225 if( drawn[pixel_x] < 2 )
1227 if ( priority || drawn[pixel_x] == 0 )
1229 line_buffer[pixel_x] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1230 // if ( vdc[which].physical_width != 512 )
1233 // while ( pixel_x + dp < ( ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width ) )
1235 // drawn[pixel_x + dp] = i + 2;
1236 // line_buffer[pixel_x + dp] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1241 drawn[pixel_x] = i + 2;
1243 /* Check for sprite #0 collision */
1244 else if ( drawn[pixel_x] == 2 )
1246 if(vdc[which].vdc_data[CR].w.l & CR_CC)
1247 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1248 vdc[which].status |= VDC_CR;
1252 // if ( vdc[which].physical_width != 512 )
1254 // pixel_x = ( ( obj_x + x + 1 ) * 512 ) / vdc[which].physical_width;
1262 /* 32 pixel wide sprites are counted as 2 sprites and the right half
1263 is only drawn if there are 2 open slots.
1266 if( sprites_drawn > 16 )
1268 if(vdc[which].vdc_data[CR].w.l&CR_OV)
1270 /* note: flag is set only if irq is taken, Mizubaku Daibouken relies on this behaviour */
1271 vdc[which].status |= VDC_OR;
1272 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1277 conv_obj(which, obj_i + (cgypos << 2) + (hf ? 0 : 2), obj_l, hf, vf, buf);
1278 for(x = 0; x < 16; x++)
1280 if(((obj_x + 0x10 + x) < (vdc[which].physical_width)) && ((obj_x + 0x10 + x) >= 0))
1284 if( drawn[pixel_x] < 2 )
1286 if( priority || drawn[pixel_x] == 0 )
1288 line_buffer[pixel_x] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1289 // if ( vdc[which].physical_width != 512 )
1292 // while ( pixel_x + dp < ( ( ( obj_x + x + 17 ) * 512 ) / vdc[which].physical_width ) )
1294 // drawn[pixel_x + dp] = i + 2;
1295 // line_buffer[pixel_x + dp] = color_base[vce.vce_data[0x100 + (palette << 4) + buf[x]].w.l];
1300 drawn[pixel_x] = i + 2;
1302 /* Check for sprite #0 collision */
1303 else if ( drawn[pixel_x] == 2 )
1305 if(vdc[which].vdc_data[CR].w.l & CR_CC)
1306 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1307 vdc[which].status |= VDC_CR;
1311 // if ( vdc[which].physical_width != 512 )
1313 // pixel_x = ( ( obj_x + x + 17 ) * 512 ) / vdc[which].physical_width;
1326 void PCE::vdc_do_dma(int which)
1328 int src = vdc[which].vdc_data[SOUR].w.l;
1329 int dst = vdc[which].vdc_data[DESR].w.l;
1330 int len = vdc[which].vdc_data[LENR].w.l;
1332 int did = (vdc[which].vdc_data[DCR].w.l >> 3) & 1;
1333 int sid = (vdc[which].vdc_data[DCR].w.l >> 2) & 1;
1334 int dvc = (vdc[which].vdc_data[DCR].w.l >> 1) & 1;
1339 l = vram_read(which, src<<1);
1340 h = vram_read(which, (src<<1) + 1);
1342 vram_write(which, dst<<1,l);
1343 vram_write(which, 1+(dst<<1),h);
1345 if(sid) src = (src - 1) & 0xFFFF;
1346 else src = (src + 1) & 0xFFFF;
1348 if(did) dst = (dst - 1) & 0xFFFF;
1349 else dst = (dst + 1) & 0xFFFF;
1351 len = (len - 1) & 0xFFFF;
1353 } while (len != 0xFFFF);
1355 vdc[which].status |= VDC_DV;
1356 vdc[which].vdc_data[SOUR].w.l = src;
1357 vdc[which].vdc_data[DESR].w.l = dst;
1358 vdc[which].vdc_data[LENR].w.l = len;
1361 d_cpu->write_signal(INPUT_LINE_IRQ1, ASSERT_LINE, 0);
1366 void PCE::vpc_update_prio_map()
1370 for( i = 0; i < 512; i++ )
1372 vpc.prio_map[i] = 0;
1373 if ( vpc.window1.w.l < 0x40 || i > vpc.window1.w.l )
1375 vpc.prio_map[i] |= 1;
1377 if ( vpc.window2.w.l < 0x40 || i > vpc.window2.w.l )
1379 vpc.prio_map[i] |= 2;
1384 void PCE::vpc_w(uint16 offset, uint8 data)
1386 switch( offset & 0x07 )
1388 case 0x00: /* Priority register #0 */
1389 vpc.priority.b.l = data;
1390 vpc.vpc_prio[0].prio = ( data >> 2 ) & 3;
1391 vpc.vpc_prio[0].vdc0_enabled = data & 1;
1392 vpc.vpc_prio[0].vdc1_enabled = data & 2;
1393 vpc.vpc_prio[1].prio = ( data >> 6 ) & 3;
1394 vpc.vpc_prio[1].vdc0_enabled = data & 0x10;
1395 vpc.vpc_prio[1].vdc1_enabled = data & 0x20;
1397 case 0x01: /* Priority register #1 */
1398 vpc.priority.b.h = data;
1399 vpc.vpc_prio[2].prio = ( data >> 2 ) & 3;
1400 vpc.vpc_prio[2].vdc0_enabled = data & 1;
1401 vpc.vpc_prio[2].vdc1_enabled = data & 2;
1402 vpc.vpc_prio[3].prio = ( data >> 6 ) & 3;
1403 vpc.vpc_prio[3].vdc0_enabled = data & 0x10;
1404 vpc.vpc_prio[3].vdc1_enabled = data & 0x20;
1406 case 0x02: /* Window 1 LSB */
1407 vpc.window1.b.l = data;
1408 vpc_update_prio_map();
1410 case 0x03: /* Window 1 MSB */
1411 vpc.window1.b.h = data & 3;
1412 vpc_update_prio_map();
1414 case 0x04: /* Window 2 LSB */
1415 vpc.window2.b.l = data;
1416 vpc_update_prio_map();
1418 case 0x05: /* Window 2 MSB */
1419 vpc.window2.b.h = data & 3;
1420 vpc_update_prio_map();
1422 case 0x06: /* VDC I/O select */
1423 vpc.vdc_select = data & 1;
1428 uint8 PCE::vpc_r(uint16 offset)
1431 switch( offset & 0x07 )
1433 case 0x00: /* Priority register #0 */
1434 data = vpc.priority.b.l;
1436 case 0x01: /* Priority register #1 */
1437 data = vpc.priority.b.h;
1439 case 0x02: /* Window 1 LSB */
1440 data = vpc.window1.b.l;
1442 case 0x03: /* Window 1 MSB; high bits are 0 or 1? */
1443 data = vpc.window1.b.h;
1445 case 0x04: /* Window 2 LSB */
1446 data = vpc.window2.b.l;
1448 case 0x05: /* Window 2 MSB; high bits are 0 or 1? */
1449 data = vpc.window2.b.h;
1455 void PCE::sgx_vdc_w(uint16 offset, uint8 data)
1457 if ( vpc.vdc_select )
1459 vdc_w( 1, offset, data );
1463 vdc_w( 0, offset, data );
1467 uint8 PCE::sgx_vdc_r(uint16 offset)
1469 return ( vpc.vdc_select ) ? vdc_r( 1, offset ) : vdc_r( 0, offset );
1474 void PCE::psg_reset()
1476 memset(psg, 0, sizeof(psg));
1477 for (int i = 0; i < 6; i++) {
1478 psg[i].regs[4] = 0x80;
1480 psg[4].randval = psg[5].randval = 0x51f631e4;
1483 psg_vol = psg_lfo_freq = psg_lfo_ctrl = 0;
1486 void PCE::psg_write(uint16 addr, uint8 data)
1488 switch(addr & 0x1f) {
1496 psg[psg_ch].regs[2] = data;
1499 // psg[psg_ch].regs[3] = data & 0x1f;
1500 psg[psg_ch].regs[3] = data & 0xf;
1503 psg[psg_ch].regs[4] = data;
1506 psg[psg_ch].regs[5] = data;
1509 if(psg[psg_ch].regs[4] & 0x40) {
1510 psg[psg_ch].wav[0] =data & 0x1f;
1513 psg[psg_ch].wav[psg[psg_ch].wavptr] = data & 0x1f;
1514 psg[psg_ch].wavptr = (psg[psg_ch].wavptr + 1) & 0x1f;
1518 psg[psg_ch].regs[7] = data;
1521 psg_lfo_freq = data;
1524 psg_lfo_ctrl = data;
1529 uint8 PCE::psg_read(uint16 addr)
1533 switch(addr & 0x1f) {
1539 return psg[psg_ch].regs[2];
1541 return psg[psg_ch].regs[3];
1543 return psg[psg_ch].regs[4];
1545 return psg[psg_ch].regs[5];
1547 ptr = psg[psg_ch].wavptr;
1548 psg[psg_ch].wavptr = (psg[psg_ch].wavptr + 1) & 0x1f;
1549 return psg[psg_ch].wav[ptr];
1551 return psg[psg_ch].regs[7];
1553 return psg_lfo_freq;
1555 return psg_lfo_ctrl;
1560 void PCE::mix(int32* buffer, int cnt)
1563 100, 451, 508, 573, 646, 728, 821, 925,1043,1175,1325, 1493, 1683, 1898, 2139, 2411,
1564 2718,3064,3454,3893,4388,4947,5576,6285,7085,7986,9002,10148,11439,12894,14535,16384
1570 for(int ch = 0; ch < 6; ch++) {
1571 if(!(psg[ch].regs[4] & 0x80)) {
1573 psg[ch].genptr = psg[ch].remain = 0;
1575 else if(psg[ch].regs[4] & 0x40) {
1577 int32 wav = ((int32)psg[ch].wav[0] - 16) * 702;
1578 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;
1579 vol = (vol < 0) ? 0 : (vol > 31) ? 31 : vol;
1580 vol = wav * vol_tbl[vol] / 16384;
1581 for(int i = 0, j = 0; i < cnt; i++, j += 2) {
1582 buffer[j ] += vol; // L
1583 buffer[j + 1] += vol; // R
1586 else if(ch >= 4 && (psg[ch].regs[7] & 0x80)) {
1588 uint16 freq = (psg[ch].regs[7] & 0x1f);
1589 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;
1590 vol = (vol < 0) ? 0 : (vol > 31) ? 31 : vol;
1592 for(int i = 0, j = 0; i < cnt; i++, j += 2) {
1593 psg[ch].remain += 3000 + freq * 512;
1594 uint32 t = psg[ch].remain / sample_rate;
1596 if(psg[ch].randval & 0x80000) {
1597 psg[ch].randval = ((psg[ch].randval ^ 4) << 1) + 1;
1598 psg[ch].noise = true;
1601 psg[ch].randval <<= 1;
1602 psg[ch].noise = false;
1604 psg[ch].remain -= sample_rate * t;
1606 int32 outvol = (int32)((psg[ch].noise ? 10 * 702 : -10 * 702) * vol / 16384);
1607 buffer[j ] += outvol; // L
1608 buffer[j + 1] += outvol; // R
1613 for(int i = 0; i < 32; i++) {
1614 wav[i] = ((int32)psg[ch].wav[i] - 16) * 702;
1616 uint32 freq = psg[ch].regs[2] + ((uint32)psg[ch].regs[3] << 8);
1618 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;
1619 vol = (vol < 0) ? 0 : (vol > 31) ? 31 : vol;
1621 for(int i = 0, j = 0; i < cnt; i++, j += 2) {
1622 int32 outvol = wav[psg[ch].genptr] * vol / 16384;
1623 buffer[j ] += outvol; // L
1624 buffer[j + 1] += outvol; // R
1625 psg[ch].remain += 32 * 1118608 / freq;
1626 uint32 t = psg[ch].remain / (10 * sample_rate);
1627 psg[ch].genptr = (psg[ch].genptr + t) & 0x1f;
1628 psg[ch].remain -= 10 * sample_rate * t;
1635 // joypad (non multipad)
1637 void PCE::joy_reset()
1639 joy_sel = joy_bank = 1;
1640 joy_clr = joy_count = 0;
1643 void PCE::joy_write(uint16 addr, uint8 data)
1645 uint8 new_sel = data & 1;
1646 uint8 new_clr = data & 2;
1648 if(joy_sel && new_sel) {
1649 if(joy_clr && !new_clr) {
1654 else if(!joy_sel && new_sel) {
1655 joy_count = (joy_count + 1) & 15;
1661 uint8 PCE::joy_read(uint16 addr)
1666 if(joy_count == 0) {
1668 if(key_stat[0x26]) stat |= 0x001; // up
1669 if(key_stat[0x28]) stat |= 0x002; // down
1670 if(key_stat[0x25]) stat |= 0x004; // left
1671 if(key_stat[0x27]) stat |= 0x008; // right
1672 if(key_stat[0x44]) stat |= 0x010; // d (1)
1673 if(key_stat[0x53]) stat |= 0x020; // s (2)
1674 if(key_stat[0x20]) stat |= 0x040; // space (select)
1675 if(key_stat[0x0d]) stat |= 0x080; // enter (run)
1676 if(key_stat[0x41]) stat |= 0x100; // a (3)
1677 if(key_stat[0x51]) stat |= 0x200; // q (4)
1678 if(key_stat[0x57]) stat |= 0x400; // w (5)
1679 if(key_stat[0x45]) stat |= 0x800; // e (6)
1680 } else if(joy_count == 1) {
1683 if(support_6btn && joy_bank) {
1687 if(stat & 0x100) val &= ~1; // b3
1688 if(stat & 0x200) val &= ~2; // b4
1689 if(stat & 0x400) val &= ~4; // b5
1690 if(stat & 0x800) val &= ~8; // b6
1694 if(stat & 0x001) val &= ~1; // up
1695 if(stat & 0x008) val &= ~2; // right
1696 if(stat & 0x002) val &= ~4; // down
1697 if(stat & 0x004) val &= ~8; // left
1699 if(stat & 0x010) val &= ~1; // b1
1700 if(stat & 0x020) val &= ~2; // b2
1701 if(stat & 0x040) val &= ~4; // sel
1702 if(stat & 0x080) val &= ~8; // run
1705 if(joy_count == 4) {
1711 #define STATE_VERSION 2
1713 void PCE::save_state(FILEIO* state_fio)
1715 state_fio->FputUint32(STATE_VERSION);
1716 state_fio->FputInt32(this_device_id);
1718 state_fio->FputBool(support_6btn);
1719 state_fio->FputBool(support_sgfx);
1720 state_fio->Fwrite(ram, sizeof(ram), 1);
1721 state_fio->Fwrite(cart + 0x80000, 0x80000, 1);
1722 #ifdef SUPPORT_BACKUP_RAM
1723 state_fio->Fwrite(backup, sizeof(backup), 1);
1724 state_fio->FputUint32(backup_crc32);
1726 state_fio->FputUint32(bank);
1727 state_fio->FputUint8(buffer);
1728 state_fio->FputInt32(prev_width);
1729 state_fio->FputBool(inserted);
1730 state_fio->Fwrite(vdc, sizeof(vdc), 1);
1731 state_fio->Fwrite(&vce, sizeof(vce), 1);
1732 state_fio->Fwrite(&vpc, sizeof(vpc), 1);
1733 state_fio->Fwrite(psg, sizeof(psg), 1);
1734 state_fio->FputUint8(psg_ch);
1735 state_fio->FputUint8(psg_vol);
1736 state_fio->FputUint8(psg_lfo_freq);
1737 state_fio->FputUint8(psg_lfo_ctrl);
1738 state_fio->FputUint8(joy_sel);
1739 state_fio->FputUint8(joy_clr);
1740 state_fio->FputUint8(joy_count);
1741 state_fio->FputUint8(joy_bank);
1742 state_fio->FputBool(joy_6btn);
1745 bool PCE::load_state(FILEIO* state_fio)
1747 if(state_fio->FgetUint32() != STATE_VERSION) {
1750 if(state_fio->FgetInt32() != this_device_id) {
1753 support_6btn = state_fio->FgetBool();
1754 support_sgfx = state_fio->FgetBool();
1755 state_fio->Fread(ram, sizeof(ram), 1);
1756 state_fio->Fread(cart + 0x80000, 0x80000, 1);
1757 #ifdef SUPPORT_BACKUP_RAM
1758 state_fio->Fread(backup, sizeof(backup), 1);
1759 backup_crc32 = state_fio->FgetUint32();
1761 bank = state_fio->FgetUint32();
1762 buffer = state_fio->FgetUint8();
1763 prev_width = state_fio->FgetInt32();
1764 inserted = state_fio->FgetBool();
1765 state_fio->Fread(vdc, sizeof(vdc), 1);
1766 state_fio->Fread(&vce, sizeof(vce), 1);
1767 state_fio->Fread(&vpc, sizeof(vpc), 1);
1768 state_fio->Fread(psg, sizeof(psg), 1);
1769 psg_ch = state_fio->FgetUint8();
1770 psg_vol = state_fio->FgetUint8();
1771 psg_lfo_freq = state_fio->FgetUint8();
1772 psg_lfo_ctrl = state_fio->FgetUint8();
1773 joy_sel = state_fio->FgetUint8();
1774 joy_clr = state_fio->FgetUint8();
1775 joy_count = state_fio->FgetUint8();
1776 joy_bank = state_fio->FgetUint8();
1777 joy_6btn = state_fio->FgetBool();