OSDN Git Service

[VM][SCSI_CDROM] Add write_signal() to control CDDA from MACHINE.
[csp-qt/common_source_project-fm7.git] / source / src / vm / pcengine / pce.cpp
index db5d856..a5f2bf3 100644 (file)
@@ -2,9 +2,9 @@
        NEC-HE PC Engine Emulator 'ePCEngine'
        SHARP X1twin Emulator 'eX1twin'
 
-       Origin : Ootake (joypad)
+       Origin : Ootake (joypad/cdrom)
               : xpce (psg)
-              : MESS (vdc/vce/vpc)
+              : MESS (vdc/vce/vpc/cdrom)
        Author : Takeda.Toshiya
        Date   : 2009.03.11-
 
 #include <math.h>
 #include "pce.h"
 #include "../huc6280.h"
+#ifdef SUPPORT_CDROM
+#include "../msm5205.h"
+#include "../scsi_host.h"
+#include "../scsi_cdrom.h"
+#endif
 
 #define STATE_VSW              0
 #define STATE_VDS              1
@@ -47,6 +52,8 @@
 #define DCR_DVC         0x02    /* VRAM > VRAM EOT interrupt enable */
 #define DCR_DSC         0x01    /* VRAM > SATB EOT interrupt enable */
 
+namespace PCEDEV {
+
 /* just to keep things simple... */
 enum vdc_regs {MAWR = 0, MARR, VxR, reg3, reg4, CR, RCR, BXR, BYR, MWR, HSR, HDR, VPR, VDW, VCR, DCR, SOUR, DESR, LENR, DVSSR };
 
@@ -66,17 +73,20 @@ enum
        INPUT_LINE_NMI
 };
 
+#ifndef SUPPORT_CDROM
+#define backup_locked  false
+#endif
+
 void PCE::initialize()
 {
        // get context
-       joy_stat = emu->joy_buffer();
-       key_stat = emu->key_buffer();
+       joy_stat = emu->get_joy_buffer();
        
        // register event
        register_vline_event(this);
        
 #ifdef SUPPORT_BACKUP_RAM
-       static const uint8 image[8] = {0x48, 0x55, 0x42, 0x4d, 0x00, 0x88, 0x10, 0x80};
+       static const uint8_t image[8] = {0x48, 0x55, 0x42, 0x4d, 0x00, 0x88, 0x10, 0x80};
        memset(backup, 0, sizeof(backup));
        memcpy(backup, image, sizeof(image));
        
@@ -87,7 +97,10 @@ void PCE::initialize()
        }
        delete fio;
        
-       backup_crc32 = getcrc32(backup, sizeof(backup));
+       backup_crc32 = get_crc32(backup, sizeof(backup));
+#endif
+#ifdef SUPPORT_CDROM
+       cdrom_initialize();
 #endif
        inserted = false;
 }
@@ -95,7 +108,7 @@ void PCE::initialize()
 void PCE::release()
 {
 #ifdef SUPPORT_BACKUP_RAM
-       if(backup_crc32 != getcrc32(backup, sizeof(backup))) {
+       if(backup_crc32 != get_crc32(backup, sizeof(backup))) {
                FILEIO* fio = new FILEIO();
                if(fio->Fopen(create_local_path(_T("BACKUP.BIN")), FILEIO_WRITE_BINARY)) {
                        fio->Fwrite(backup, sizeof(backup), 1);
@@ -117,6 +130,9 @@ void PCE::reset()
        vdc_reset();
        psg_reset();
        joy_reset();
+#ifdef SUPPORT_CDROM
+       cdrom_reset();
+#endif
        
        prev_width = -1;
 }
@@ -131,11 +147,17 @@ void PCE::event_vline(int v, int clock)
        pce_interrupt();
 }
 
-void PCE::write_data8(uint32 addr, uint32 data)
+void PCE::write_data8(uint32_t addr, uint32_t data)
 {
-       uint8 mpr = (addr >> 13) & 0xff;
-       uint16 ofs = addr & 0x1fff;
+       uint8_t mpr = (addr >> 13) & 0xff;
+       uint16_t ofs = addr & 0x1fff;
        
+#ifdef SUPPORT_CDROM
+       if(support_cdrom && mpr >= 0x68 && mpr <= 0x87) {
+               cdrom_ram[addr & 0x3ffff] = data;
+               return;
+       }
+#endif
        switch(mpr) {
        case 0x40:
        case 0x41:
@@ -146,9 +168,9 @@ void PCE::write_data8(uint32 addr, uint32 data)
                return;
 #ifdef SUPPORT_BACKUP_RAM
        case 0xf7:
-//             if(inserted) {
+               if(!backup_locked) {
                        backup[ofs] = data;
-//             }
+               }
                return;
 #endif
        case 0xf8:
@@ -202,6 +224,13 @@ void PCE::write_data8(uint32 addr, uint32 data)
                        buffer = data;
                        d_cpu->irq_status_w(addr, data);
                        break;
+#ifdef SUPPORT_CDROM
+               case 0x1800:
+                       if(support_cdrom && (addr & 0x1e00) != 0x1a00) {
+                               cdrom_write(addr, data);
+                               break;
+                       }
+#endif
                }
                return;
        }
@@ -211,14 +240,19 @@ void PCE::write_data8(uint32 addr, uint32 data)
        }
 }
 
-uint32 PCE::read_data8(uint32 addr)
+uint32_t PCE::read_data8(uint32_t addr)
 {
-       uint8 mpr = (addr >> 13) & 0xff;
-       uint16 ofs = addr & 0x1fff;
+       uint8_t mpr = (addr >> 13) & 0xff;
+       uint16_t ofs = addr & 0x1fff;
        
        if(mpr <= 0x3f) {
                return cart[addr & 0x7ffff];
        }
+#ifdef SUPPORT_CDROM
+       if(support_cdrom && mpr >= 0x68 && mpr <= 0x87) {
+               return cdrom_ram[addr & 0x3ffff];
+       }
+#endif
        if(mpr <= 0x7f) {
                return cart[bank | (addr & 0x7ffff)];
        }
@@ -271,13 +305,19 @@ uint32 PCE::read_data8(uint32 addr)
                                buffer = (buffer & 0xf8) | (d_cpu->irq_status_r(addr) & 0x07);
                        }
                        return buffer;
+#ifdef SUPPORT_CDROM
+               case 0x1800:
+                       if(support_cdrom && (addr & 0x1e00) != 0x1a00) {
+                               return cdrom_read(addr);
+                       }
+#endif
                }
                break;
        }
        return 0xff;
 }
 
-void PCE::write_io8(uint32 addr, uint32 data)
+void PCE::write_io8(uint32_t addr, uint32_t data)
 {
 #ifdef SUPPORT_SUPER_GFX
        if(support_sgfx) {
@@ -287,7 +327,7 @@ void PCE::write_io8(uint32 addr, uint32 data)
        vdc_w(0, addr, data);
 }
 
-uint32 PCE::read_io8(uint32 addr)
+uint32_t PCE::read_io8(uint32_t addr)
 {
 #ifdef SUPPORT_SUPER_GFX
        if(support_sgfx) {
@@ -300,7 +340,7 @@ uint32 PCE::read_io8(uint32 addr)
 void PCE::draw_screen()
 {
        int dx = (SCREEN_WIDTH - vdc[0].physical_width) / 2, sx = 0;
-       int dy = (SCREEN_HEIGHT - 238) / 2;
+       int dy = (SCREEN_HEIGHT - 240) / 2;
        
        if(dx < 0) {
                sx = -dx;
@@ -309,14 +349,15 @@ void PCE::draw_screen()
 #ifndef _X1TWIN
        if(prev_width != vdc[0].physical_width) {
                for(int y = 0; y < SCREEN_HEIGHT; y++) {
-                       memset(emu->screen_buffer(y), 0, sizeof(scrntype) * SCREEN_WIDTH);
+                       memset(emu->get_screen_buffer(y), 0, sizeof(scrntype_t) * SCREEN_WIDTH);
                }
                prev_width = vdc[0].physical_width;
        }
+       emu->set_vm_screen_lines(240);
 #endif
-       for(int y = 0; y < 238; y++, dy++) {
-               scrntype* src = &vce.bmp[y + 17][86];
-               scrntype* dst = emu->screen_buffer(dy);
+       for(int y = 0; y < 240; y++, dy++) {
+               scrntype_t* src = &vce.bmp[y + 17][86];
+               scrntype_t* dst = emu->get_screen_buffer(dy);
                for(int x = sx, x2 = dx; x < vdc[0].physical_width && x2 < SCREEN_WIDTH; x++, x2++) {
                        dst[x2] = src[x];
                }
@@ -327,6 +368,15 @@ void PCE::open_cart(const _TCHAR* file_path)
 {
        FILEIO* fio = new FILEIO();
        
+       support_6btn_pad = ((config.joystick_type & 1) != 0);
+       support_multi_tap = ((config.joystick_type & 2) != 0);
+#ifdef SUPPORT_SUPER_GFX
+       support_sgfx = false;
+#endif
+#ifdef SUPPORT_CDROM
+       support_cdrom = false;
+#endif
+       
        if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
                memset(cart, 0xff, sizeof(cart));
                fio->Fseek(0, FILEIO_SEEK_END);
@@ -354,8 +404,7 @@ void PCE::open_cart(const _TCHAR* file_path)
                        memcpy(cart + 0x080000, cart + 0x040000, 0x040000);     /* Set up 080000 - 0BFFFF region */
                        memcpy(cart + 0x0C0000, cart + 0x040000, 0x040000);     /* Set up 0C0000 - 0FFFFF region */
                        memcpy(cart + 0x040000, cart, 0x040000);                /* Set up 040000 - 07FFFF region */
-               }
-               else {
+               } else {
                        /* mirror 256KB rom data */
                        if (size <= 0x040000)
                                memcpy(cart + 0x040000, cart, 0x040000);
@@ -363,15 +412,30 @@ void PCE::open_cart(const _TCHAR* file_path)
                        if (size <= 0x080000)
                                memcpy(cart + 0x080000, cart, 0x080000);
                }
-               uint32 cart_crc32 = getcrc32(cart,size);
-               support_sgfx = (size == 0x100000 && cart_crc32 == 0x8c4588e2)   // 1941 Counter Attack
-                           || (size == 0x100000 && cart_crc32 == 0x4c2126b0)   // Aldynes
-                           || (size == 0x080000 && cart_crc32 == 0x3b13af61)   // Battle Ace
-                           || (size == 0x100000 && cart_crc32 == 0xb486a8ed)   // Daimakaimura
-                           || (size == 0x0c0000 && cart_crc32 == 0xbebfe042)   // Darius Plus
-                           || (size == 0x100000 && cart_crc32 == 0x1e1d0319)   // Darius Plus (1024K)
-                           || (size == 0x080000 && cart_crc32 == 0x1f041166);  // Grandzort
-               support_6btn = (size == 0x280000 && cart_crc32 == 0xd15cb6bb);  // Street Fighter II
+               uint32_t cart_crc32 = get_crc32(cart, size);
+#ifdef SUPPORT_SUPER_GFX
+               if((size == 0x100000 && cart_crc32 == 0x8c4588e2) ||    // 1941 Counter Attack
+                  (size == 0x100000 && cart_crc32 == 0x4c2126b0) ||    // Aldynes
+                  (size == 0x080000 && cart_crc32 == 0x3b13af61) ||    // Battle Ace
+                  (size == 0x100000 && cart_crc32 == 0xb486a8ed) ||    // Daimakaimura
+                  (size == 0x0c0000 && cart_crc32 == 0xbebfe042) ||    // Darius Plus
+                  (size == 0x100000 && cart_crc32 == 0x1e1d0319) ||    // Darius Plus (1024K)
+                  (size == 0x080000 && cart_crc32 == 0x1f041166)) {    // Grandzort
+                       support_sgfx = true;
+               }
+#endif
+#ifdef SUPPORT_CDROM
+               if(size >= 0x40000 && memcmp(cart + 0x3ffb6, "PC Engine CD-ROM SYSTEM", 23) == 0) {
+                       support_cdrom = true;
+               }
+#endif
+               if((size == 0x280000 && cart_crc32 == 0xd15cb6bb) ||    // Street Fighter II Champion Edition
+                  (size == 0x100000 && cart_crc32 == 0xd6fc51ce)) {    // Strip Figher II
+                       support_6btn_pad = true;
+               }
+               if(size == 0x40000 && cart_crc32 == 0x80c3f824) {       // Yokai Dochu Ki
+                       support_multi_tap = false;
+               }
                inserted = true;
        }
        delete fio;
@@ -400,9 +464,9 @@ void PCE::pce_interrupt()
                        /* 0 - no sprite and background pixels drawn
                           1 - background pixel drawn
                           otherwise is 2 + sprite# */
-                       uint8 drawn[VDC_WPF];
+                       uint8_t drawn[VDC_WPF];
                        /* our line buffer */
-                       scrntype *line_buffer = &vce.bmp[vce.current_bitmap_line][86];
+                       scrntype_t *line_buffer = &vce.bmp[vce.current_bitmap_line][86];
 
                        /* clear our priority/sprite collision detection buffer. */
                        memset(drawn, 0, VDC_WPF);
@@ -430,6 +494,7 @@ void PCE::pce_interrupt()
        vdc_advance_line(0);
 }
 
+#ifdef SUPPORT_SUPER_GFX
 void PCE::sgx_interrupt()
 {
        /* Draw the last scanline */
@@ -445,9 +510,9 @@ void PCE::sgx_interrupt()
                        /* 0 - no sprite and background pixels drawn
                           1 - background pixel drawn
                           otherwise is 2 + sprite# */
-                       uint8 drawn[2][512];
-                       scrntype *line_buffer;
-                       scrntype temp_buffer[2][512];
+                       uint8_t drawn[2][512];
+                       scrntype_t *line_buffer;
+                       scrntype_t temp_buffer[2][512];
                        int i;
 
                        /* clear our priority/sprite collision detection buffer. */
@@ -583,6 +648,7 @@ void PCE::sgx_interrupt()
        vdc_advance_line(0);
        vdc_advance_line(1);
 }
+#endif
 
 void PCE::vdc_advance_line(int which)
 {
@@ -745,7 +811,7 @@ void PCE::draw_black_line(int line)
        int i;
 
        /* our line buffer */
-       scrntype *line_buffer = vce.bmp[line];
+       scrntype_t *line_buffer = vce.bmp[line];
 
        for( i=0; i< VDC_WPF; i++ )
                line_buffer[i] = 0;
@@ -756,30 +822,32 @@ void PCE::draw_overscan_line(int line)
        int i;
 
        /* Are we in greyscale mode or in color mode? */
-       scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
+       scrntype_t *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
 
        /* our line buffer */
-       scrntype *line_buffer = vce.bmp[line];
+       scrntype_t *line_buffer = vce.bmp[line];
 
        for ( i = 0; i < VDC_WPF; i++ )
                line_buffer[i] = color_base[vce.vce_data[0x100].w.l];
 }
 
+#ifdef SUPPORT_SUPER_GFX
 void PCE::draw_sgx_overscan_line(int line)
 {
        int i;
 
        /* Are we in greyscale mode or in color mode? */
-       scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
+       scrntype_t *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
 
        /* our line buffer */
-       scrntype *line_buffer = vce.bmp[line];
+       scrntype_t *line_buffer = vce.bmp[line];
 
        for ( i = 0; i < VDC_WPF; i++ )
                line_buffer[i] = color_base[vce.vce_data[0].w.l];
 }
+#endif
 
-void PCE::vram_write(int which, uint32 offset, uint8 data)
+void PCE::vram_write(int which, uint32_t offset, uint8_t data)
 {
        if(offset & 0x10000)
        {
@@ -791,9 +859,9 @@ void PCE::vram_write(int which, uint32 offset, uint8 data)
        }
 }
 
-uint8 PCE::vram_read(int which, uint32 offset)
+uint8_t PCE::vram_read(int which, uint32_t offset)
 {
-       uint8 temp;
+       uint8_t temp;
 
        if(offset & 0x10000)
        {
@@ -807,7 +875,7 @@ uint8 PCE::vram_read(int which, uint32 offset)
        return temp;
 }
 
-void PCE::vdc_w(int which, uint16 offset, uint8 data)
+void PCE::vdc_w(int which, uint16_t offset, uint8_t data)
 {
        switch(offset&3)
        {
@@ -890,7 +958,7 @@ void PCE::vdc_w(int which, uint16 offset, uint8 data)
        }
 }
 
-uint8 PCE::vdc_r(int which, uint16 offset)
+uint8_t PCE::vdc_r(int which, uint16_t offset)
 {
        int temp = 0;
        switch(offset & 3)
@@ -916,7 +984,7 @@ uint8 PCE::vdc_r(int which, uint16 offset)
        return (temp);
 }
 
-uint8 PCE::vce_r(uint16 offset)
+uint8_t PCE::vce_r(uint16_t offset)
 {
        int temp = 0xFF;
        switch(offset & 7)
@@ -934,7 +1002,7 @@ uint8 PCE::vce_r(uint16 offset)
        return (temp);
 }
 
-void PCE::vce_w(uint16 offset, uint8 data)
+void PCE::vce_w(uint16_t offset, uint8_t data)
 {
        switch(offset & 7)
        {
@@ -965,7 +1033,7 @@ void PCE::vce_w(uint16 offset, uint8 data)
        }
 }
 
-void PCE::pce_refresh_line(int which, int line, int external_input, uint8 *drawn, scrntype *line_buffer)
+void PCE::pce_refresh_line(int which, int line, int external_input, uint8_t *drawn, scrntype_t *line_buffer)
 {
        static const int width_table[4] = {5, 6, 7, 7};
 
@@ -983,13 +1051,13 @@ void PCE::pce_refresh_line(int which, int line, int external_input, uint8 *drawn
        int nt_row = (v_line >> 3);
 
        /* virtual X size (# bits to shift) */
-       int v_width =           width_table[(vdc[which].vdc_data[MWR].w.l >> 4) & 3];
+       int v_width = width_table[(vdc[which].vdc_data[MWR].w.l >> 4) & 3];
 
        /* pointer to the name table (Background Attribute Table) in VRAM */
-       uint8 *bat = &(vdc[which].vram[nt_row << (v_width+1)]);
+       uint8_t *bat = &(vdc[which].vram[nt_row << (v_width+1)]);
 
        /* Are we in greyscale mode or in color mode? */
-       scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
+       scrntype_t *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
 
        int b0, b1, b2, b3;
        int i0, i1, i2, i3;
@@ -1094,13 +1162,13 @@ void PCE::conv_obj(int which, int i, int l, int hf, int vf, char *buf)
        }
 }
 
-void PCE::pce_refresh_sprites(int which, int line, uint8 *drawn, scrntype *line_buffer)
+void PCE::pce_refresh_sprites(int which, int line, uint8_t *drawn, scrntype_t *line_buffer)
 {
        int i;
-       uint8 sprites_drawn = 0;
+       uint8_t sprites_drawn = 0;
 
        /* Are we in greyscale mode or in color mode? */
-       scrntype *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
+       scrntype_t *color_base = vce.palette + (vce.vce_control & 0x80 ? 512 : 0);
 
        /* count up: Highest priority is Sprite 0 */
        for(i = 0; i < 64; i++)
@@ -1333,7 +1401,7 @@ void PCE::vdc_do_dma(int which)
        int dvc = (vdc[which].vdc_data[DCR].w.l >> 1) & 1;
 
        do {
-               uint8 l, h;
+               uint8_t l, h;
 
                l = vram_read(which, src<<1);
                h = vram_read(which, (src<<1) + 1);
@@ -1380,7 +1448,7 @@ void PCE::vpc_update_prio_map()
        }
 }
 
-void PCE::vpc_w(uint16 offset, uint8 data)
+void PCE::vpc_w(uint16_t offset, uint8_t data)
 {
        switch( offset & 0x07 )
        {
@@ -1424,9 +1492,9 @@ void PCE::vpc_w(uint16 offset, uint8 data)
        }
 }
 
-uint8 PCE::vpc_r(uint16 offset)
+uint8_t PCE::vpc_r(uint16_t offset)
 {
-       uint8 data = 0;
+       uint8_t data = 0;
        switch( offset & 0x07 )
        {
        case 0x00:  /* Priority register #0 */
@@ -1451,7 +1519,8 @@ uint8 PCE::vpc_r(uint16 offset)
        return data;
 }
 
-void PCE::sgx_vdc_w(uint16 offset, uint8 data)
+#ifdef SUPPORT_SUPER_GFX
+void PCE::sgx_vdc_w(uint16_t offset, uint8_t data)
 {
        if ( vpc.vdc_select )
        {
@@ -1463,15 +1532,17 @@ void PCE::sgx_vdc_w(uint16 offset, uint8 data)
        }
 }
 
-uint8 PCE::sgx_vdc_r(uint16 offset)
+uint8_t PCE::sgx_vdc_r(uint16_t offset)
 {
        return ( vpc.vdc_select ) ? vdc_r( 1, offset ) : vdc_r( 0, offset );
 }
+#endif
 
 // psg
 
 void PCE::psg_reset()
 {
+       touch_sound();
        memset(psg, 0, sizeof(psg));
        for (int i = 0; i < 6; i++) {
                psg[i].regs[4] = 0x80;
@@ -1482,29 +1553,36 @@ void PCE::psg_reset()
        psg_vol = psg_lfo_freq = psg_lfo_ctrl = 0;
 }
 
-void PCE::psg_write(uint16 addr, uint8 data)
+void PCE::psg_write(uint16_t addr, uint8_t data)
 {
        switch(addr & 0x1f) {
        case 0:
+               touch_sound();
                psg_ch = data & 7;
                break;
        case 1:
+               touch_sound();
                psg_vol = data;
                break;
        case 2:
+               touch_sound();
                psg[psg_ch].regs[2] = data;
                break;
        case 3:
+               touch_sound();
 //             psg[psg_ch].regs[3] = data & 0x1f;
                psg[psg_ch].regs[3] = data & 0xf;
                break;
        case 4:
+               touch_sound();
                psg[psg_ch].regs[4] = data;
                break;
        case 5:
+               touch_sound();
                psg[psg_ch].regs[5] = data;
                break;
        case 6:
+               touch_sound();
                if(psg[psg_ch].regs[4] & 0x40) {
                        psg[psg_ch].wav[0] =data & 0x1f;
                }
@@ -1514,18 +1592,21 @@ void PCE::psg_write(uint16 addr, uint8 data)
                }
                break;
        case 7:
+               touch_sound();
                psg[psg_ch].regs[7] = data;
                break;
        case 8:
+               touch_sound();
                psg_lfo_freq = data;
                break;
        case 9:
+               touch_sound();
                psg_lfo_ctrl = data;
                break;
        }
 }
 
-uint8 PCE::psg_read(uint16 addr)
+uint8_t PCE::psg_read(uint16_t addr)
 {
        int ptr;
        
@@ -1556,7 +1637,7 @@ uint8 PCE::psg_read(uint16 addr)
        return 0xff;
 }
 
-void PCE::mix(int32* buffer, int cnt)
+void PCE::mix(int32_t* buffer, int cnt)
 {
        int vol_tbl[32] = {
                 100, 451, 508, 573, 646, 728, 821, 925,1043,1175,1325, 1493, 1683, 1898, 2139, 2411,
@@ -1573,24 +1654,24 @@ void PCE::mix(int32* buffer, int cnt)
                }
                else if(psg[ch].regs[4] & 0x40) {
                        // dda
-                       int32 wav = ((int32)psg[ch].wav[0] - 16) * 702;
-                       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;
+                       int32_t wav = ((int32_t)psg[ch].wav[0] - 16) * 702;
+                       int32_t 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;
                        vol = (vol < 0) ? 0 : (vol > 31) ? 31 : vol;
-                       vol = wav * vol_tbl[vol] / 16384;
+                       int32_t outvol = wav * vol_tbl[vol] / 16384;
                        for(int i = 0, j = 0; i < cnt; i++, j += 2) {
-                               buffer[j    ] += vol; // L
-                               buffer[j + 1] += vol; // R
+                               buffer[j    ] += apply_volume(outvol, volume_l); // L
+                               buffer[j + 1] += apply_volume(outvol, volume_r); // R
                        }
                }
                else if(ch >= 4 && (psg[ch].regs[7] & 0x80)) {
                        // noise
-                       uint16 freq = (psg[ch].regs[7] & 0x1f);
-                       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;
+                       uint16_t freq = (psg[ch].regs[7] & 0x1f);
+                       int32_t 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;
                        vol = (vol < 0) ? 0 : (vol > 31) ? 31 : vol;
                        vol = vol_tbl[vol];
                        for(int i = 0, j = 0; i < cnt; i++, j += 2) {
                                psg[ch].remain += 3000 + freq * 512;
-                               uint32 t = psg[ch].remain / sample_rate;
+                               uint32_t t = psg[ch].remain / sample_rate;
                                if(t >= 1) {
                                        if(psg[ch].randval & 0x80000) {
                                                psg[ch].randval = ((psg[ch].randval ^ 4) << 1) + 1;
@@ -1602,178 +1683,1030 @@ void PCE::mix(int32* buffer, int cnt)
                                        }
                                        psg[ch].remain -= sample_rate * t;
                                }
-                               int32 outvol = (int32)((psg[ch].noise ? 10 * 702 : -10 * 702) * vol / 16384);
-                               buffer[j    ] += outvol; // L
-                               buffer[j + 1] += outvol; // R
+                               int32_t outvol = (int32_t)((psg[ch].noise ? 10 * 702 : -10 * 702) * vol / 16384);
+                               buffer[j    ] += apply_volume(outvol, volume_l); // L
+                               buffer[j + 1] += apply_volume(outvol, volume_r); // R
                        }
                }
                else {
-                       int32 wav[32];
+                       int32_t wav[32];
                        for(int i = 0; i < 32; i++) {
-                               wav[i] = ((int32)psg[ch].wav[i] - 16) * 702;
+                               wav[i] = ((int32_t)psg[ch].wav[i] - 16) * 702;
                        }
-                       uint32 freq = psg[ch].regs[2] + ((uint32)psg[ch].regs[3] << 8);
+                       uint32_t freq = psg[ch].regs[2] + ((uint32_t)psg[ch].regs[3] << 8);
                        if(freq) {
-                               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;
+                               int32_t 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;
                                vol = (vol < 0) ? 0 : (vol > 31) ? 31 : vol;
                                vol = vol_tbl[vol];
                                for(int i = 0, j = 0; i < cnt; i++, j += 2) {
-                                       int32 outvol = wav[psg[ch].genptr] * vol / 16384;
-                                       buffer[j    ] += outvol; // L
-                                       buffer[j + 1] += outvol; // R
+                                       int32_t outvol = wav[psg[ch].genptr] * vol / 16384;
+                                       buffer[j    ] += apply_volume(outvol, volume_l); // L
+                                       buffer[j + 1] += apply_volume(outvol, volume_r); // R
                                        psg[ch].remain += 32 * 1118608 / freq;
-                                       uint32 t = psg[ch].remain / (10 * sample_rate);
+                                       uint32_t t = psg[ch].remain / (10 * sample_rate);
                                        psg[ch].genptr = (psg[ch].genptr + t) & 0x1f;
                                        psg[ch].remain -= 10 * sample_rate * t;
                                }
                        }
                }
        }
+#ifdef SUPPORT_CDROM
+       if(support_cdrom) {
+               if(!msm_idle) {
+                       d_msm->mix(buffer, cnt);
+               }
+               d_scsi_cdrom->mix(buffer, cnt);
+       }
+#endif
+}
+
+void PCE::set_volume(int ch, int decibel_l, int decibel_r)
+{
+       volume_l = decibel_to_volume(decibel_l);
+       volume_r = decibel_to_volume(decibel_r);
 }
 
 // joypad (non multipad)
 
 void PCE::joy_reset()
 {
-       joy_sel = joy_bank = 1;
-       joy_clr = joy_count = 0;
+       joy_counter = 0;
+       joy_high_nibble = joy_second_byte = false;
+}
+
+void PCE::joy_write(uint16_t addr, uint8_t data)
+{
+       joy_high_nibble = ((data & 1) != 0);
+       
+       if(data & 2) {
+               joy_counter = 0;
+               joy_high_nibble = false;
+               joy_second_byte = !joy_second_byte;
+       }
 }
 
-void PCE::joy_write(uint16 addr, uint8 data)
+uint8_t PCE::joy_read(uint16_t addr)
 {
-       uint8 new_sel = data & 1;
-       uint8 new_clr = data & 2;
+       uint8_t index;
        
-       if(joy_sel && new_sel) {
-               if(joy_clr && !new_clr) {
-                       joy_count = 0;
-                       joy_bank ^= 1;
+       if(joy_high_nibble) {
+               if(++joy_counter == 16) {
+                       joy_counter = 0;
                }
        }
-       else if(!joy_sel && new_sel) {
-               joy_count = (joy_count + 1) & 15;
+       if(joy_counter == 0) {
+               return 0x00;
+       }
+       if(support_multi_tap) {
+               if(joy_counter > 4) {
+                       return 0x0f;
+               }
+               index = joy_counter;
+       } else {
+               index = 1;
+       }
+       if(support_6btn_pad) {
+               return joy_6btn_pad_r(index);
+       } else {
+               return joy_2btn_pad_r(index);
        }
-       joy_sel = new_sel;
-       joy_clr = new_clr;
 }
 
-uint8 PCE::joy_read(uint16 addr)
+uint8_t PCE::joy_2btn_pad_r(uint8_t index)
 {
-       uint8 val = 0xf;
-       uint32 stat = 0;
+       uint8_t data = 0x0f;
        
-       if(joy_count == 0) {
-               stat = joy_stat[0];
-               if(key_stat[0x26]) stat |= 0x001;       // up
-               if(key_stat[0x28]) stat |= 0x002;       // down
-               if(key_stat[0x25]) stat |= 0x004;       // left
-               if(key_stat[0x27]) stat |= 0x008;       // right
-               if(key_stat[0x44]) stat |= 0x010;       // d (1)
-               if(key_stat[0x53]) stat |= 0x020;       // s (2)
-               if(key_stat[0x20]) stat |= 0x040;       // space (select)
-               if(key_stat[0x0d]) stat |= 0x080;       // enter (run)
-               if(key_stat[0x41]) stat |= 0x100;       // a (3)
-               if(key_stat[0x51]) stat |= 0x200;       // q (4)
-               if(key_stat[0x57]) stat |= 0x400;       // w (5)
-               if(key_stat[0x45]) stat |= 0x800;       // e (6)
-       } else if(joy_count == 1) {
-               stat = joy_stat[1];
-       }
-       if(support_6btn && joy_bank) {
-               if(joy_sel) {
-                       val = 0;
+       if(joy_high_nibble) {
+               if(joy_stat[index - 1] & 0x001) data &= ~0x01;  // Up
+               if(joy_stat[index - 1] & 0x008) data &= ~0x02;  // Right
+               if(joy_stat[index - 1] & 0x002) data &= ~0x04;  // Down
+               if(joy_stat[index - 1] & 0x004) data &= ~0x08;  // Left
+       } else {
+               if(joy_stat[index - 1] & 0x010) data &= ~0x01;  // Button #1
+               if(joy_stat[index - 1] & 0x020) data &= ~0x02;  // Button #2
+               if(joy_stat[index - 1] & 0x040) data &= ~0x04;  // Select
+               if(joy_stat[index - 1] & 0x080) data &= ~0x08;  // Run
+       }
+       return data;
+}
+
+uint8_t PCE::joy_6btn_pad_r(uint8_t index)
+{
+       uint8_t data = 0x0f;
+       
+       if(joy_second_byte) {
+               if(joy_high_nibble) {
+                       if(joy_stat[index - 1] & 0x001) data &= ~0x01;  // Up
+                       if(joy_stat[index - 1] & 0x008) data &= ~0x02;  // Right
+                       if(joy_stat[index - 1] & 0x002) data &= ~0x04;  // Down
+                       if(joy_stat[index - 1] & 0x004) data &= ~0x08;  // Left
                } else {
-                       if(stat & 0x100) val &= ~1;     // b3
-                       if(stat & 0x200) val &= ~2;     // b4
-                       if(stat & 0x400) val &= ~4;     // b5
-                       if(stat & 0x800) val &= ~8;     // b6
+                       if(joy_stat[index - 1] & 0x010) data &= ~0x01;  // Button #1
+                       if(joy_stat[index - 1] & 0x020) data &= ~0x02;  // Button #2
+                       if(joy_stat[index - 1] & 0x040) data &= ~0x04;  // Select
+                       if(joy_stat[index - 1] & 0x080) data &= ~0x08;  // Run
                }
        } else {
-               if(joy_sel) {
-                       if(stat & 0x001) val &= ~1;     // up
-                       if(stat & 0x008) val &= ~2;     // right
-                       if(stat & 0x002) val &= ~4;     // down
-                       if(stat & 0x004) val &= ~8;     // left
+               if(joy_high_nibble) {
+                       return 0x00;
                } else {
-                       if(stat & 0x010) val &= ~1;     // b1
-                       if(stat & 0x020) val &= ~2;     // b2
-                       if(stat & 0x040) val &= ~4;     // sel
-                       if(stat & 0x080) val &= ~8;     // run
+                       if(joy_stat[index - 1] & 0x100) data &= ~0x01;  // Button #3
+                       if(joy_stat[index - 1] & 0x200) data &= ~0x02;  // Button #4
+                       if(joy_stat[index - 1] & 0x400) data &= ~0x04;  // Button #5
+                       if(joy_stat[index - 1] & 0x800) data &= ~0x08;  // Button #6
                }
        }
-       if(joy_count == 4) {
-               joy_bank = 1;
+       if(!support_multi_tap) {
+               if(joy_counter == 5 && !joy_high_nibble) {
+                       joy_second_byte = false;
+               }
        }
-       return val;
+       return data;
 }
 
-#define STATE_VERSION  2
+// CD-ROM^2
+
+#ifdef SUPPORT_CDROM
+#define PCE_CD_IRQ_TRANSFER_READY      0x40
+#define PCE_CD_IRQ_TRANSFER_DONE       0x20
+#define PCE_CD_IRQ_BRAM                        0x10 /* ??? */
+#define PCE_CD_IRQ_SAMPLE_FULL_PLAY    0x08
+#define PCE_CD_IRQ_SAMPLE_HALF_PLAY    0x04
+
+#define PCE_CD_ADPCM_PLAY_FLAG         0x08
+#define PCE_CD_ADPCM_STOP_FLAG         0x01
+
+#define EVENT_CDDA_FADE_IN             0
+#define EVENT_CDDA_FADE_OUT            1
+#define EVENT_ADPCM_FADE_IN            2
+#define EVENT_ADPCM_FADE_OUT           3
+
+void PCE::cdrom_initialize()
+{
+       adpcm_clock_divider = 1;
+       backup_locked = true;
+       event_cdda_fader = event_adpcm_fader = -1;
+}
 
-void PCE::save_state(FILEIO* state_fio)
+void PCE::cdrom_reset()
 {
-       state_fio->FputUint32(STATE_VERSION);
-       state_fio->FputInt32(this_device_id);
+       touch_sound();
+       memset(cdrom_regs, 0, sizeof(cdrom_regs));
+       cdrom_regs[0x0c] |= PCE_CD_ADPCM_STOP_FLAG;
+       cdrom_regs[0x0c] &= ~PCE_CD_ADPCM_PLAY_FLAG;
        
-       state_fio->FputBool(support_6btn);
-       state_fio->FputBool(support_sgfx);
-       state_fio->Fwrite(ram, sizeof(ram), 1);
-       state_fio->Fwrite(cart + 0x80000, 0x80000, 1);
-#ifdef SUPPORT_BACKUP_RAM
-       state_fio->Fwrite(backup, sizeof(backup), 1);
-       state_fio->FputUint32(backup_crc32);
+       irq_status = drq_status = false;
+       
+       adpcm_read_ptr = adpcm_write_ptr = 0;
+       adpcm_read_buf = adpcm_write_buf = 0;
+       adpcm_dma_enabled = false;
+       adpcm_play_in_progress = false;
+       msm_idle = 1;
+       
+       if(event_cdda_fader != -1) {
+               cancel_event(this, event_cdda_fader);
+       }
+       if(event_adpcm_fader != -1) {
+               cancel_event(this, event_adpcm_fader);
+       }
+       cdda_volume = adpcm_volume = 100.0;
+       event_cdda_fader = event_adpcm_fader = -1;
+       
+       d_scsi_cdrom->set_volume((int)cdda_volume);
+       d_msm->set_volume((int)adpcm_volume);
+}
+
+void PCE::cdrom_write(uint16_t addr, uint8_t data)
+{
+       touch_sound();
+       switch(addr & 0x0f) {
+       case 0x00:  /* CDC status */
+               data = 0xd0; // Force set data to $D0
+               // Reset req?
+               d_scsi_host->write_signal(SIG_SCSI_SEL, 1, 1);
+               d_scsi_host->write_signal(SIG_SCSI_SEL, 0, 1);
+               adpcm_dma_enabled = false;
+               // From Ootake v2.38
+               cdrom_regs[0x03] = 0x00; // Reset IRQ status at al.
+               set_cdrom_irq_line(0x0, 0x0); // Update IRQ
+               break;
+               
+       case 0x01:  /* CDC command / status / data */
+               //out_debug_log(_T("CDC CMD %02x\n"), data);
+               write_cdrom_data(data);
+               break;
+               
+       case 0x02:  /* ADPCM / CD control / IRQ enable/disable */
+               /* bit 6 - transfer ready irq */
+               /* bit 5 - transfer done irq */
+               /* bit 4 - BRAM irq? */
+               /* bit 3 - ADPCM FULL irq */
+               /* bit 2 - ADPCM HALF irq */
+               if(data & 0x80) {
+                       set_ack();
+               } else {
+                       clear_ack();
+               }
+               /* Update mask register now otherwise it won't catch the irq enable/disable change */
+               cdrom_regs[0x02] = data;
+               /* Don't set or reset any irq lines, but just verify the current state */
+               set_cdrom_irq_line(0, 0);
+               break;
+               
+       case 0x03:  /* BRAM lock / CD status / IRQ - Read Only register */
+               break;
+               
+       case 0x04:  /* CD reset */
+               if(data & 0x02) {
+                       // Reset CDROM
+                       // From Ootake v2.38
+                       d_scsi_cdrom->write_signal(SIG_SCSI_CDROM_CDDA_STOP, 0xff, 0xff);
+                       // Reset ADPCM hardware
+                       reset_adpcm();
+                       adpcm_dma_enabled = false;
+                       out_debug_log(_T("ADPCM CMD=$04 RESET\n"));
+                       cdrom_regs[0x03] = 0x00; // Reset IRQ status at al.
+                       set_cdrom_irq_line(0x0, 0x0); // Update IRQ
+               }
+               d_scsi_host->write_signal(SIG_SCSI_RST, data, 0x02);
+               break;
+               
+       case 0x05:  /* Convert PCM data / PCM data */
+       case 0x06:  /* PCM data */
+               break;
+               
+       case 0x07:  /* BRAM unlock / CD status */
+               if(data & 0x80) {
+                       backup_locked = false;
+               }
+               break;
+               
+       case 0x08:  /* ADPCM address (LSB) / CD data */
+       case 0x09:  /* ADPCM address (MSB) */
+               break;
+               
+       case 0x0a:  /* ADPCM RAM data port */
+               if(adpcm_write_buf > 0) {
+                       adpcm_write_buf--;
+               } else {
+                       write_adpcm_ram(data);
+               }
+               break;
+               
+       case 0x0b:  /* ADPCM DMA control */
+               if(data & 3) {
+                       /* Start CD to ADPCM transfer */
+                       adpcm_dma_enabled = true;
+                       cdrom_regs[0x0c] |= 0x04;
+                       if(d_scsi_cdrom->get_cur_command() == SCSI_CMD_READ6 &&
+                          d_scsi_host->read_signal(SIG_SCSI_BSY) != 0 &&
+                          d_scsi_host->read_signal(SIG_SCSI_REQ) != 0 &&
+                          d_scsi_host->read_signal(SIG_SCSI_CD ) == 0 &&
+                          d_scsi_host->read_signal(SIG_SCSI_MSG) == 0 &&
+                          d_scsi_host->read_signal(SIG_SCSI_IO ) != 0) {
+                               // already data is received, read first byte
+                               adpcm_do_dma();
+                               out_debug_log(_T("Start DMA port $0B/ALREADY READ DATA ADPCM_WRITE_PTR=%04x ADPCM_READ_PTR=%04x MSM_START_ADDR=%04x\n"),adpcm_write_ptr, adpcm_read_ptr, msm_start_addr);
+                       } else {
+                               //cdrom_regs[0x0c] |= 0x04;
+                               out_debug_log(_T("Start DMA port $0B/WAIT FOR DATA\n"));
+                       }
+               } else {
+                       //adpcm_dma_enabled = false;
+               }
+               break;
+               
+       case 0x0c:  /* ADPCM status */
+               break;
+               
+       case 0x0d:  /* ADPCM address control */
+               if((cdrom_regs[0x0d] & 0x80) && !(data & 0x80)) {
+                       // Reset ADPCM hardware
+                       reset_adpcm();
+                       adpcm_stop(true);
+                       out_debug_log(_T("ADPCM CMD=$0D RESET\n"));
+               }
+               if(data & 0x02) {
+                       // ADPCM set write address
+                       adpcm_write_ptr = (cdrom_regs[0x09] << 8) | cdrom_regs[0x08];
+                       adpcm_write_buf = ((data & 1) == 0) ? 1 : 0;
+                       adpcm_written = 0;
+                       out_debug_log(_T("ADPCM SET WRITE ADDRESS ADDR=%04x\n"), adpcm_write_ptr);
+               }
+               if(data & 0x08) {
+                       // ADPCM set read address
+                       adpcm_read_ptr = (cdrom_regs[0x09] << 8) | cdrom_regs[0x08];
+                       adpcm_read_buf = ((data & 0x04) == 0) ? 2 : 1;
+                       out_debug_log(_T("ADPCM SET READ ADDRESS ADDR=%04x\n"), adpcm_read_ptr);
+               }
+               if(data & 0x10) {
+                       // ADPCM set length
+                       adpcm_length = (cdrom_regs[0x09] << 8) | cdrom_regs[0x08];
+                       uint32_t _clk = (ADPCM_CLOCK / 6) / adpcm_clock_divider;
+
+                       if(((adpcm_read_ptr & 0xffff) >= 0x4000) &&
+                          ((adpcm_write_ptr & 0xffff) == 0x0000) &&
+                          (adpcm_length != 0x8000) &&
+                          (adpcm_length != 0xffff) &&
+                          (_clk < 16000)) {
+                               adpcm_length = adpcm_length & 0x7fff;
+                       }
+                       out_debug_log(_T("ADPCM SET LENGTH LENGTH=%04x\n"), adpcm_length);
+               }
+               if((((data & 0x40) != 0) && ((cdrom_regs[0x0D] & 0x40) == 0)) ||
+                  (!(adpcm_play_in_progress) && ((data & 0x20 != 0)))) { // From Ootake v2.83
+                       // ADPCM play
+                       msm_start_addr = (adpcm_read_ptr) & 0xffff;
+                       msm_end_addr = (adpcm_read_ptr + adpcm_length) & 0xffff;
+                       msm_half_addr = (adpcm_read_ptr + (adpcm_length / 2)) & 0xffff;
+                       adpcm_write_ptr &= 0xffff;
+                       msm_nibble = 0;
+                       adpcm_play_in_progress = true;
+                       adpcm_play();
+                       d_msm->reset_w(0);
+                       out_debug_log(_T("ADPCM START PLAY START=%04x END=%04x HALF=%04x\n"), msm_start_addr, msm_end_addr, msm_half_addr);
+               } else if(((data & 0x40) == 0)) {
+                       if(((cdrom_regs[0x0D] & 0x40) != 0) && (adpcm_play_in_progress)) {
+                               // 20181213 K.O: Import from Ootake v2.83.Thanks to developers of Ootake.
+                               if(((data & 0x20) != 0) && ((adpcm_length & 0xffff) >= 0x8000) && ((adpcm_length & 0xffff) <= 0x80ff)) {
+                                       msm_half_addr = (adpcm_read_ptr + 0x85) & 0xffff;
+                               } else {
+                                       msm_half_addr = (adpcm_read_ptr + (adpcm_length >> 1)) & 0xffff;
+                               }
+                       }
+                       // used by Buster Bros to cancel an in-flight sample
+                       // if repeat flag (bit5) is high, ADPCM should be fully played (from Ootake)
+                       if(!(data & 0x20)) {
+                               set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, CLEAR_LINE);
+                               set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, CLEAR_LINE);
+                               adpcm_stop(false);
+                               d_msm->reset_w(1);
+                       }
+               }
+               break;
+               
+       case 0x0e:  /* ADPCM playback rate */
+               adpcm_clock_divider = 0x10 - (data & 0x0f);
+               d_msm->change_clock_w((ADPCM_CLOCK / 6) / adpcm_clock_divider);
+               break;
+               
+       case 0x0f:  /* ADPCM and CD audio fade timer */
+               if(cdrom_regs[0x0f] != data) {
+                       switch(data & 0x0f) {
+                       case 0x00: // CD-DA / ADPCM enable (100 msecs)
+                               cdda_fade_in(100);
+                               adpcm_fade_in(100);
+                               break;
+                       case 0x01: // CD-DA enable (100 msecs)
+                               cdda_fade_in(100);
+                               break;
+                       case 0x08: // CD-DA short (1500 msecs) fade out / ADPCM enable
+                       case 0x0c: // CD-DA short (1500 msecs) fade out / ADPCM enable
+                               cdda_fade_out(1500);
+                               adpcm_fade_in(100);
+                               break;
+                       case 0x09: // CD-DA long (5000 msecs) fade out
+                               cdda_fade_out(5000);
+                               break;
+                       case 0x0a: // ADPCM long (5000 msecs) fade out
+                               adpcm_fade_out(5000);
+                               break;
+                       case 0x0d: // CD-DA short (1500 msecs) fade out
+                               cdda_fade_out(1500);
+                               break;
+                       case 0x0e: // ADPCM short (1500 msecs) fade out
+                               adpcm_fade_out(1500);
+                               break;
+                       }
+               }
+               break;
+       }
+       cdrom_regs[addr & 0x0f] = data;
+}
+
+uint8_t PCE::cdrom_read(uint16_t addr)
+{
+       // System 3 Card header handling
+       if((addr & 0xc0) == 0xc0) {
+               switch(addr & 0xcf) {
+               case 0xc1: return 0xaa;
+               case 0xc2: return 0x55;
+               case 0xc3: return 0x00;
+               case 0xc5: return 0xaa;
+               case 0xc6: return 0x55;
+               case 0xc7: return 0x03;
+               }
+               return 0xff; // From Ootake v2.83.
+       }
+       uint8_t data = cdrom_regs[addr & 0x0f];
+       
+       switch(addr & 0x0f) {
+       case 0x00:  /* CDC status */
+               if((cdrom_regs[0x02] & 0x80) != 0) {
+                       // ToDo: Imprement read_1801() at Ootake v2.83.
+                       // ToDo: Implement _CheckCountAfterRead at CDROM.cpp of Ootake v2.83.
+                       if((d_scsi_host->read_signal(SIG_SCSI_CD) != 0) &&
+                          (d_scsi_host->read_signal(SIG_SCSI_MSG) == 0) &&
+                          (d_scsi_host->read_signal(SIG_SCSI_IO) != 0)) { // STATUS PHASE: Porting from Ootake v2.83.
+                               //      // busy = false;
+                               cdrom_regs[0x02] = cdrom_regs[0x02] & ~(0x80 | PCE_CD_IRQ_TRANSFER_READY | PCE_CD_IRQ_TRANSFER_DONE);
+                               set_cdrom_irq_line(PCE_CD_IRQ_TRANSFER_DONE, CLEAR_LINE);
+                       }
+                       return data & ~0x40; // Clear REQ
+               }                               
+               data = 0;
+               if(d_cpu->get_pc() == 0xf34b) {
+                       // XXX: Hack to wait the CD-DA will be finished for the Manhole
+                       data |= d_scsi_cdrom->read_signal(SIG_SCSI_CDROM_PLAYING) ? 0x80 : 0;
+               }
+               data |= d_scsi_host->read_signal(SIG_SCSI_BSY) ? 0x80 : 0;
+               data |= d_scsi_host->read_signal(SIG_SCSI_REQ) ? 0x40 : 0;
+               data |= d_scsi_host->read_signal(SIG_SCSI_MSG) ? 0x20 : 0;
+               data |= d_scsi_host->read_signal(SIG_SCSI_CD ) ? 0x10 : 0;
+               data |= d_scsi_host->read_signal(SIG_SCSI_IO ) ? 0x08 : 0;
+               cdrom_regs[0x00] = data;
+               if(false){
+                       // Import from Ootake v2.83 20181211 K.O
+                       if(((data & 0x10) != 0) &&
+                          ((data & 0x20) == 0) &&
+                          ((data & 0x08) == 0)){ // Command phase
+                               if((data & 0x80) != 0) { // Busy
+                                       //      data = (data & ~0x40) | 0x08; // (data & ~REQ) | IO
+                               } else {
+                                       //data = data & ~0x40; // (data & ~REQ)
+                               }
+                       } else if((data & 0x80) != 0) { // BUSY
+                               data = (data & ~0x40) | 0x10 | 0x80; // (data & ~REQ) | BUSY | CD
+                       } /*else if(_CheckCountAfterRead == 0) && (Status phase) && (cmd == read6)){
+                                data = data & ~0x40;
+                       } else  */ /* if(_bCDReqWait) {
+                               _bCDReqWait = false;
+                               if(data boundary) {
+                                        data = data & ~0x40;
+                               }
+                       }*/
+               }
+               break;
+               
+       case 0x01:  /* CDC command / status / data */
+       case 0x08:  /* ADPCM address (LSB) / CD data */
+               {
+                       bool read6_data_in = false;
+                       if(d_scsi_cdrom->get_cur_command() == SCSI_CMD_READ6 &&
+                          d_scsi_host->read_signal(SIG_SCSI_BSY) != 0 &&
+                          d_scsi_host->read_signal(SIG_SCSI_REQ) != 0 &&
+                          d_scsi_host->read_signal(SIG_SCSI_CD ) == 0 &&
+                          d_scsi_host->read_signal(SIG_SCSI_MSG) == 0 &&
+                          d_scsi_host->read_signal(SIG_SCSI_IO ) != 0) {
+                               // read6 command, data in phase
+                               read6_data_in = true;
+                       }
+                       data = read_cdrom_data();
+                       if(read6_data_in) {
+                               // set ack automatically and immediately for correct transfer speed
+                               set_ack();
+                               
+                               // XXX: Hack to wait until next REQ signal is raised
+                               // because PCE does not check REQ signal before reads next byte
+                               d_cpu->write_signal(SIG_CPU_BUSREQ, 1, 1);
+                       } else {
+                               if((addr & 0x0f) == 0x01) {
+                                       // ToDo: Imprement read_1801() at Ootake v2.83.
+                                       // ToDo: Implement _CheckCountAfterRead at CDROM.cpp of Ootake v2.83.
+                               }
+                       }
+               }
+               break;
+               
+       case 0x02:  /* ADPCM / CD control */
+               break;
+               
+       case 0x03:  /* BRAM lock / CD status */
+               // from Ootake
+               backup_locked = true;
+               data |= PCE_CD_IRQ_BRAM;
+               cdrom_regs[3] ^= 0x02;
+               if(cdrom_regs[2] == 0) {
+                       cdrom_regs[3] &= 0x02;
+               }
+               set_cdrom_irq_line(0, 0);
+               break;
+               
+       case 0x04:  /* CD reset */
+               break;
+               
+       case 0x05:  /* Convert PCM data / PCM data */
+               data = (d_scsi_cdrom->read_signal((cdrom_regs[3] & 0x02) ? SIG_SCSI_CDROM_SAMPLE_L : SIG_SCSI_CDROM_SAMPLE_R) >> 0) & 0xff;
+               break;
+               
+       case 0x06:  /* PCM data */
+               data = (d_scsi_cdrom->read_signal((cdrom_regs[3] & 0x02) ? SIG_SCSI_CDROM_SAMPLE_L : SIG_SCSI_CDROM_SAMPLE_R) >> 8) & 0xff;
+               break;
+               
+       case 0x07:  /* BRAM unlock / CD status */
+               data = (backup_locked ? (data & 0x7f) : (data | 0x80));
+               break;
+               
+       case 0x0a:  /* ADPCM RAM data port */
+               if(adpcm_read_buf > 0) {
+                       adpcm_read_buf--;
+                       data = 0x00;
+               } else {
+                       data = read_adpcm_ram();
+               }
+               break;
+               
+       case 0x0b:  /* ADPCM DMA control */
+               break;
+               
+       case 0x0c:  /* ADPCM status */
+               // Hack from Ootake v2.83.
+               if(adpcm_play_in_progress) {
+                       data = data & ~0x85;
+                       data = data | 0x08;
+               } else {
+                       data = data | 0x01;
+                       data = data & ~0x0c;
+               }
+               cdrom_regs[0x0c] = data;
+               // ToDo: HuVideo
+               break;
+       case 0x09:  /* ADPCM address (MSB) */
+       case 0x0d:  /* ADPCM address control */
+       case 0x0e:  /* ADPCM playback rate */
+       case 0x0f:  /* ADPCM and CD audio fade timer */
+               data = 0;
+               break;
+       }
+       return data;
+}
+
+void PCE::write_cdrom_data(uint8_t data)
+{
+       d_scsi_host->write_dma_io8(0, data);
+}
+
+uint8_t PCE::read_cdrom_data()
+{
+       
+       return d_scsi_host->read_dma_io8(0);
+}
+
+void PCE::reset_adpcm()
+{
+       touch_sound();
+       // reset ADPCM hardware
+       adpcm_read_ptr = adpcm_write_ptr = 0;
+       msm_start_addr = msm_end_addr = msm_half_addr = 0;
+       msm_nibble = 0;
+       adpcm_stop(false);
+       d_msm->reset_w(1);
+       out_debug_log(_T("RESET ADPCM\n"));
+       
+       // stop ADPCM dma
+       adpcm_dma_enabled = false;
+}
+
+void PCE::write_adpcm_ram(uint8_t data)
+{
+       adpcm_ram[(adpcm_write_ptr++) & 0xffff] = data;
+//     adpcm_write_ptr = adpcm_write_ptr & 0xffff;
+}
+
+uint8_t PCE::read_adpcm_ram()
+{
+       uint8_t _data = adpcm_ram[(adpcm_read_ptr++) & 0xffff];
+//     adpcm_read_ptr = adpcm_read_ptr & 0xffff;
+       return _data;
+}
+
+void PCE::adpcm_do_dma()
+{
+       write_adpcm_ram(read_cdrom_data());
+       adpcm_written++;
+       set_ack();
+       cdrom_regs[0x0c] &= ~0x04;
+}
+
+void PCE::adpcm_play()
+{
+       touch_sound();
+       cdrom_regs[0x0c] &= ~PCE_CD_ADPCM_STOP_FLAG;
+       cdrom_regs[0x0c] |= PCE_CD_ADPCM_PLAY_FLAG;
+       set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, CLEAR_LINE);
+       //set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, CLEAR_LINE);
+       cdrom_regs[0x03] &= ~0x0c;
+       msm_idle = 0;
+}
+
+void PCE::adpcm_stop(bool do_irq)
+{
+       touch_sound();
+       cdrom_regs[0x0c] |= PCE_CD_ADPCM_STOP_FLAG;
+       cdrom_regs[0x0c] &= ~PCE_CD_ADPCM_PLAY_FLAG;
+       if(do_irq) {
+               set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, ASSERT_LINE);
+       }
+       cdrom_regs[0x0d] &= ~0x60;
+       msm_idle = 1;
+       adpcm_play_in_progress = false;
+//     adpcm_dma_enabled = false;
+       out_debug_log(_T("ADPCM STOP PLAY PTR=%04x IRQ=%s\n"), msm_start_addr, (do_irq) ? _T("YES") : _T("NO"));
+}
+
+void PCE::set_ack()
+{
+       cdrom_regs[0x03] |= 0x40; // From Ootake v2.38
+       d_scsi_host->write_signal(SIG_SCSI_ACK, 1, 1);
+}
+
+void PCE::clear_ack()
+{
+       cdrom_regs[0x03] &= ~0x40; // From Ootake v2.38
+       if(d_scsi_host->read_signal(SIG_SCSI_CD) != 0) {
+               cdrom_regs[0x0b] &= 0xfc;
+       }
+       d_scsi_host->write_signal(SIG_SCSI_ACK, 0, 0);
+}
+
+void PCE::set_cdrom_irq_line(int num, int state)
+{
+       if (state == ASSERT_LINE) {
+               cdrom_regs[0x03] |= num;
+       } else {
+               cdrom_regs[0x03] &= ~num;
+       }
+       if (cdrom_regs[0x02] & cdrom_regs[0x03] & 0x7c) {
+               d_cpu->write_signal(INPUT_LINE_IRQ2, ASSERT_LINE, 0);
+       } else {
+               d_cpu->write_signal(INPUT_LINE_IRQ2, CLEAR_LINE, 0);
+       }
+}
+
+void PCE::cdda_fade_in(int time)
+{
+       if(event_cdda_fader != -1) {
+               cancel_event(this, event_cdda_fader);
+       }
+       register_event(this, EVENT_CDDA_FADE_IN, time, true, &event_cdda_fader);
+       d_scsi_cdrom->set_volume((int)(cdda_volume = 0.0));
+}
+
+void PCE::cdda_fade_out(int time)
+{
+       if(event_cdda_fader != -1) {
+               cancel_event(this, event_cdda_fader);
+       }
+       register_event(this, EVENT_CDDA_FADE_OUT, time, true, &event_cdda_fader);
+       d_scsi_cdrom->set_volume((int)(cdda_volume = 100.0));
+}
+
+void PCE::adpcm_fade_in(int time)
+{
+       if(event_adpcm_fader != -1) {
+               cancel_event(this, event_adpcm_fader);
+       }
+       register_event(this, EVENT_ADPCM_FADE_IN, time, true, &event_adpcm_fader);
+       d_msm->set_volume((int)(adpcm_volume = 0.0));
+}
+
+void PCE::adpcm_fade_out(int time)
+{
+       if(event_adpcm_fader != -1) {
+               cancel_event(this, event_adpcm_fader);
+       }
+       register_event(this, EVENT_ADPCM_FADE_OUT, time, true, &event_adpcm_fader);
+       d_msm->set_volume((int)(adpcm_volume = 100.0));
+}
+
+void PCE::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       switch(id) {
+       case SIG_PCE_SCSI_IRQ:
+               if(data & mask) {
+                       if(!irq_status) {
+                               irq_status = true;
+                               
+                               if(d_scsi_host->read_signal(SIG_SCSI_BSY) != 0 &&
+                                  d_scsi_host->read_signal(SIG_SCSI_CD ) != 0 &&
+                                  d_scsi_host->read_signal(SIG_SCSI_MSG) == 0 &&
+                                  d_scsi_host->read_signal(SIG_SCSI_IO ) != 0) {
+                                       // status phase, command is finished
+                                       set_cdrom_irq_line(PCE_CD_IRQ_TRANSFER_READY, CLEAR_LINE);
+                                       set_cdrom_irq_line(PCE_CD_IRQ_TRANSFER_DONE, ASSERT_LINE);
+                               }
+                               // clear busreq because next REQ signal is raised
+                               d_cpu->write_signal(SIG_CPU_BUSREQ, 0, 1);
+                       }
+               } else {
+                       if(irq_status) {
+                               irq_status = false;
+                       }
+               }
+               break;
+               
+       case SIG_PCE_SCSI_DRQ:
+               if(data & mask) {
+                       if(!drq_status) {
+                               drq_status = true;
+                               set_cdrom_irq_line(PCE_CD_IRQ_TRANSFER_READY, ASSERT_LINE);
+                               
+                               // clear busreq because next REQ signal is raised
+                               d_cpu->write_signal(SIG_CPU_BUSREQ, 0, 1);
+                               
+                               if(adpcm_dma_enabled) {
+                                       if(!(msm_idle) && ((adpcm_write_ptr & 0xffff) >= (msm_start_addr & 0xffff))) {
+                                               // now streaming, wait dma not to overwrite buffer before it is played
+                                               cdrom_regs[0x0b] = 0x00; // From Ootake v2.38.
+                                               //adpcm_do_dma();
+                                       } else {
+                                               adpcm_do_dma();
+                                       }
+                               }
+                       }
+               } else {
+                       if(drq_status) {
+                               drq_status = false;
+                               
+                               if(d_scsi_cdrom->get_cur_command() == SCSI_CMD_READ6 &&
+                                  d_scsi_host->read_signal(SIG_SCSI_BSY) != 0 &&
+                                  d_scsi_host->read_signal(SIG_SCSI_CD ) == 0 &&
+                                  d_scsi_host->read_signal(SIG_SCSI_MSG) == 0 &&
+                                  d_scsi_host->read_signal(SIG_SCSI_IO ) != 0) {
+                                       // clear ack automatically and immediately for correct transfer speed
+                                       clear_ack();
+                               }
+                       }
+               }
+               break;
+               
+       case SIG_PCE_SCSI_BSY:
+               if(!(data & mask)) {
+                       // bus free
+                       set_cdrom_irq_line(PCE_CD_IRQ_TRANSFER_READY, CLEAR_LINE);
+                       set_cdrom_irq_line(PCE_CD_IRQ_TRANSFER_DONE, CLEAR_LINE);
+                       if(!(adpcm_play_in_progress) && (adpcm_dma_enabled)){
+                               d_msm->reset_w(1);
+                               adpcm_dma_enabled = false;
+                               out_debug_log(_T("SIG_PCE_SCSI_BSY: RESET ADPCM\n"));
+                       }
+               }
+               break;
+               
+       case SIG_PCE_CDDA_DONE:
+               touch_sound();
+               if(data & mask) {
+                       set_cdrom_irq_line(PCE_CD_IRQ_TRANSFER_DONE, ASSERT_LINE);
+               }
+               break;
+               
+       case SIG_PCE_ADPCM_VCLK:
+               // Callback for new data from the MSM5205.
+               // The PCE cd unit actually divides the clock signal supplied to
+               // the MSM5205. Currently we can only use static clocks for the
+               // MSM5205.
+               if((data & mask) == 0) {
+               if(!msm_idle) {
+                       uint8_t msm_data = (msm_nibble) ? (adpcm_ram[msm_start_addr & 0xffff] & 0x0f) : ((adpcm_ram[msm_start_addr & 0xffff] & 0xf0) >> 4);
+                       d_msm->data_w(msm_data);
+                       msm_nibble ^= 1;
+                       
+                       if(msm_nibble == 0) {
+                               adpcm_written--;
+                               // 20181213 K.O: Re-order sequence from Ootake v2.83.Thanks to developers of Ootake.
+                               if((msm_start_addr & 0xffff) == msm_end_addr) {
+                                       // reached to end address
+                                       if(adpcm_dma_enabled) {
+                                               // restart streaming
+                                               set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, CLEAR_LINE);
+                                               set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, CLEAR_LINE);
+                                               //set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, ASSERT_LINE);
+                                               out_debug_log(_T("END ADDRESS(DMA) READ_PTR=%04x WRITE_PTR=%04x\n"), adpcm_read_ptr, adpcm_write_ptr);
+                                       } else {
+                                               // stop playing adpcm
+                                               set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, CLEAR_LINE);
+                                               set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, ASSERT_LINE);
+                                               adpcm_stop(true);
+                                               d_msm->reset_w(1);
+                                               out_debug_log(_T("END ADDRESS(NON-DMA) READ_PTR=%04x WRITE_PTR=%04x\n"), adpcm_read_ptr, adpcm_write_ptr);
+                                       }
+                               } else if(adpcm_dma_enabled && adpcm_written == 0) {
+                                       // finish streaming when all samples are played
+                                       set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, CLEAR_LINE);
+                                       set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, ASSERT_LINE);
+                                       adpcm_stop(true);
+                                       d_msm->reset_w(1);
+                               } else if((msm_start_addr & 0xffff) == msm_half_addr) {
+                                       // reached to half address
+                                       // 20181213 K.O: Porting from Ootake v2.83.Thanks to developers of Ootake.
+                                       if((adpcm_dma_enabled) && (adpcm_length >= 0x8000) && (adpcm_length <= 0x80ff)) {
+                                               msm_half_addr = (msm_half_addr + 0x85) & 0xffff;
+                                       } else if(adpcm_length < 0x7fff) {
+                                               msm_half_addr = (msm_half_addr + (uint16_t)(adpcm_length - 1024)) & 0xffff;
+                                       }
+                                       set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, CLEAR_LINE);
+                                       set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, ASSERT_LINE);
+                                       out_debug_log(_T("HALF ADDRESS READ_PTR=%04x WRITE_PTR=%04x\n"), adpcm_read_ptr, adpcm_write_ptr);
+                               } else if((!((adpcm_dma_enabled) && (adpcm_length >= 0x8000) && (adpcm_length <= 0x80ff)) &&
+                                                  !(adpcm_length < 0x7fff)) &&
+                                                 (((msm_start_addr & 0xffff) == 0x8000) || ((msm_start_addr & 0xffff) == 0x0000))) {
+                                       // 20181213 K.O: Porting from Ootake v2.83.Thanks to developers of Ootake.
+                                       set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_FULL_PLAY, CLEAR_LINE);
+                                       set_cdrom_irq_line(PCE_CD_IRQ_SAMPLE_HALF_PLAY, ASSERT_LINE);
+                                       out_debug_log(_T("HALF ADDRESS READ_PTR=%04x WRITE_PTR=%04x\n"), adpcm_read_ptr, adpcm_write_ptr);
+                               } 
+                               
+                               msm_start_addr++;
+                               //msm_start_addr = msm_start_addr & 0xffff;
+                               if(adpcm_dma_enabled) {
+                                       if(!(msm_idle) && (adpcm_write_ptr < msm_start_addr)) {
+                                               if(d_scsi_cdrom->get_cur_command() == SCSI_CMD_READ6 &&
+                                                  d_scsi_host->read_signal(SIG_SCSI_BSY) != 0 &&
+                                                  d_scsi_host->read_signal(SIG_SCSI_REQ) != 0 &&
+                                                  d_scsi_host->read_signal(SIG_SCSI_CD ) == 0 &&
+                                                  d_scsi_host->read_signal(SIG_SCSI_MSG) == 0 &&
+                                                  d_scsi_host->read_signal(SIG_SCSI_IO ) != 0) {
+                                                       // already data is received, read next byte
+                                                       adpcm_do_dma();
+                                               }
+                                       }
+                               } else {
+                               }
+                       }
+               }
+               }
+               break;
+       }
+}
+
+void PCE::event_callback(int event_id, int err)
+{
+       switch(event_id) {
+       case EVENT_CDDA_FADE_IN:
+               if((cdda_volume += 0.1) >= 100.0) {
+                       cancel_event(this, event_cdda_fader);
+                       event_cdda_fader = -1;
+                       cdda_volume = 100.0;
+               }
+               d_scsi_cdrom->set_volume((int)cdda_volume);
+               break;
+               
+       case EVENT_CDDA_FADE_OUT:
+               if((cdda_volume -= 0.1) <= 0) {
+                       cancel_event(this, event_cdda_fader);
+                       event_cdda_fader = -1;
+                       cdda_volume = 0.0;
+               }
+               d_scsi_cdrom->set_volume((int)cdda_volume);
+               break;
+               
+       case EVENT_ADPCM_FADE_IN:
+               if((adpcm_volume += 0.1) >= 100.0) {
+                       cancel_event(this, event_adpcm_fader);
+                       event_adpcm_fader = -1;
+                       adpcm_volume = 100.0;
+               }
+               d_msm->set_volume((int)adpcm_volume);
+               break;
+               
+       case EVENT_ADPCM_FADE_OUT:
+               if((adpcm_volume -= 0.1) <= 0) {
+                       cancel_event(this, event_adpcm_fader);
+                       event_adpcm_fader = -1;
+                       adpcm_volume = 0.0;
+               }
+               d_msm->set_volume((int)adpcm_volume);
+               break;
+       }
+}
 #endif
-       state_fio->FputUint32(bank);
-       state_fio->FputUint8(buffer);
-       state_fio->FputInt32(prev_width);
-       state_fio->FputBool(inserted);
-       state_fio->Fwrite(vdc, sizeof(vdc), 1);
-       state_fio->Fwrite(&vce, sizeof(vce), 1);
-       state_fio->Fwrite(&vpc, sizeof(vpc), 1);
-       state_fio->Fwrite(psg, sizeof(psg), 1);
-       state_fio->FputUint8(psg_ch);
-       state_fio->FputUint8(psg_vol);
-       state_fio->FputUint8(psg_lfo_freq);
-       state_fio->FputUint8(psg_lfo_ctrl);
-       state_fio->FputUint8(joy_sel);
-       state_fio->FputUint8(joy_clr);
-       state_fio->FputUint8(joy_count);
-       state_fio->FputUint8(joy_bank);
-       state_fio->FputBool(joy_6btn);
-}
-
-bool PCE::load_state(FILEIO* state_fio)
-{
-       if(state_fio->FgetUint32() != STATE_VERSION) {
-               return false;
-       }
-       if(state_fio->FgetInt32() != this_device_id) {
-               return false;
-       }
-       support_6btn = state_fio->FgetBool();
-       support_sgfx = state_fio->FgetBool();
-       state_fio->Fread(ram, sizeof(ram), 1);
-       state_fio->Fread(cart + 0x80000, 0x80000, 1);
-#ifdef SUPPORT_BACKUP_RAM
-       state_fio->Fread(backup, sizeof(backup), 1);
-       backup_crc32 = state_fio->FgetUint32();
+
+#define STATE_VERSION  6
+
+void process_state_vdc(vdc_t* val, FILEIO* state_fio)
+{
+       state_fio->StateValue(val->dvssr_write);
+       state_fio->StateValue(val->physical_width);
+       state_fio->StateValue(val->physical_height);
+       state_fio->StateArray(val->sprite_ram, sizeof(val->sprite_ram), 1);
+       state_fio->StateValue(val->curline);
+       state_fio->StateValue(val->current_segment);
+       state_fio->StateValue(val->current_segment_line);
+       state_fio->StateValue(val->vblank_triggered);
+       state_fio->StateValue(val->raster_count);
+       state_fio->StateValue(val->satb_countdown);
+       state_fio->StateArray(val->vram, sizeof(val->vram), 1);
+       state_fio->StateValue(val->inc);
+       state_fio->StateValue(val->vdc_register);
+       state_fio->StateValue(val->vdc_latch);
+       state_fio->StateArray(val->vdc_data, sizeof(val->vdc_data), 1);
+       state_fio->StateValue(val->status);
+       state_fio->StateValue(val->y_scroll);
+}
+
+void process_state_vce(vce_t* val, FILEIO* state_fio)
+{
+       state_fio->StateValue(val->vce_control);
+       state_fio->StateValue(val->vce_address);
+       state_fio->StateArray(val->vce_data, sizeof(val->vce_data), 1);
+       state_fio->StateValue(val->current_bitmap_line);
+       state_fio->StateArrayScrnType_t(&val->bmp[0][0], sizeof(val->bmp), 1);
+       state_fio->StateArrayScrnType_t(val->palette, sizeof(val->palette), 1);
+}
+
+void process_state_vpc(vpc_t* val, FILEIO* state_fio)
+{
+       for(int i = 0; i < array_length(val->vpc_prio); i++) {
+               state_fio->StateValue(val->vpc_prio[i].prio);
+               state_fio->StateValue(val->vpc_prio[i].vdc0_enabled);
+               state_fio->StateValue(val->vpc_prio[i].vdc1_enabled);
+       }
+       state_fio->StateArray(val->prio_map, sizeof(val->prio_map), 1);
+       state_fio->StateValue(val->priority);
+       state_fio->StateValue(val->window1);
+       state_fio->StateValue(val->window2);
+       state_fio->StateValue(val->vdc_select);
+}
+
+void process_state_psg(psg_t* val, FILEIO* state_fio)
+{
+       state_fio->StateArray(val->regs, sizeof(val->regs), 1);
+       state_fio->StateArray(val->wav, sizeof(val->wav), 1);
+       state_fio->StateValue(val->wavptr);
+       state_fio->StateValue(val->genptr);
+       state_fio->StateValue(val->remain);
+       state_fio->StateValue(val->noise);
+       state_fio->StateValue(val->randval);
+}
+
+bool PCE::process_state(FILEIO* state_fio, bool loading)
+{
+       if(!state_fio->StateCheckUint32(STATE_VERSION)) {
+               return false;
+       }
+       if(!state_fio->StateCheckInt32(this_device_id)) {
+               return false;
+       }
+       state_fio->StateValue(support_6btn_pad);
+       state_fio->StateValue(support_multi_tap);
+ #ifdef SUPPORT_SUPER_GFX
+       state_fio->StateValue(support_sgfx);
+ #endif
+ #ifdef SUPPORT_CDROM
+       state_fio->StateValue(support_cdrom);
+ #endif
+       state_fio->StateArray(ram, sizeof(ram), 1);
+       state_fio->StateArray(cart + 0x80000, 0x80000, 1);
+ #ifdef SUPPORT_BACKUP_RAM
+       state_fio->StateArray(backup, sizeof(backup), 1);
+       state_fio->StateValue(backup_crc32);
+ #endif
+       state_fio->StateValue(bank);
+       state_fio->StateValue(buffer);
+       state_fio->StateValue(prev_width);
+       state_fio->StateValue(inserted);
+       for(int i = 0; i < array_length(vdc); i++) {
+               process_state_vdc(&vdc[i], state_fio);
+       }
+       process_state_vce(&vce, state_fio);
+       process_state_vpc(&vpc, state_fio);
+       for(int i = 0; i < array_length(psg); i++) {
+               process_state_psg(&psg[i], state_fio);
+       }
+       state_fio->StateValue(psg_ch);
+       state_fio->StateValue(psg_vol);
+       state_fio->StateValue(psg_lfo_freq);
+       state_fio->StateValue(psg_lfo_ctrl);
+       state_fio->StateValue(joy_counter);
+       state_fio->StateValue(joy_high_nibble);
+       state_fio->StateValue(joy_second_byte);
+#ifdef SUPPORT_CDROM
+       state_fio->StateArray(cdrom_ram, sizeof(cdrom_ram), 1);
+       state_fio->StateArray(cdrom_regs, sizeof(cdrom_regs), 1);
+       state_fio->StateValue(backup_locked);
+       state_fio->StateValue(irq_status);
+       state_fio->StateValue(drq_status);
+       state_fio->StateArray(adpcm_ram, sizeof(adpcm_ram), 1);
+       state_fio->StateValue(adpcm_read_ptr);
+       state_fio->StateValue(adpcm_write_ptr);
+       state_fio->StateValue(adpcm_written);
+       state_fio->StateValue(adpcm_length);
+       state_fio->StateValue(adpcm_clock_divider);
+       state_fio->StateValue(adpcm_read_buf);
+       state_fio->StateValue(adpcm_write_buf);
+       state_fio->StateValue(adpcm_dma_enabled);
+       state_fio->StateValue(msm_start_addr);
+       state_fio->StateValue(msm_end_addr);
+       state_fio->StateValue(msm_half_addr);
+       state_fio->StateValue(msm_nibble);
+       state_fio->StateValue(msm_idle);
+       state_fio->StateValue(cdda_volume);
+       state_fio->StateValue(adpcm_volume);
+       state_fio->StateValue(event_cdda_fader);
+       state_fio->StateValue(event_adpcm_fader);
+       state_fio->StateValue(adpcm_play_in_progress);
 #endif
-       bank = state_fio->FgetUint32();
-       buffer = state_fio->FgetUint8();
-       prev_width = state_fio->FgetInt32();
-       inserted = state_fio->FgetBool();
-       state_fio->Fread(vdc, sizeof(vdc), 1);
-       state_fio->Fread(&vce, sizeof(vce), 1);
-       state_fio->Fread(&vpc, sizeof(vpc), 1);
-       state_fio->Fread(psg, sizeof(psg), 1);
-       psg_ch = state_fio->FgetUint8();
-       psg_vol = state_fio->FgetUint8();
-       psg_lfo_freq = state_fio->FgetUint8();
-       psg_lfo_ctrl = state_fio->FgetUint8();
-       joy_sel = state_fio->FgetUint8();
-       joy_clr = state_fio->FgetUint8();
-       joy_count = state_fio->FgetUint8();
-       joy_bank = state_fio->FgetUint8();
-       joy_6btn = state_fio->FgetBool();
-       return true;
+       return true;
 }
 
+}