OSDN Git Service

[VM][Towns] Initial start.Still not working.
authorK.Ohta <whatisthis.sowhat@gmail.com>
Tue, 27 Dec 2016 19:24:12 +0000 (04:24 +0900)
committerK.Ohta <whatisthis.sowhat@gmail.com>
Tue, 27 Dec 2016 19:24:12 +0000 (04:24 +0900)
20 files changed:
source/src/vm/fmtowns/CMakeLists.txt [new file with mode: 0644]
source/src/vm/fmtowns/bios.cpp [new file with mode: 0644]
source/src/vm/fmtowns/bios.h [new file with mode: 0644]
source/src/vm/fmtowns/cmos.cpp [new file with mode: 0644]
source/src/vm/fmtowns/cmos.h [new file with mode: 0644]
source/src/vm/fmtowns/floppy.cpp [new file with mode: 0644]
source/src/vm/fmtowns/floppy.h [new file with mode: 0644]
source/src/vm/fmtowns/fmtowns.cpp [new file with mode: 0644]
source/src/vm/fmtowns/fmtowns.h [new file with mode: 0644]
source/src/vm/fmtowns/keyboard.cpp [new file with mode: 0644]
source/src/vm/fmtowns/keyboard.h [new file with mode: 0644]
source/src/vm/fmtowns/memory.cpp [new file with mode: 0644]
source/src/vm/fmtowns/memory.h [new file with mode: 0644]
source/src/vm/fmtowns/scsi.cpp [new file with mode: 0644]
source/src/vm/fmtowns/scsi.h [new file with mode: 0644]
source/src/vm/fmtowns/timer.cpp [new file with mode: 0644]
source/src/vm/fmtowns/timer.h [new file with mode: 0644]
source/src/vm/fmtowns/towns_crtc.cpp [new file with mode: 0644]
source/src/vm/fmtowns/towns_crtc.h [new file with mode: 0644]
source/src/vm/fmtowns/towns_vram.h [new file with mode: 0644]

diff --git a/source/src/vm/fmtowns/CMakeLists.txt b/source/src/vm/fmtowns/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f2f39c7
--- /dev/null
@@ -0,0 +1,14 @@
+cmake_minimum_required (VERSION 2.6)
+
+message("* vm/fm-towns")
+
+add_library(vm_fmtowns
+       bios.cpp
+       cmos.cpp
+       floppy.cpp
+       keyboard.cpp
+       memory.cpp
+       scsi.cpp
+       timer.cpp
+       fmr50.cpp
+)
\ No newline at end of file
diff --git a/source/src/vm/fmtowns/bios.cpp b/source/src/vm/fmtowns/bios.cpp
new file mode 100644 (file)
index 0000000..f56c9f7
--- /dev/null
@@ -0,0 +1,1133 @@
+/*
+       FUJITSU FMR-30 Emulator 'eFMR-30'
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.10.06 -
+
+       [ bios ]
+*/
+
+#include "bios.h"
+#include "../disk.h"
+
+// regs
+#define AX     regs[0]
+#define CX     regs[1]
+#define DX     regs[2]
+#define BX     regs[3]
+#define SP     regs[4]
+#define BP     regs[5]
+#define SI     regs[6]
+#define DI     regs[7]
+
+#define AL     regs8[0]
+#define AH     regs8[1]
+#define CL     regs8[2]
+#define CH     regs8[3]
+#define DL     regs8[4]
+#define DH     regs8[5]
+#define BL     regs8[6]
+#define BH     regs8[7]
+#define SPL    regs8[8]
+#define SPH    regs8[9]
+#define BPL    regs8[10]
+#define BPH    regs8[11]
+#define SIL    regs8[12]
+#define SIH    regs8[13]
+#define DIL    regs8[14]
+#define DIH    regs8[15]
+
+// sregs
+#define ES     sregs[0]
+#define CS     sregs[1]
+#define SS     sregs[2]
+#define DS     sregs[3]
+
+// error
+#define ERR_FDD_NOTREADY       1
+#define ERR_FDD_PROTECTED      2
+#define ERR_FDD_DELETED                4
+#define ERR_FDD_NOTFOUND       8
+#define ERR_FDD_CRCERROR       0x10
+#define ERR_SCSI_NOTREADY      1
+#define ERR_SCSI_PARAMERROR    2
+#define ERR_SCSI_NOTCONNECTED  4
+
+#if defined(_FMR30)
+// FMR-30
+#define CMOS_SIZE      0x2000
+#define VRAM_SIZE      0x20000
+#define IPL_SIZE       0x10000
+#define IPL_ID         '2'
+#elif defined(_FMR50)
+// FMR-50
+#define CMOS_SIZE      0x800
+#define VRAM_SIZE      0x40000
+#define IPL_SIZE       0x4000
+#define IPL_ID         '1'
+#elif defined(_FMR60)
+// FMR-60
+#define CMOS_SIZE      0x800
+#define VRAM_SIZE      0x80000
+#define IPL_SIZE       0x4000
+#define IPL_ID         '1'
+#endif
+
+#define BLOCK_SIZE     512
+
+static const int iotable[][2] = {
+#ifdef _FMR30
+       {0x0100, 0x19}, // pic
+       {0x0101, 0x40},
+       {0x0101, 0x80},
+       {0x0101, 0x01},
+       {0x0101, 0xff},
+       {0x0108, 0x19},
+       {0x010a, 0x48},
+       {0x010a, 0x07},
+       {0x010a, 0x01},
+       {0x010a, 0xff},
+       {0x0042, 0x00}, // timer
+       {0x0133, 0x30},
+       {0x0130, 0xa0},
+       {0x0130, 0x86},
+       {0x000b, 0x02}, // sio
+       {0x0009, 0x00},
+       {0x0009, 0x50},
+       {0x0009, 0x7f},
+       {0x0009, 0x15},
+       {0x0013, 0x02},
+       {0x001d, 0x02}, // memory
+       {0x001e, 0x00},
+       {0x0040, 0x9f}, // psg
+       {0x0040, 0xbf},
+       {0x0040, 0xdf},
+       {0x0040, 0xff},
+       {0x0300, 0x01}, // lcdc
+       {0x0302, 0x50},
+       {0x0300, 0x09},
+       {0x0302, 0x0f},
+       {0x0300, 0x0a},
+       {0x0302, 0x20},
+       {0x0300, 0x0b},
+       {0x0302, 0x0d},
+       {0x0300, 0x0c},
+       {0x0302, 0x00},
+       {0x0300, 0x0d},
+       {0x0302, 0x00},
+       {0x0300, 0x0e},
+       {0x0302, 0x00},
+       {0x0300, 0x0f},
+       {0x0302, 0x00},
+       {0x0300, 0x11},
+       {0x0302, 0xc7},
+       {0x0300, 0x1d},
+       {0x0302, 0x00},
+       {0x0308, 0x63},
+       {0x0309, 0x00},
+       {0x030a, 0x00},
+#else
+       {0x0060, 0x00}, // timer
+       {0x0604, 0x00}, // keyboard
+       {0x0000, 0x19}, // pic
+       {0x0002, 0x40},
+       {0x0002, 0x80},
+       {0x0002, 0x0d},
+       {0x0002, 0xfe},
+       {0x0010, 0x19},
+       {0x0012, 0x48},
+       {0x0012, 0x87},
+       {0x0012, 0x09},
+       {0x0012, 0xff},
+       {0x0000, 0x20},
+       {0x0046, 0x36}, // pit
+       {0x0040, 0x00},
+       {0x0040, 0x78},
+       {0x0404, 0x00}, // memory
+       {0x0500, 0x00}, // crtc
+       {0x0502, 0x35},
+       {0x0500, 0x01},
+       {0x0502, 0x28},
+       {0x0500, 0x02},
+       {0x0502, 0x2c},
+       {0x0500, 0x03},
+       {0x0502, 0x04},
+       {0x0500, 0x04},
+       {0x0502, 0x1a},
+       {0x0500, 0x05},
+       {0x0502, 0x08},
+       {0x0500, 0x06},
+       {0x0502, 0x19},
+       {0x0500, 0x07},
+       {0x0502, 0x19},
+       {0x0500, 0x08},
+       {0x0502, 0x00},
+       {0x0500, 0x09},
+       {0x0502, 0x0f},
+       {0x0500, 0x0a},
+       {0x0502, 0x20},
+       {0x0500, 0x0b},
+       {0x0502, 0x1e},
+       {0x0500, 0x0c},
+       {0x0502, 0x00},
+       {0x0500, 0x0d},
+       {0x0502, 0x00},
+       {0x0500, 0x0e},
+       {0x0502, 0x00},
+       {0x0500, 0x0f},
+       {0x0502, 0x00},
+       {0x0500, 0x10},
+       {0x0502, 0x00},
+       {0x0500, 0x11},
+       {0x0502, 0x00},
+       {0x0500, 0x1e},
+       {0x0502, 0x00},
+       {0x0500, 0x1f},
+       {0x0502, 0x00},
+       {0xfd98, 0x00}, // palette
+       {0xfd99, 0x01},
+       {0xfd9a, 0x02},
+       {0xfd9b, 0x03},
+       {0xfd9c, 0x04},
+       {0xfd9d, 0x05},
+       {0xfd9e, 0x06},
+       {0xfd9f, 0x07},
+       {0xfda0, 0x0f}, // video
+#endif
+       {-1, -1}
+};
+
+// cmos: $000-
+static const uint8_t cmos_t[] = {
+#ifdef _FMR30
+       0x01,0xff,0x42,0x4f,0x4f,0x54,0xa8,0x00,0x40,0x00,0x01,0xfe,0x53,0x45,0x54,0x55,
+       0xe8,0x00,0x00,0x01,0x01,0xfd,0x4c,0x4f,0x47,0x20,0xe8,0x01,0x10,0x03,0x01,0xfc,
+       0x4f,0x41,0x53,0x59,0xf8,0x04,0x20,0x00,0x01,0xfb,0x44,0x45,0x42,0x20,0x18,0x05,
+       0x00,0x01,0x01,0xfa,0x44,0x45,0x53,0x4b,0x18,0x06,0x32,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x4a,0x06,0x7b,0x19,0x97,0x62,0x79,0x41
+#else
+       0x01,0xff,0x42,0x4f,0x4f,0x54,0xa8,0x00,0x40,0x00,0x01,0xfe,0x53,0x45,0x54,0x55,
+       0xe8,0x00,0x00,0x01,0x01,0xfd,0x4c,0x4f,0x47,0x20,0xe8,0x01,0x10,0x03,0x01,0xfc,
+       0x4f,0x41,0x53,0x59,0xf8,0x04,0x20,0x00,0x01,0xfb,0x58,0x45,0x4e,0x49,0x18,0x05,
+       0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+//     0x28,0x05,0x99,0x02,0xe1,0xe1,0x79,0x41
+       0x28,0x05,0x99,0x02,0x00,0x00,0x79,0x41
+#endif
+};
+// FMR-30: cmos $1fd0-
+// FMR-50: cmos $7d0-
+static const uint8_t cmos_b[] = {
+#ifdef _FMR30
+       0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0xff,0xff,0xff,0xff,0xff,
+       0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x7f,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
+#else
+       0x00,0x00,0x01,0x02,0x03,0x04,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+       0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+#endif
+};
+
+// boot message
+static const uint8_t msg_c[] = {
+       0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,
+       0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,
+       0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,
+       0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07,0xff,0x47,0xff,0x07
+};
+
+// '\83V\83X\83e\83\80\82ð\83Z\83b\83g\82µ\82Ä\82­\82¾\82³\82¢'
+static const uint8_t msg_k[] = {
+       0x25,0x37,0x00,0x00,0x25,0x39,0x00,0x00,0x25,0x46,0x00,0x00,0x25,0x60,0x00,0x00,
+       0x24,0x72,0x00,0x00,0x25,0x3b,0x00,0x00,0x25,0x43,0x00,0x00,0x25,0x48,0x00,0x00,
+       0x24,0x37,0x00,0x00,0x24,0x46,0x00,0x00,0x24,0x2f,0x00,0x00,0x24,0x40,0x00,0x00,
+       0x24,0x35,0x00,0x00,0x24,0x24,0x00,0x00,0x21,0x21,0x00,0x00
+};
+
+void BIOS::initialize()
+{
+       // to increment timeout counter
+       register_frame_event(this);
+       
+       // check scsi drives
+       FILEIO* fio = new FILEIO();
+       for(int i = 0; i < MAX_SCSI; i++) {
+               if(fio->Fopen(create_local_path(_T("SCSI%d.DAT"), i), FILEIO_READ_WRITE_BINARY)) {
+                       uint32_t file_size = fio->FileLength();
+                       if(file_size == 0) {
+                               // from ../scsi_hdd.cpp
+                               #define SCSI_BUFFER_SIZE        0x10000
+                               uint32_t remain = (file_size = 0x2800000); // 40MB
+                               void *tmp = calloc(1, SCSI_BUFFER_SIZE);
+                               while(remain > 0) {
+                                       uint32_t length = min(remain, (uint32_t)SCSI_BUFFER_SIZE);
+                                       fio->Fwrite(tmp, length, 1);
+                                       remain -= length;
+                               }
+                               free(tmp);
+                               #undef SCSI_BUFFER_SIZE
+                       }
+                       scsi_blocks[i] = file_size / BLOCK_SIZE;
+                       fio->Fclose();
+               } else {
+                       scsi_blocks[i] = 0;
+               }
+       }
+       delete fio;
+}
+
+void BIOS::reset()
+{
+       for(int i = 0; i < MAX_DRIVE; i++) {
+               access_fdd[i] = false;
+       }
+       access_scsi = false;
+       secnum = 1;
+       timeout = 0;
+}
+
+void BIOS::event_frame()
+{
+       timeout++;
+}
+
+bool BIOS::bios_call_i86(uint32_t PC, uint16_t regs[], uint16_t sregs[], int32_t* ZeroFlag, int32_t* CarryFlag)
+{
+       uint8_t *regs8 = (uint8_t *)regs;
+       int drv = AL & 0xf;
+       uint8_t buffer[BLOCK_SIZE * 4];
+       
+       if(PC == 0xfffc4) {
+               // disk bios
+#ifdef _DEBUG_LOG
+               this->out_debug_log(_T("%6x\tDISK BIOS: AH=%2x,AL=%2x,CX=%4x,DX=%4x,BX=%4x,DS=%2x,DI=%2x\n"), get_cpu_pc(0), AH,AL,CX,DX,BX,DS,DI);
+#endif
+               if(AH == 2) {
+                       // drive status
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               if(!(drv < MAX_DRIVE && disk[drv]->inserted)) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_NOTREADY;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               AH = 0;
+                               DL = 4;
+                               if(disk[drv]->write_protected) {
+                                       DL |= 2;
+                               }
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       if((AL & 0xf0) == 0xb0) {
+                               // scsi
+                               if(!(drv < MAX_SCSI && scsi_blocks[drv])) {
+                                       AH = 0x80;
+                                       CX = ERR_SCSI_NOTCONNECTED;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               AH = 0;
+                               AL = (BLOCK_SIZE == 128) ? 0 : (BLOCK_SIZE == 256) ? 1 : (BLOCK_SIZE == 512) ? 2 : 3;
+                               BX = scsi_blocks[drv] >> 16;
+                               DX = scsi_blocks[drv] & 0xffff;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       AH = 2;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 3 || AH == 4) {
+                       // restore/seek
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               if(!(drv < MAX_DRIVE && disk[drv]->inserted)) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_NOTREADY;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       if((AL & 0xf0) == 0xb0) {
+                               // scsi
+                               if(!(drv < MAX_SCSI && scsi_blocks[drv])) {
+                                       AH = 0x80;
+                                       CX = ERR_SCSI_NOTCONNECTED;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       AH = 2;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 5) {
+                       // read sectors
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               if(!(drv < MAX_DRIVE && disk[drv]->inserted)) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_NOTREADY;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               // get initial c/h/r
+                               int ofs = DS * 16 + DI;
+                               int trk = CX;
+                               int hed = DH & 1;
+                               int sct = DL;
+                               while(BX > 0) {
+                                       // search sector
+                                       disk[drv]->get_track(trk, hed);
+                                       access_fdd[drv] = true;
+                                       secnum = sct;
+                                       if(!disk[drv]->get_sector(trk, hed, sct - 1)) {
+                                               AH = 0x80;
+                                               CX = ERR_FDD_NOTFOUND;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       // check id crc error
+                                       if(disk[drv]->addr_crc_error && !disk[drv]->ignore_crc()) {
+                                               AH = 0x80;
+                                               CX = ERR_FDD_NOTFOUND | ERR_FDD_CRCERROR;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       // check deleted mark
+                                       if(disk[drv]->deleted) {
+                                               AH = 0x80;
+                                               CX = ERR_FDD_DELETED;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       // data transfer
+                                       for(int i = 0; i < disk[drv]->sector_size.sd; i++) {
+                                               d_mem->write_data8(ofs++, disk[drv]->sector[i]);
+                                       }
+                                       BX--;
+                                       // check data crc error
+                                       if(disk[drv]->data_crc_error && !disk[drv]->ignore_crc()) {
+                                               AH = 0x80;
+                                               CX = ERR_FDD_CRCERROR;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       // update c/h/r
+                                       if(++sct > disk[drv]->sector_num.sd) {
+                                               sct = 1;
+                                               if(++hed > 1) {
+                                                       hed = 0;
+                                                       ++trk;
+                                               }
+                                       }
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       if((AL & 0xf0) == 0xb0) {
+                               // scsi
+                               if(!(drv < MAX_SCSI && scsi_blocks[drv])) {
+                                       AH = 0x80;
+                                       CX = ERR_SCSI_NOTCONNECTED;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               FILEIO* fio = new FILEIO();
+                               if(!fio->Fopen(create_local_path(_T("SCSI%d.DAT"), drv), FILEIO_READ_BINARY)) {
+                                       AH = 0x80;
+                                       CX = ERR_SCSI_NOTREADY;
+                                       *CarryFlag = 1;
+                                       delete fio;
+                                       return true;
+                               }
+                               // get params
+                               int ofs = DS * 16 + DI;
+                               int block = (CL << 16) | DX;
+                               fio->Fseek(block * BLOCK_SIZE, FILEIO_SEEK_SET);
+                               while(BX > 0) {
+                                       // check block
+                                       access_scsi = true;
+                                       if(!(block++ < scsi_blocks[drv])) {
+                                               AH = 0x80;
+                                               CX = ERR_SCSI_PARAMERROR;
+                                               *CarryFlag = 1;
+                                               fio->Fclose();
+                                               delete fio;
+                                               return true;
+                                       }
+                                       // data transfer
+                                       fio->Fread(buffer, BLOCK_SIZE, 1);
+                                       for(int i = 0; i < BLOCK_SIZE; i++) {
+                                               d_mem->write_data8(ofs++, buffer[i]);
+                                       }
+                                       BX--;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               fio->Fclose();
+                               delete fio;
+                               return true;
+                       }
+                       AH = 2;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 6) {
+                       // write sectors
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               if(!(drv < MAX_DRIVE && disk[drv]->inserted)) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_NOTREADY;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               if(disk[drv]->write_protected) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_PROTECTED;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               // get initial c/h/r
+                               int ofs = DS * 16 + DI;
+                               int trk = CX;
+                               int hed = DH & 1;
+                               int sct = DL;
+                               while(BX > 0) {
+                                       // search sector
+                                       disk[drv]->get_track(trk, hed);
+                                       access_fdd[drv] = true;
+                                       secnum = sct;
+                                       if(!disk[drv]->get_sector(trk, hed, sct - 1)) {
+                                               AH = 0x80;
+                                               CX = ERR_FDD_NOTFOUND;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       // check id crc error
+                                       if(disk[drv]->addr_crc_error && !disk[drv]->ignore_crc()) {
+                                               AH = 0x80;
+                                               CX = ERR_FDD_NOTFOUND | ERR_FDD_CRCERROR;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       // data transfer
+                                       for(int i = 0; i < disk[drv]->sector_size.sd; i++) {
+                                               disk[drv]->sector[i] = d_mem->read_data8(ofs++);
+                                       }
+                                       BX--;
+                                       // clear deleted mark and data crc error
+                                       disk[drv]->set_deleted(false);
+                                       disk[drv]->set_data_crc_error(false);
+                                       // update c/h/r
+                                       if(++sct > disk[drv]->sector_num.sd) {
+                                               sct = 1;
+                                               if(++hed > 1) {
+                                                       hed = 0;
+                                                       ++trk;
+                                               }
+                                       }
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       if((AL & 0xf0) == 0xb0) {
+                               // scsi
+                               if(!(drv < MAX_SCSI && scsi_blocks[drv])) {
+                                       AH = 0x80;
+                                       CX = ERR_SCSI_NOTCONNECTED;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               FILEIO* fio = new FILEIO();
+                               if(!fio->Fopen(create_local_path(_T("SCSI%d.DAT"), drv), FILEIO_READ_WRITE_BINARY)) {
+                                       AH = 0x80;
+                                       CX = ERR_SCSI_NOTREADY;
+                                       *CarryFlag = 1;
+                                       delete fio;
+                                       return true;
+                               }
+                               // get params
+                               int ofs = DS * 16 + DI;
+                               int block = (CL << 16) | DX;
+                               fio->Fseek(block * BLOCK_SIZE, FILEIO_SEEK_SET);
+                               while(BX > 0) {
+                                       // check block
+                                       access_scsi = true;
+                                       if(!(block++ < scsi_blocks[drv])) {
+                                               AH = 0x80;
+                                               CX = ERR_SCSI_PARAMERROR;
+                                               *CarryFlag = 1;
+                                               fio->Fclose();
+                                               delete fio;
+                                               return true;
+                                       }
+                                       // data transfer
+                                       for(int i = 0; i < BLOCK_SIZE; i++) {
+                                               buffer[i] = d_mem->read_data8(ofs++);
+                                       }
+                                       fio->Fwrite(buffer, BLOCK_SIZE, 1);
+                                       BX--;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               fio->Fclose();
+                               delete fio;
+                               return true;
+                       }
+                       AH = 2;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 7) {
+                       // verify sectors
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               if(!(drv < MAX_DRIVE && disk[drv]->inserted)) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_NOTREADY;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               // get initial c/h/r
+                               int trk = CX;
+                               int hed = DH & 1;
+                               int sct = DL;
+                               while(BX > 0) {
+                                       // search sector
+                                       disk[drv]->get_track(trk, hed);
+                                       access_fdd[drv] = true;
+                                       secnum = sct;
+                                       if(!disk[drv]->get_sector(trk, hed, sct - 1)) {
+                                               AH = 0x80;
+                                               CX = ERR_FDD_NOTFOUND;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       // check id crc error
+                                       if(disk[drv]->addr_crc_error && !disk[drv]->ignore_crc()) {
+                                               AH = 0x80;
+                                               CX = ERR_FDD_NOTFOUND | ERR_FDD_CRCERROR;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       // FIXME: verify
+                                       BX--;
+                                       // check data crc error
+                                       if(disk[drv]->data_crc_error && !disk[drv]->ignore_crc()) {
+                                               AH = 0x80;
+                                               CX = ERR_FDD_CRCERROR;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       // update c/h/r
+                                       if(++sct > disk[drv]->sector_num.sd) {
+                                               sct = 1;
+                                               if(++hed > 1) {
+                                                       hed = 0;
+                                                       ++trk;
+                                               }
+                                       }
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       if((AL & 0xf0) == 0xb0) {
+                               // scsi
+                               if(!(drv < MAX_SCSI && scsi_blocks[drv])) {
+                                       AH = 0x80;
+                                       CX = ERR_SCSI_NOTCONNECTED;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               // get params
+                               int block = (CL << 16) | DX;
+                               while(BX > 0) {
+                                       // check block
+                                       access_scsi = true;
+                                       if(!(block++ < scsi_blocks[drv])) {
+                                               AH = 0x80;
+                                               CX = ERR_SCSI_PARAMERROR;
+                                               *CarryFlag = 1;
+                                               return true;
+                                       }
+                                       BX--;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       AH = 2;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 8) {
+                       // reset hard drive controller
+                       AH = 0;
+                       CX = 0;
+                       *CarryFlag = 0;
+                       return true;
+               } else if(AH == 9) {
+                       // read id
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               if(!(drv < MAX_DRIVE && disk[drv]->inserted)) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_NOTREADY;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               // get initial c/h
+                               int ofs = DS * 16 + DI;
+                               int trk = CX;
+                               int hed = DH & 1;
+                               // search sector
+                               disk[drv]->get_track(trk, hed);
+                               access_fdd[drv] = true;
+                               if(++secnum > disk[drv]->sector_num.sd) {
+                                       secnum = 1;
+                               }
+                               if(!disk[drv]->get_sector(trk, hed, secnum - 1)) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_NOTFOUND;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               // data transfer
+                               for(int i = 0; i < 6; i++) {
+                                       d_mem->write_data8(ofs++, disk[drv]->id[i]);
+                               }
+                               // check id crc error
+                               if(disk[drv]->addr_crc_error && !disk[drv]->ignore_crc()) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_CRCERROR;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       AH = 2;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 0xa) {
+                       // format track
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               if(!(drv < MAX_DRIVE && disk[drv]->inserted)) {
+                                       AH = 0x80;
+                                       CX = ERR_FDD_NOTREADY;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               // get initial c/h
+                               int ofs = DS * 16 + DI;
+                               int trk = CX;
+                               int hed = DH & 1;
+                               // format track
+                               disk[drv]->format_track(trk, hed);
+                               access_fdd[drv] = true;
+                               bool id_written = false;
+                               bool sector_found = false;
+                               int sector_length, sector_index;
+                               for(int index = 0; index < disk[drv]->get_track_size(); index++) {
+                                       uint8_t datareg = d_mem->read_data8(ofs++);
+                                       if(datareg == 0xf5) {
+                                               // write a1h in missing clock
+                                       } else if(datareg == 0xf6) {
+                                               // write c2h in missing clock
+                                       } else if(datareg == 0xf7) {
+                                               // write crc
+                                               if(!id_written) {
+                                                       // insert new sector with data crc error
+write_id:
+                                                       id_written = true;
+                                                       sector_found = false;
+                                                       uint8_t c = disk[drv]->track[index - 4];
+                                                       uint8_t h = disk[drv]->track[index - 3];
+                                                       uint8_t r = disk[drv]->track[index - 2];
+                                                       uint8_t n = disk[drv]->track[index - 1];
+                                                       sector_length = 0x80 << (n & 3);
+                                                       sector_index = 0;
+                                                       disk[drv]->insert_sector(c, h, r, n, false, true, 0xe5, sector_length);
+                                               } else if(sector_found) {
+                                                       // clear data crc error if all sector data are written
+                                                       disk[drv]->set_data_crc_error(false);
+                                                       id_written = false;
+                                               } else {
+                                                       // data mark of current sector is not written
+                                                       disk[drv]->set_data_mark_missing();
+                                                       goto write_id;
+                                               }
+                                       } else if(id_written) {
+                                               if(sector_found) {
+                                                       // sector data
+                                                       if(sector_index < sector_length) {
+                                                               disk[drv]->sector[sector_index] = datareg;
+                                                       }
+                                                       sector_index++;
+                                               } else if(datareg == 0xf8 || datareg == 0xfb) {
+                                                       // data mark
+                                                       disk[drv]->set_deleted(datareg == 0xf8);
+                                                       sector_found = true;
+                                               }
+                                       }
+                                       disk[drv]->track[index] = datareg;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       AH = 2;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 0xd) {
+                       // read error
+                       AH = 0;
+                       CX = 0;
+                       *CarryFlag = 0;
+                       return true;
+               } else if(AH == 0xe) {
+                       // disk change ???
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               if(!(drv < MAX_DRIVE && disk[drv]->inserted)) {
+                                       AH = 0;
+                                       CX = 0;
+                                       DL = 1;
+                                       *CarryFlag = 0;
+                                       return true;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               DL = disk[drv]->changed ? 1 : 0;
+                               disk[drv]->changed = false;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       if((AL & 0xf0) == 0xb0) {
+                               // scsi
+                               if(!(drv < MAX_SCSI && scsi_blocks[drv])) {
+                                       AH = 3; // ???
+                                       CX = 0;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       AH = 2;
+                       CX = 0;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 0xfa) {
+                       // unknown
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               AH = 1;
+                               CX = 0;
+                               *CarryFlag = 1;
+                               return true;
+                       }
+                       if((AL & 0xf0) == 0xb0) {
+                               // scsi
+                               if(!(drv < MAX_SCSI && scsi_blocks[drv])) {
+                                       AH = 0x80;
+                                       CX = ERR_SCSI_NOTCONNECTED;
+                                       *CarryFlag = 1;
+                                       return true;
+                               }
+                               AH = 0;
+                               CX = 0;
+                               *CarryFlag = 0;
+                               return true;
+                       }
+                       AH = 2;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 0xfd) {
+                       // unknown
+                       if((AL & 0xf0) == 0x20) {
+                               // floppy
+                               AH = 1;
+                               CX = 0;
+                               *CarryFlag = 1;
+                               return true;
+                       }
+                       if((AL & 0xf0) == 0xb0) {
+                               // scsi
+                               if(!(drv < MAX_SCSI && scsi_blocks[drv])) {
+                                       AH = 0;
+                                       CX = 0x200;     // ???
+                                       *CarryFlag = 0;
+                                       return true;
+                               }
+                               AH = 2;
+                               CX = 0;
+                               *CarryFlag = 1;
+                               return true;
+                       }
+                       AH = 2;
+                       CX = 0;
+                       *CarryFlag = 1;
+                       return true;
+               } else if(AH == 0x80) {
+                       // pseudo bios: init i/o
+                       for(int i = 0;; i++) {
+                               if(iotable[i][0] < 0) {
+                                       break;
+                               }
+                               d_io->write_io8(iotable[i][0], iotable[i][1]);
+                       }
+                       // init cmos
+                       memset(cmos, 0, CMOS_SIZE);
+                       memcpy(cmos, cmos_t, sizeof(cmos_t));
+                       memcpy(cmos + CMOS_SIZE - sizeof(cmos_b), cmos_b, sizeof(cmos_b));
+                       // init int vector
+                       for(int i = 0, ofs = 0; i < 256; i++) {
+                               // int vector = ffff:0008
+                               d_mem->write_data16(ofs + 0, 0x0008);
+                               d_mem->write_data16(ofs + 2, 0xffff);
+                               ofs += 4;
+                       }
+                       // init screen
+                       memset(vram, 0, VRAM_SIZE);
+#ifdef _FMR60
+                       memset(cvram, 0, 0x2000);
+                       memset(avram, 0, 0x2000);
+#else
+                       memset(cvram, 0, 0x1000);
+                       memset(kvram, 0, 0x1000);
+                       memcpy(cvram + 0xf00, msg_c, sizeof(msg_c));
+                       memcpy(kvram + 0xf00, msg_k, sizeof(msg_k));
+#endif
+                       *CarryFlag = 0;
+                       return true;
+               } else if(AH == 0x81) {
+                       // pseudo bios: boot from fdd #0
+                       *ZeroFlag = (timeout > (int)(FRAMES_PER_SEC * 4));
+                       if(!disk[0]->inserted) {
+                               *CarryFlag = 1;
+                               return true;
+                       }
+                       // load ipl
+                       disk[0]->get_track(0, 0);
+                       access_fdd[0] = true;
+                       if(!disk[0]->get_sector(0, 0, 0)) {
+                               *CarryFlag = 1;
+                               return true;
+                       }
+                       for(int i = 0; i < disk[0]->sector_size.sd; i++) {
+                               buffer[i] = disk[0]->sector[i];
+                       }
+                       // check ipl
+                       if(!(buffer[0] == 'I' && buffer[1] == 'P' && buffer[2] == 'L' && buffer[3] == IPL_ID)) {
+                               *CarryFlag = 1;
+                               return true;
+                       }
+                       // data transfer
+                       for(int i = 0; i < disk[0]->sector_size.sd; i++) {
+                               d_mem->write_data8(0xb0000 + i, buffer[i]);
+                       }
+                       // clear screen
+#ifdef _FMR60
+                       memset(cvram, 0, 0x2000);
+                       memset(avram, 0, 0x2000);
+#else
+                       memset(cvram, 0, 0x1000);
+                       memset(kvram, 0, 0x1000);
+#endif
+                       // set result
+                       AX = 0xff;
+                       CX = 0;
+                       BX = 2;
+                       *ZeroFlag = 1;
+                       *CarryFlag = 0;
+                       return true;
+               } else if(AH == 0x82) {
+                       // pseudo bios: boot from scsi-hdd #0
+                       timeout = 0;
+                       if(!scsi_blocks[0]) {
+                               *CarryFlag = 1;
+                               return true;
+                       }
+                       FILEIO* fio = new FILEIO();
+                       if(!fio->Fopen(create_local_path(_T("SCSI%d.DAT"), drv), FILEIO_READ_BINARY)) {
+                               *CarryFlag = 1;
+                               delete fio;
+                               return true;
+                       }
+                       // load ipl
+                       access_scsi = true;
+                       fio->Fread(buffer, BLOCK_SIZE * 4, 1);
+                       fio->Fclose();
+                       delete fio;
+                       // check ipl
+                       if(!(buffer[0] == 'I' && buffer[1] == 'P' && buffer[2] == 'L' && buffer[3] == IPL_ID)) {
+                               *CarryFlag = 1;
+                               return true;
+                       }
+                       // data transfer
+                       for(int i = 0; i < BLOCK_SIZE * 4; i++) {
+                               d_mem->write_data8(0xb0000 + i, buffer[i]);
+                       }
+                       // clear screen
+#ifdef _FMR60
+                       memset(cvram, 0, 0x2000);
+                       memset(avram, 0, 0x2000);
+#else
+                       memset(cvram, 0, 0x1000);
+                       memset(kvram, 0, 0x1000);
+#endif
+                       // set result
+                       AX = 0xffff;
+                       CX = 0;
+                       BX = 1;
+                       *ZeroFlag = 1;
+                       *CarryFlag = 0;
+                       return true;
+               }
+       } else if(PC == 0xfffc9) {
+               // cmos
+#ifdef _DEBUG_LOG
+               this->out_debug_log(_T("%6x\tCMOS BIOS: AH=%2x,AL=%2x,CX=%4x,DX=%4x,BX=%4x,DS=%2x,DI=%2x\n"), get_cpu_pc(0), AH,AL,CX,DX,BX,DS,DI);
+#endif
+               if(AH == 0) {
+                       // init cmos
+                       memcpy(cmos, cmos_t, sizeof(cmos_t));
+                       memcpy(cmos + CMOS_SIZE - sizeof(cmos_b), cmos_b, sizeof(cmos_b));
+               } else if(AH == 5) {
+                       // get $a2
+                       BX = cmos[0xa2] | (cmos[0xa3] << 8);
+               } else if(AH == 10) {
+                       // memory to cmos
+                       int block = AL * 10;
+                       int len = cmos[block + 6] | (cmos[block + 7] << 8);
+                       int dst = cmos[block + 8] | (cmos[block + 9] << 8);
+                       int src = DS * 16 + DI;
+                       for(int i = 0; i < len; i++) {
+                               cmos[dst++] = d_mem->read_data8(src++);
+                       }
+               } else if(AH == 11) {
+                       // cmos to memory
+                       int block = AL * 10;
+                       int len = cmos[block + 6] | (cmos[block + 7] << 8);
+                       int src = cmos[block + 8] | (cmos[block + 9] << 8);
+                       int dst = DS * 16 + DI;
+                       for(int i = 0; i < len; i++) {
+                               d_mem->write_data8(dst++, cmos[src++]);
+                       }
+               } else if(AH == 20) {
+                       // check block header
+                       BX = 0;
+               }
+               AH = 0;
+               *CarryFlag = 0;
+               return true;
+       } else if(PC == 0xfffd3) {
+               // wait
+#ifdef _DEBUG_LOG
+               this->out_debug_log(_T("%6x\tWAIT BIOS: AH=%2x,AL=%2x,CX=%4x,DX=%4x,BX=%4x,DS=%2x,DI=%2x\n"), get_cpu_pc(0), AH,AL,CX,DX,BX,DS,DI);
+#endif
+               *CarryFlag = 0;
+               return true;
+       }
+       return false;
+}
+
+bool BIOS::bios_int_i86(int intnum, uint16_t regs[], uint16_t sregs[], int32_t* ZeroFlag, int32_t* CarryFlag)
+{
+       uint8_t *regs8 = (uint8_t *)regs;
+       
+       if(intnum == 0x93) {
+               // disk bios
+               return bios_call_i86(0xfffc4, regs, sregs, ZeroFlag, CarryFlag);
+       }
+       return false;
+}
+
+uint32_t BIOS::read_signal(int ch)
+{
+       // get access status
+       uint32_t stat = 0;
+       for(int i = 0; i < MAX_DRIVE; i++) {
+               if(access_fdd[i]) {
+                       stat |= 1 << i;
+               }
+               access_fdd[i] = false;
+       }
+       if(access_scsi) {
+               stat |= 0x10;
+       }
+       access_scsi = false;
+       return stat;
+}
+
+#define STATE_VERSION  3
+
+void BIOS::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       
+       for(int i = 0; i < MAX_DRIVE; i++) {
+               disk[i]->save_state(state_fio);
+       }
+       state_fio->FputInt32(secnum);
+       state_fio->FputInt32(timeout);
+}
+
+bool BIOS::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       if(state_fio->FgetInt32() != this_device_id) {
+               return false;
+       }
+       for(int i = 0; i < MAX_DRIVE; i++) {
+               if(!disk[i]->load_state(state_fio)) {
+                       return false;
+               }
+       }
+       secnum = state_fio->FgetInt32();
+       timeout = state_fio->FgetInt32();
+       return true;
+}
+
diff --git a/source/src/vm/fmtowns/bios.h b/source/src/vm/fmtowns/bios.h
new file mode 100644 (file)
index 0000000..056a6be
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+       FUJITSU FMR-30 Emulator 'eFMR-30'
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.10.06 -
+
+       [ bios ]
+*/
+
+#ifndef _BIOS_H_
+#define _BIOS_H_
+
+#include "../vm.h"
+#include "../../emu.h"
+#include "../device.h"
+
+class DISK;
+
+class BIOS : public DEVICE
+{
+private:
+       DEVICE *d_mem, *d_io;
+       DISK *disk[MAX_DRIVE];
+       
+       // pseudo bios
+       uint8_t *cmos, *vram, *cvram;
+#ifdef _FMR60
+       uint8_t *avram;
+#else
+       uint8_t *kvram;
+#endif
+       int secnum, timeout;
+       
+       // disk bios
+       bool access_fdd[MAX_DRIVE], access_scsi;
+       int scsi_blocks[MAX_SCSI];
+       
+public:
+       BIOS(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) {
+               set_device_name(_T("PSEUDO BIOS"));
+       }
+       ~BIOS() {}
+       
+       // common functions
+       void initialize();
+       void reset();
+       void event_frame();
+       bool bios_call_i86(uint32_t PC, uint16_t regs[], uint16_t sregs[], int32_t* ZeroFlag, int32_t* CarryFlag);
+       bool bios_int_i86(int intnum, uint16_t regs[], uint16_t sregs[], int32_t* ZeroFlag, int32_t* CarryFlag);
+       uint32_t read_signal(int ch);
+       void save_state(FILEIO* state_fio);
+       bool load_state(FILEIO* state_fio);
+       
+       // unique functions
+       void set_context_mem(DEVICE* device)
+       {
+               d_mem = device;
+       }
+       void set_context_io(DEVICE* device)
+       {
+               d_io = device;
+       }
+       void set_disk_handler(int drv, DISK* dsk)
+       {
+               disk[drv] = dsk;
+       }
+       void set_cmos_ptr(uint8_t* ptr)
+       {
+               cmos = ptr;
+       }
+       void set_vram_ptr(uint8_t* ptr)
+       {
+               vram = ptr;
+       }
+       void set_cvram_ptr(uint8_t* ptr)
+       {
+               cvram = ptr;
+       }
+#ifdef _FMR60
+       void set_avram_ptr(uint8_t* ptr)
+       {
+               avram = ptr;
+       }
+#else
+       void set_kvram_ptr(uint8_t* ptr)
+       {
+               kvram = ptr;
+       }
+#endif
+};
+
+#endif
+
diff --git a/source/src/vm/fmtowns/cmos.cpp b/source/src/vm/fmtowns/cmos.cpp
new file mode 100644 (file)
index 0000000..75058ba
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.05.01 -
+
+       [ cmos ]
+*/
+
+#include "cmos.h"
+
+void CMOS::initialize()
+{
+       // load cmos image
+       memset(cmos, 0, sizeof(cmos));
+       modified = false;
+       
+       FILEIO* fio = new FILEIO();
+       if(fio->Fopen(create_local_path(_T("CMOS.BIN")), FILEIO_READ_BINARY)) {
+               fio->Fread(cmos, sizeof(cmos), 1);
+               fio->Fclose();
+       }
+       delete fio;
+}
+
+void CMOS::release()
+{
+       if(modified) {
+               FILEIO* fio = new FILEIO();
+               if(fio->Fopen(create_local_path(_T("CMOS.BIN")), FILEIO_WRITE_BINARY)) {
+                       fio->Fwrite(cmos, sizeof(cmos), 1);
+                       fio->Fclose();
+               }
+               delete fio;
+       }
+}
+
+void CMOS::reset()
+{
+       bank = 0;
+}
+
+void CMOS::write_io8(uint32_t addr, uint32_t data)
+{
+       switch(addr) {
+       case 0x90:
+               bank = data & 3;
+               break;
+       default:
+               if(!(addr & 1)) {
+                       if(cmos[bank][(addr >> 1) & 0x7ff] != data) {
+                               cmos[bank][(addr >> 1) & 0x7ff] = data;
+                               modified = true;
+                       }
+               }
+               break;
+       }
+}
+
+uint32_t CMOS::read_io8(uint32_t addr)
+{
+       if(!(addr & 1)) {
+               return cmos[bank][(addr >> 1) & 0x7ff];
+       }
+       return 0xff;
+}
+
+#define STATE_VERSION  1
+
+void CMOS::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       
+       state_fio->Fwrite(cmos, sizeof(cmos), 1);
+       state_fio->FputBool(modified);
+       state_fio->FputUint8(bank);
+}
+
+bool CMOS::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       if(state_fio->FgetInt32() != this_device_id) {
+               return false;
+       }
+       state_fio->Fread(cmos, sizeof(cmos), 1);
+       modified = state_fio->FgetBool();
+       bank = state_fio->FgetUint8();
+       return true;
+}
+
diff --git a/source/src/vm/fmtowns/cmos.h b/source/src/vm/fmtowns/cmos.h
new file mode 100644 (file)
index 0000000..787d0f8
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.05.01 -
+
+       [ cmos ]
+*/
+
+#ifndef _CMOS_H_
+#define _CMOS_H_
+
+#include "../vm.h"
+#include "../../emu.h"
+#include "../device.h"
+
+class CMOS : public DEVICE
+{
+private:
+#ifdef _FMRCARD
+       uint8_t cmos[4][0x800];
+#else
+       uint8_t cmos[1][0x800];
+#endif
+       bool modified;
+       uint8_t bank;
+       
+public:
+       CMOS(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) {}
+       ~CMOS() {}
+       
+       // common functions
+       void initialize();
+       void release();
+       void reset();
+       void write_io8(uint32_t addr, uint32_t data);
+       uint32_t read_io8(uint32_t addr);
+       void save_state(FILEIO* state_fio);
+       bool load_state(FILEIO* state_fio);
+       
+       // unique function
+       uint8_t* get_cmos()
+       {
+               return cmos[0];
+       }
+};
+
+#endif
+
diff --git a/source/src/vm/fmtowns/floppy.cpp b/source/src/vm/fmtowns/floppy.cpp
new file mode 100644 (file)
index 0000000..ed0c4e3
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.04.30 -
+
+       [ floppy ]
+*/
+
+#include "floppy.h"
+#include "../i8259.h"
+#include "../mb8877.h"
+
+void FLOPPY::initialize()
+{
+       drvreg = drvsel = 0;
+       irq = irqmsk = false;
+       changed[0] = changed[1] = changed[2] = changed[3] = false;
+}
+
+void FLOPPY::write_io8(uint32_t addr, uint32_t data)
+{
+       int nextdrv = drvsel;
+       
+       switch(addr & 0xffff) {
+       case 0x208:
+               // drive control register
+               irqmsk = ((data & 1) != 0);
+               update_intr();
+               d_fdc->write_signal(SIG_MB8877_MOTOR, data, 0x10);
+               d_fdc->write_signal(SIG_MB8877_SIDEREG, data, 4);
+               break;
+       case 0x20c:
+               // drive select register
+               if(data & 1) {
+                       nextdrv = 0;
+               } else if(data & 2) {
+                       nextdrv = 1;
+               } else if(data & 4) {
+                       nextdrv = 2;
+               } else if(data & 8) {
+                       nextdrv = 3;
+               }
+               if(drvsel != nextdrv) {
+                       d_fdc->write_signal(SIG_MB8877_DRIVEREG, drvsel = nextdrv, 3);
+               }
+               drvreg = data;
+               break;
+       }
+}
+
+uint32_t FLOPPY::read_io8(uint32_t addr)
+{
+       switch(addr & 0xffff) {
+       case 0x208:
+               if(changed[drvsel]) {
+                       changed[drvsel] = false;
+                       return d_fdc->fdc_status() | 0xe1;      // fdd*2
+               }
+//             return d_fdc->fdc_status() | 0x60;      // fdd*1
+               return d_fdc->fdc_status() | 0xe0;      // fdd*2
+       case 0x20c:
+               return drvreg;
+       }
+       return 0xff;
+}
+
+void FLOPPY::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       if(id == SIG_FLOPPY_IRQ) {
+               irq = ((data & mask) != 0);
+               update_intr();
+       }
+}
+
+void FLOPPY::update_intr()
+{
+       d_pic->write_signal(SIG_I8259_CHIP0 | SIG_I8259_IR6, irq && irqmsk ? 1 : 0, 1);
+}
+
+#define STATE_VERSION  1
+
+void FLOPPY::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       
+       state_fio->FputInt32(drvreg);
+       state_fio->FputInt32(drvsel);
+       state_fio->FputBool(irq);
+       state_fio->FputBool(irqmsk);
+       state_fio->Fwrite(changed, sizeof(changed), 1);
+}
+
+bool FLOPPY::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       if(state_fio->FgetInt32() != this_device_id) {
+               return false;
+       }
+       drvreg = state_fio->FgetInt32();
+       drvsel = state_fio->FgetInt32();
+       irq = state_fio->FgetBool();
+       irqmsk = state_fio->FgetBool();
+       state_fio->Fread(changed, sizeof(changed), 1);
+       return true;
+}
+
diff --git a/source/src/vm/fmtowns/floppy.h b/source/src/vm/fmtowns/floppy.h
new file mode 100644 (file)
index 0000000..70a9249
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.04.30 -
+
+       [ floppy ]
+*/
+
+#ifndef _FLOPPY_H_
+#define _FLOPPY_H_
+
+#include "../vm.h"
+#include "../../emu.h"
+#include "../device.h"
+
+#define SIG_FLOPPY_IRQ 0
+
+class MB8877;
+
+class FLOPPY : public DEVICE
+{
+private:
+       MB8877 *d_fdc;
+       DEVICE *d_pic;
+       
+       int drvreg, drvsel;
+       bool irq, irqmsk, changed[4];
+       void update_intr();
+       
+public:
+       FLOPPY(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) {}
+       ~FLOPPY() {}
+       
+       // common functions
+       void initialize();
+       void write_io8(uint32_t addr, uint32_t data);
+       uint32_t read_io8(uint32_t addr);
+       void write_signal(int id, uint32_t data, uint32_t mask);
+       void save_state(FILEIO* state_fio);
+       bool load_state(FILEIO* state_fio);
+       
+       // unique functions
+       void set_context_fdc(MB8877* device)
+       {
+               d_fdc = device;
+       }
+       void set_context_pic(DEVICE* device)
+       {
+               d_pic = device;
+       }
+       void change_disk(int drv)
+       {
+               changed[drv] = true;
+       }
+};
+
+#endif
+
diff --git a/source/src/vm/fmtowns/fmtowns.cpp b/source/src/vm/fmtowns/fmtowns.cpp
new file mode 100644 (file)
index 0000000..bd4e829
--- /dev/null
@@ -0,0 +1,554 @@
+/*
+       FUJITSU FM-Towns Emulator 'eFMR-60'
+
+       Author : Kyuma Ohta <whatisthis.sowhat _at_ gmail.com>
+       Date   : 216.12.28 -
+
+       [ virtual machine ]
+       History: 
+               2016-12-28 Copy from eFMR-50.
+*/
+
+#include "fmtowns.h"
+#include "../../emu.h"
+#include "../device.h"
+#include "../event.h"
+
+#include "towns_crtc.h"
+//#include "../hd46505.h"
+#include "../i8251.h"
+#include "../i8253.h"
+#include "../i8259.h"
+
+#include "../i386.h"
+
+#include "../io.h"
+#include "../mb8877.h"
+#include "../msm58321.h"
+#include "../pcm1bit.h"
+#include "../scsi_hdd.h"
+#include "../scsi_host.h"
+#include "../upd71071.h"
+
+// Electric Volume
+//#include "mb87078.h"
+//YM-2612 "OPN2"
+//#include "../ym2612.h"
+//RF5C68 PCM
+//#include "rp5c68.h"
+//AD7820 ADC
+//#include "ad7820.h"
+// 80387?
+
+#ifdef USE_DEBUGGER
+#include "../debugger.h"
+#endif
+
+#include "bios.h"
+#include "cmos.h"
+#include "floppy.h"
+#include "keyboard.h"
+#include "memory.h"
+#include "scsi.h"
+//#include "serial.h"
+#include "timer.h"
+
+// ----------------------------------------------------------------------------
+// initialize
+// ----------------------------------------------------------------------------
+
+VM::VM(EMU* parent_emu) : emu(parent_emu)
+{
+/*
+       Machine ID & CPU ID
+
+       FMR-50FD/HD/LT  0xF8
+       FMR-50FX/HX     0xE0
+       FMR-50SFX/SHX   0xE8
+       FMR-50LT        0xF8
+       FMR-50NBX       0x28
+       FMR-50NB        0x60
+       FMR-50NE/T      0x08
+       FMR-CARD        0x70
+
+       80286           0x00
+       80386           0x01
+       80386SX         0x03
+       80486           0x02
+*/
+       static const int cpu_clock[] = {
+#if defined(HAS_I386)
+               16000000, 20000000
+#elif defined(HAS_I486)
+               20000000, 25000000
+#endif
+       };
+       
+#if defined(_FMR60) && (defined(HAS_I386) || defined(HAS_I486) || defined(HAS_PENTIUM))
+       uint8_t machine_id = 0xf0;      // FMR-70/80
+#else
+       uint8_t machine_id = 0xf8;      // FMR-50/60
+#endif
+       
+       FILEIO* fio = new FILEIO();
+       if(fio->Fopen(create_local_path(_T("MACHINE.ID")), FILEIO_READ_BINARY)) {
+               machine_id = fio->Fgetc();
+               fio->Fclose();
+       }
+       delete fio;
+       
+       machine_id &= ~7;
+#if defined(HAS_I286)
+       machine_id |= 0;        // 286
+#elif defined(HAS_I386)
+//     machine_id |= 1;        // 386DX
+       machine_id |= 3;        // 386SX
+#elif defined(HAS_I486)
+       machine_id |= 2;        // 486SX/DX
+#endif
+       
+       // create devices
+       first_device = last_device = NULL;
+       dummy = new DEVICE(this, emu);  // must be 1st device
+       event = new EVENT(this, emu);   // must be 2nd device
+#if defined(_USE_QT)
+       dummy->set_device_name(_T("1st Dummy"));
+       event->set_device_name(_T("EVENT"));
+#endif 
+
+       cpu = new I386(this, emu);
+#if defined(_USE_QT)
+  #if defined(HAS_I386)
+       cpu->set_device_name(_T("CPU(i386)"));
+  #elif defined(HAS_I486)
+       cpu->set_device_name(_T("CPU(i486)"));
+  #elif defined(HAS_PENTIUM)
+       cpu->set_device_name(_T("CPU(Pentium)"));
+  #endif
+#endif 
+
+       crtc = new TOWNS_CRTC(this, emu);
+       
+#if defined(_USE_QT)
+       crtc->set_device_name(_T("TOWNS CRTC"));
+#endif
+       
+       sio = new I8251(this, emu);
+       pit0 = new I8253(this, emu);
+       pit1 = new I8253(this, emu);
+       pic = new I8259(this, emu);
+       io = new IO(this, emu);
+       fdc = new MB8877(this, emu);
+       rtc = new MSM58321(this, emu);
+       pcm = new PCM1BIT(this, emu);
+#if defined(_USE_QT)   
+       sio->set_device_name(_T("i8251 SIO"));
+       pit0->set_device_name(_T("i8253 PIT #0"));
+       pit1->set_device_name(_T("i8253 PIT #1"));
+       pic->set_device_name(_T("i8259 PIC"));
+       rtc->set_device_name(_T("MSM58321 RTC"));
+       pcm->set_device_name(_T("PCM SOUND"));
+#endif
+       
+       scsi_host = new SCSI_HOST(this, emu);
+#if defined(_USE_QT)   
+       scsi_host->set_device_name(_T("SCSI HOST"));
+#endif 
+       for(int i = 0; i < 7; i++) {
+               if(FILEIO::IsFileExisting(create_local_path(_T("SCSI%d.DAT"), i))) {
+                       SCSI_HDD* scsi_hdd = new SCSI_HDD(this, emu);
+#if defined(_USE_QT)
+                       char d_name[64] = {0};
+                       snprintf(d_name, 64, "SCSI DISK #%d", i + 1);
+                       scsi_hdd->set_device_name(d_name);
+#endif                 
+                       scsi_hdd->scsi_id = i;
+                       scsi_hdd->set_context_interface(scsi_host);
+                       scsi_host->set_context_target(scsi_hdd);
+               }
+       }
+       dma = new UPD71071(this, emu);
+#if defined(_USE_QT)   
+       dma->set_device_name(_T("uPD71071 DMAC"));
+#endif 
+       if(FILEIO::IsFileExisting(create_local_path(_T("IPL.ROM")))) {
+               bios = NULL;
+       } else {
+               bios = new BIOS(this, emu);
+#if defined(_USE_QT)
+               bios->set_device_name(_T("PSEUDO BIOS"));
+#endif
+       }
+       cmos = new CMOS(this, emu);
+       floppy = new FLOPPY(this, emu);
+       keyboard = new KEYBOARD(this, emu);
+       memory = new MEMORY(this, emu);
+       scsi = new SCSI(this, emu);
+//     serial = new SERIAL(this, emu);
+       timer = new TIMER(this, emu);
+#if defined(_USE_QT)
+       cmos->set_device_name(_T("CMOS RAM"));
+       floppy->set_device_name(_T("FLOPPY I/F"));
+       keyboard->set_device_name(_T("KEYBOARD"));
+       memory->set_device_name(_T("MEMORY"));
+       scsi->set_device_name(_T("SCSI I/F"));
+       //serial->set_device_name(_T("SERIAL I/F"));
+       timer->set_device_name(_T("TIMER I/F"));
+#endif
+       
+       // set contexts
+       event->set_context_cpu(cpu, cpu_clock[config.cpu_type & 1]);
+       event->set_context_sound(pcm);
+#if defined(USE_SOUND_FILES)
+       if(fdc->load_sound_data(MB8877_SND_TYPE_SEEK, _T("FDDSEEK.WAV"))) {
+               event->set_context_sound(fdc);
+       }
+#endif
+       
+/*     pic     0       timer
+               1       keyboard
+               2       rs-232c
+               3       ex rs-232c
+               4       (option)
+               5       (option)
+               6       floppy drive or dma ???
+               7       (slave)
+               8       scsi
+               9       (option)
+               10      (option)
+               11      (option)
+               12      printer
+               13      (option)
+               14      (option)
+               15      (reserve)
+
+       dma     0       floppy drive
+               1       hard drive
+               2       (option)
+               3       (reserve)
+*/
+       crtc->set_context_disp(memory, SIG_MEMORY_DISP, 1);
+       crtc->set_context_vsync(memory, SIG_MEMORY_VSYNC, 1);
+#ifdef _FMR60
+       acrtc->set_vram_ptr((uint16_t*)memory->get_vram(), 0x80000);
+#endif
+       pit0->set_context_ch0(timer, SIG_TIMER_CH0, 1);
+       pit0->set_context_ch1(timer, SIG_TIMER_CH1, 1);
+       pit0->set_context_ch2(pcm, SIG_PCM1BIT_SIGNAL, 1);
+       pit0->set_constant_clock(0, 307200);
+       pit0->set_constant_clock(1, 307200);
+       pit0->set_constant_clock(2, 307200);
+       pit1->set_constant_clock(1, 1228800);
+       pic->set_context_cpu(cpu);
+       fdc->set_context_drq(dma, SIG_UPD71071_CH0, 1);
+       fdc->set_context_irq(floppy, SIG_FLOPPY_IRQ, 1);
+       rtc->set_context_data(timer, SIG_TIMER_RTC, 0x0f, 0);
+       rtc->set_context_busy(timer, SIG_TIMER_RTC, 0x80);
+       scsi_host->set_context_irq(scsi, SIG_SCSI_IRQ, 1);
+       scsi_host->set_context_drq(scsi, SIG_SCSI_DRQ, 1);
+       dma->set_context_memory(memory);
+       dma->set_context_ch0(fdc);
+       dma->set_context_ch1(scsi_host);
+       
+       floppy->set_context_fdc(fdc);
+       floppy->set_context_pic(pic);
+       keyboard->set_context_pic(pic);
+       memory->set_context_cpu(cpu);
+       memory->set_machine_id(machine_id);
+       memory->set_context_crtc(crtc);
+       memory->set_chregs_ptr(crtc->get_regs());
+       scsi->set_context_dma(dma);
+       scsi->set_context_pic(pic);
+       scsi->set_context_host(scsi_host);
+       timer->set_context_pcm(pcm);
+       timer->set_context_pic(pic);
+       timer->set_context_rtc(rtc);
+       
+       // cpu bus
+       cpu->set_context_mem(memory);
+       cpu->set_context_io(io);
+       cpu->set_context_intr(pic);
+       if(bios) {
+               bios->set_context_mem(memory);
+               bios->set_context_io(io);
+               bios->set_cmos_ptr(cmos->get_cmos());
+               bios->set_vram_ptr(memory->get_vram());
+               bios->set_cvram_ptr(memory->get_cvram());
+#ifdef _FMR60
+               bios->set_avram_ptr(memory->get_avram());
+#else
+               bios->set_kvram_ptr(memory->get_kvram());
+#endif
+               cpu->set_context_bios(bios);
+       }
+#ifdef SINGLE_MODE_DMA
+       cpu->set_context_dma(dma);
+#endif
+#ifdef USE_DEBUGGER
+       cpu->set_context_debugger(new DEBUGGER(this, emu));
+#endif
+       
+       // i/o bus
+       io->set_iomap_alias_rw(0x00, pic, I8259_ADDR_CHIP0 | 0);
+       io->set_iomap_alias_rw(0x02, pic, I8259_ADDR_CHIP0 | 1);
+       io->set_iomap_alias_rw(0x10, pic, I8259_ADDR_CHIP1 | 0);
+       io->set_iomap_alias_rw(0x12, pic, I8259_ADDR_CHIP1 | 1);
+       io->set_iomap_single_rw(0x20, memory);  // reset
+       io->set_iomap_single_r(0x21, memory);   // cpu misc
+       io->set_iomap_single_w(0x22, memory);   // dma
+       io->set_iomap_single_rw(0x24, memory);  // dma
+       io->set_iomap_single_r(0x26, timer);
+       io->set_iomap_single_r(0x27, timer);
+       io->set_iomap_single_r(0x30, memory);   // cpu id
+       io->set_iomap_alias_rw(0x40, pit0, 0);
+       io->set_iomap_alias_rw(0x42, pit0, 1);
+       io->set_iomap_alias_rw(0x44, pit0, 2);
+       io->set_iomap_alias_rw(0x46, pit0, 3);
+       io->set_iomap_alias_rw(0x50, pit1, 0);
+       io->set_iomap_alias_rw(0x52, pit1, 1);
+       io->set_iomap_alias_rw(0x54, pit1, 2);
+       io->set_iomap_alias_rw(0x56, pit1, 3);
+       io->set_iomap_single_rw(0x60, timer);
+       io->set_iomap_single_rw(0x70, timer);
+       io->set_iomap_single_w(0x80, timer);
+#ifdef _FMRCARD
+       io->set_iomap_single_w(0x90, cmos);
+#endif
+       io->set_iomap_range_rw(0xa0, 0xaf, dma);
+       io->set_iomap_alias_rw(0x200, fdc, 0);
+       io->set_iomap_alias_rw(0x202, fdc, 1);
+       io->set_iomap_alias_rw(0x204, fdc, 2);
+       io->set_iomap_alias_rw(0x206, fdc, 3);
+       io->set_iomap_single_rw(0x208, floppy);
+       io->set_iomap_single_rw(0x20c, floppy);
+       io->set_iomap_single_rw(0x400, memory); // crtc
+       io->set_iomap_single_rw(0x402, memory); // crtc
+       io->set_iomap_single_rw(0x404, memory); // crtc
+       io->set_iomap_single_w(0x408, memory);  // crtc
+       io->set_iomap_single_rw(0x40a, memory); // crtc
+       io->set_iomap_single_rw(0x40c, memory); // crtc
+       io->set_iomap_single_rw(0x40e, memory); // crtc
+       io->set_iomap_alias_rw(0x500, crtc, 0);
+       io->set_iomap_alias_rw(0x502, crtc, 1);
+#ifdef _FMR60
+       io->set_iomap_range_rw(0x520, 0x523, acrtc);
+#endif
+       io->set_iomap_single_rw(0x600, keyboard);
+       io->set_iomap_single_rw(0x602, keyboard);
+       io->set_iomap_single_rw(0x604, keyboard);
+       io->set_iomap_alias_rw(0xa00, sio, 0);
+       io->set_iomap_alias_rw(0xa02, sio, 1);
+//     io->set_iomap_single_r(0xa04, serial);
+//     io->set_iomap_single_r(0xa06, serial);
+//     io->set_iomap_single_w(0xa08, serial);
+       io->set_iomap_single_rw(0xc30, scsi);
+       io->set_iomap_single_rw(0xc32, scsi);
+       io->set_iomap_range_rw(0x3000, 0x3fff, cmos);
+       io->set_iomap_range_rw(0xfd98, 0xfd9f, memory); // crtc
+       io->set_iomap_single_rw(0xfda0, memory);        // crtc
+       
+       // initialize all devices
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               device->initialize();
+       }
+       if(bios) {
+               for(int i = 0; i < MAX_DRIVE; i++) {
+                       bios->set_disk_handler(i, fdc->get_disk_handler(i));
+               }
+       }
+}
+
+VM::~VM()
+{
+       // delete all devices
+       for(DEVICE* device = first_device; device;) {
+               DEVICE *next_device = device->next_device;
+               device->release();
+               delete device;
+               device = next_device;
+       }
+}
+
+DEVICE* VM::get_device(int id)
+{
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               if(device->this_device_id == id) {
+                       return device;
+               }
+       }
+       return NULL;
+}
+
+// ----------------------------------------------------------------------------
+// drive virtual machine
+// ----------------------------------------------------------------------------
+
+void VM::reset()
+{
+       // reset all devices
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               device->reset();
+       }
+       // temporary fix...
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               device->reset();
+       }
+}
+
+void VM::run()
+{
+       event->drive();
+}
+
+// ----------------------------------------------------------------------------
+// debugger
+// ----------------------------------------------------------------------------
+
+#ifdef USE_DEBUGGER
+DEVICE *VM::get_cpu(int index)
+{
+       if(index == 0) {
+               return cpu;
+       }
+       return NULL;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// draw screen
+// ----------------------------------------------------------------------------
+
+void VM::draw_screen()
+{
+       memory->draw_screen();
+}
+
+uint32_t VM::get_access_lamp_status()
+{
+       uint32_t status_fdd = fdc->read_signal(0);
+       uint32_t status_hdd = scsi_host->read_signal(0);
+       if(bios) {
+               uint32_t status = bios->read_signal(0);
+               status_fdd |= status & 0x0f;
+               status_hdd |= status >> 4;
+       }
+       return (status_hdd) ? 4 : (status_fdd & (1 | 4)) ? 1 : (status_fdd & (2 | 8)) ? 2 : 0;
+}
+
+// ----------------------------------------------------------------------------
+// soud manager
+// ----------------------------------------------------------------------------
+
+void VM::initialize_sound(int rate, int samples)
+{
+       // init sound manager
+       event->initialize_sound(rate, samples);
+       
+       // init sound gen
+       pcm->initialize_sound(rate, 8000);
+}
+
+uint16_t* VM::create_sound(int* extra_frames)
+{
+       return event->create_sound(extra_frames);
+}
+
+int VM::get_sound_buffer_ptr()
+{
+       return event->get_sound_buffer_ptr();
+}
+
+#ifdef USE_SOUND_VOLUME
+void VM::set_sound_device_volume(int ch, int decibel_l, int decibel_r)
+{
+       if(ch == 0) {
+               pcm->set_volume(0, decibel_l, decibel_r);
+       }
+#if defined(USE_SOUND_FILES)
+       else if(ch == 1) {
+               fdc->set_volume(0, decibel_l, decibel_r);
+       }
+#endif
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// notify key
+// ----------------------------------------------------------------------------
+
+void VM::key_down(int code, bool repeat)
+{
+       keyboard->key_down(code);
+}
+
+void VM::key_up(int code)
+{
+       keyboard->key_up(code);
+}
+
+// ----------------------------------------------------------------------------
+// user interface
+// ----------------------------------------------------------------------------
+
+void VM::open_floppy_disk(int drv, const _TCHAR* file_path, int bank)
+{
+       fdc->open_disk(drv, file_path, bank);
+       floppy->change_disk(drv);
+}
+
+void VM::close_floppy_disk(int drv)
+{
+       fdc->close_disk(drv);
+}
+
+bool VM::is_floppy_disk_inserted(int drv)
+{
+       return fdc->is_disk_inserted(drv);
+}
+
+void VM::is_floppy_disk_protected(int drv, bool value)
+{
+       fdc->is_disk_protected(drv, value);
+}
+
+bool VM::is_floppy_disk_protected(int drv)
+{
+       return fdc->is_disk_protected(drv);
+}
+
+bool VM::is_frame_skippable()
+{
+       return event->is_frame_skippable();
+}
+
+void VM::update_config()
+{
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               device->update_config();
+       }
+}
+
+#define STATE_VERSION  3
+
+void VM::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               device->save_state(state_fio);
+       }
+}
+
+bool VM::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       for(DEVICE* device = first_device; device; device = device->next_device) {
+               if(!device->load_state(state_fio)) {
+                       return false;
+               }
+       }
+       return true;
+}
+
diff --git a/source/src/vm/fmtowns/fmtowns.h b/source/src/vm/fmtowns/fmtowns.h
new file mode 100644 (file)
index 0000000..6722d03
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.04.28 -
+
+       [ virtual machine ]
+*/
+
+#ifndef _FMR50_H_
+#define _FMR50_H_
+
+#if defined(_FMR50)
+#if defined(HAS_I286)
+#define DEVICE_NAME            "FUJITSU FMR-50 (i286)"
+#define CONFIG_NAME            "fmr50_i286"
+#elif defined(HAS_I386)
+#define DEVICE_NAME            "FUJITSU FMR-50 (i386)"
+#define CONFIG_NAME            "fmr50_i386"
+#elif defined(HAS_I486)
+#define DEVICE_NAME            "FUJITSU FMR-50 (i486)"
+#define CONFIG_NAME            "fmr50_i486"
+#elif defined(HAS_PENTIUM)
+#define DEVICE_NAME            "FUJITSU FMR-250"
+#define CONFIG_NAME            "fmr250"
+#endif
+#elif defined(_FMR60)
+#if defined(HAS_I286)
+#define DEVICE_NAME            "FUJITSU FMR-60"
+#define CONFIG_NAME            "fmr60"
+#elif defined(HAS_I386)
+#define DEVICE_NAME            "FUJITSU FMR-70"
+#define CONFIG_NAME            "fmr70"
+#elif defined(HAS_I486)
+#define DEVICE_NAME            "FUJITSU FMR-80"
+#define CONFIG_NAME            "fmr80"
+#elif defined(HAS_PENTIUM)
+#define DEVICE_NAME            "FUJITSU FMR-280"
+#define CONFIG_NAME            "fmr280"
+#endif
+#endif
+
+// device informations for virtual machine
+#define FRAMES_PER_SEC         55.4
+#if defined(_FMR60)
+#define LINES_PER_FRAME        784
+#define CHARS_PER_LINE         98
+#else
+#define LINES_PER_FRAME        440
+#define CHARS_PER_LINE         54
+#endif
+//#define CPU_CLOCKS           12000000
+#define CPU_CLOCKS             8000000
+#if defined(_FMR60)
+#define SCREEN_WIDTH           1120
+#define SCREEN_HEIGHT          750
+#define WINDOW_HEIGHT_ASPECT   840
+#else
+#define SCREEN_WIDTH           640
+#define SCREEN_HEIGHT          400
+#define WINDOW_HEIGHT_ASPECT   480
+#endif
+#define MAX_DRIVE              4
+#define MAX_SCSI               8
+#define MAX_MEMCARD            2
+#if defined(HAS_I286)
+#define I86_PSEUDO_BIOS
+#else
+#define I386_PSEUDO_BIOS
+#endif
+#define I8259_MAX_CHIPS                2
+#define SINGLE_MODE_DMA
+#define MB8877_NO_BUSY_AFTER_SEEK
+#define IO_ADDR_MAX            0x10000
+#define SCSI_HOST_AUTO_ACK
+
+// device informations for win32
+#define USE_CPU_TYPE           2
+#define USE_FD1
+#define USE_FD2
+#define USE_FD3
+#define USE_FD4
+#define NOTIFY_KEY_DOWN
+#define USE_SHIFT_NUMPAD_KEY
+#define USE_ALT_F10_KEY
+#define USE_AUTO_KEY           5
+#define USE_AUTO_KEY_RELEASE   6
+#define USE_CRT_FILTER
+#define USE_ACCESS_LAMP
+#define USE_SOUND_FILES                1
+#define USE_SOUND_FILES_FDD
+#if defined(USE_SOUND_FILES)
+#define USE_SOUND_VOLUME       2
+#else
+#define USE_SOUND_VOLUME       1
+#endif
+#define USE_DEBUGGER
+#define USE_STATE
+
+#include "../../common.h"
+#include "../../fileio.h"
+
+#ifdef USE_SOUND_VOLUME
+static const _TCHAR *sound_device_caption[] = {
+       _T("Beep"),
+#if defined(USE_SOUND_FILES)
+       _T("FDD SEEK"),
+#endif
+};
+#endif
+
+class EMU;
+class DEVICE;
+class EVENT;
+
+class HD46505;
+#ifdef _FMR60
+class HD63484;
+#endif
+class I8251;
+class I8253;
+class I8259;
+#if defined(HAS_I286)
+class I286;
+#else
+class I386;
+#endif
+class IO;
+class MB8877;
+class MSM58321;
+class PCM1BIT;
+class SCSI_HOST;
+class UPD71071;
+
+class BIOS;
+class CMOS;
+class FLOPPY;
+class KEYBOARD;
+class MEMORY;
+//class SERIAL;
+class SCSI;
+class TIMER;
+
+class VM
+{
+protected:
+       EMU* emu;
+       
+       // devices
+       EVENT* event;
+       
+       HD46505* crtc;
+#if defined(_FMR60)
+       HD63484* acrtc;
+#endif
+       I8251* sio;
+       I8253* pit0;
+       I8253* pit1;
+       I8259* pic;
+#if defined(HAS_I286)
+       I286* cpu;
+#else
+       I386* cpu;
+#endif
+       IO* io;
+       MB8877* fdc;
+       MSM58321* rtc;
+       PCM1BIT* pcm;
+       SCSI_HOST* scsi_host;
+       UPD71071* dma;
+       
+       BIOS* bios;
+       CMOS* cmos;
+       FLOPPY* floppy;
+       KEYBOARD* keyboard;
+       MEMORY* memory;
+       SCSI* scsi;
+//     SERIAL* serial;
+       TIMER* timer;
+       
+public:
+       // ----------------------------------------
+       // initialize
+       // ----------------------------------------
+       
+       VM(EMU* parent_emu);
+       ~VM();
+       
+       // ----------------------------------------
+       // for emulation class
+       // ----------------------------------------
+       
+       // drive virtual machine
+       void reset();
+       void run();
+       
+#ifdef USE_DEBUGGER
+       // debugger
+       DEVICE *get_cpu(int index);
+#endif
+       
+       // draw screen
+       void draw_screen();
+       uint32_t get_access_lamp_status();
+       
+       // sound generation
+       void initialize_sound(int rate, int samples);
+       uint16_t* create_sound(int* extra_frames);
+       int get_sound_buffer_ptr();
+#ifdef USE_SOUND_VOLUME
+       void set_sound_device_volume(int ch, int decibel_l, int decibel_r);
+#endif
+       
+       // notify key
+       void key_down(int code, bool repeat);
+       void key_up(int code);
+       
+       // user interface
+       void open_floppy_disk(int drv, const _TCHAR* file_path, int bank);
+       void close_floppy_disk(int drv);
+       bool is_floppy_disk_inserted(int drv);
+       void is_floppy_disk_protected(int drv, bool value);
+       bool is_floppy_disk_protected(int drv);
+       bool is_frame_skippable();
+       
+       void update_config();
+       void save_state(FILEIO* state_fio);
+       bool load_state(FILEIO* state_fio);
+       
+       // ----------------------------------------
+       // for each device
+       // ----------------------------------------
+       
+       // devices
+       DEVICE* get_device(int id);
+       DEVICE* dummy;
+       DEVICE* first_device;
+       DEVICE* last_device;
+};
+
+#endif
diff --git a/source/src/vm/fmtowns/keyboard.cpp b/source/src/vm/fmtowns/keyboard.cpp
new file mode 100644 (file)
index 0000000..68bbfa0
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.05.01 -
+
+       [ keyboard ]
+*/
+
+#include "keyboard.h"
+#include "../i8259.h"
+#include "../../fifo.h"
+
+void KEYBOARD::initialize()
+{
+       key_buf = new FIFO(64);
+       register_frame_event(this);
+}
+
+void KEYBOARD::release()
+{
+       key_buf->release();
+       delete key_buf;
+}
+
+void KEYBOARD::reset()
+{
+       memset(table, 0, sizeof(table));
+       key_buf->clear();
+       kbstat = kbdata = kbint = kbmsk = 0;
+}
+
+void KEYBOARD::write_io8(uint32_t addr, uint32_t data)
+{
+       switch(addr) {
+       case 0x600:
+               // data
+//             kbstat |= 2;
+               break;
+       case 0x602:
+               // command
+               break;
+       case 0x604:
+               kbmsk = data;
+               break;
+       }
+}
+
+uint32_t KEYBOARD::read_io8(uint32_t addr)
+{
+       switch(addr) {
+       case 0x600:
+               kbint &= ~1;
+               d_pic->write_signal(SIG_I8259_CHIP0 | SIG_I8259_IR1, 0, 0);
+               kbstat &= ~1;
+               return kbdata;
+       case 0x602:
+               return kbstat;
+       case 0x604:
+               return kbint | 0xfc;
+       }
+       return 0;
+}
+
+void KEYBOARD::event_frame()
+{
+       if(!(kbstat & 1) && !key_buf->empty()) {
+               kbstat |= 1;
+               kbdata = key_buf->read();
+       }
+       if((kbstat & 1) && (kbmsk & 1) && !(kbint & 1)) {
+               kbint |= 1;
+               d_pic->write_signal(SIG_I8259_CHIP0 | SIG_I8259_IR1, 1, 1);
+       }
+//     kbstat &= ~2;
+}
+
+void KEYBOARD::key_down(int code)
+{
+//     if(!table[code]) {
+               table[code] = 1;
+               if(code = key_table[code]) {
+                       // $11:CTRL, $10:SHIFT
+                       key_buf->write(0xa0 | (table[0x11] ? 8 : 0) | (table[0x10] ? 4 : 0));
+                       key_buf->write(code);
+               }
+//     }
+}
+
+void KEYBOARD::key_up(int code)
+{
+//     if(table[code]) {
+               table[code] = 0;
+               if(code = key_table[code]) {
+                       key_buf->write(0xb0);
+                       key_buf->write(code);
+               }
+//     }
+}
+
+#define STATE_VERSION  1
+
+void KEYBOARD::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       
+       key_buf->save_state((void *)state_fio);
+       state_fio->FputUint8(kbstat);
+       state_fio->FputUint8(kbdata);
+       state_fio->FputUint8(kbint);
+       state_fio->FputUint8(kbmsk);
+       state_fio->Fwrite(table, sizeof(table), 1);
+}
+
+bool KEYBOARD::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       if(state_fio->FgetInt32() != this_device_id) {
+               return false;
+       }
+       if(!key_buf->load_state((void *)state_fio)) {
+               return false;
+       }
+       kbstat = state_fio->FgetUint8();
+       kbdata = state_fio->FgetUint8();
+       kbint = state_fio->FgetUint8();
+       kbmsk = state_fio->FgetUint8();
+       state_fio->Fread(table, sizeof(table), 1);
+       return true;
+}
+
diff --git a/source/src/vm/fmtowns/keyboard.h b/source/src/vm/fmtowns/keyboard.h
new file mode 100644 (file)
index 0000000..5c77135
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.05.01 -
+
+       [ keyboard ]
+*/
+
+#ifndef _KEYBOARD_H_
+#define _KEYBOARD_H_
+
+#include "../vm.h"
+#include "../../emu.h"
+#include "../device.h"
+
+/*
+       \82Ð\82ç\82ª\82È/\83\8d\81[\83}\8e\9a       \82Ð\82ç\82ª\82È
+       \94¼\8ap/\91S\8ap               \94¼\8ap/\91S\8ap
+       \95Ï\8a·                    \95Ï\8a·
+       \96³\95Ï\8a·                  \96³\95Ï\8a·
+       \82©\82È/\8a¿\8e\9a
+       \83J\83^\83J\83i
+       \91O\8ds                    PgUp
+       \8e\9f\8ds                    PgDn
+       \8eÀ\8ds                    F12
+       \8eæ\8fÁ                    F11
+       COPY
+*/
+
+static const int key_table[256] = {
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x10,0x00,0x00,0x00,0x1D,0x00,0x00,
+       0x53,0x52,0x00,0x7C,0x55,0x52,0x00,0x00,0x00,0x71,0x00,0x01,0x58,0x57,0x00,0x00,
+       0x35,0x6E,0x70,0x00,0x4E,0x4F,0x4D,0x51,0x50,0x00,0x00,0x00,0x00,0x48,0x4B,0x00,
+       0x0B,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x1E,0x2E,0x2C,0x20,0x13,0x21,0x22,0x23,0x18,0x24,0x25,0x26,0x30,0x2F,0x19,
+       0x1A,0x11,0x14,0x1F,0x15,0x17,0x2D,0x12,0x2B,0x16,0x2A,0x00,0x00,0x00,0x00,0x00,
+       0x46,0x42,0x43,0x44,0x3E,0x3F,0x40,0x3A,0x3B,0x3C,0x36,0x38,0x00,0x39,0x47,0x37,
+       0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x72,0x73,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x7D,0x6B,0x6C,0x6D,0x57,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x27,0x31,0x0C,0x32,0x33,
+       0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x0E,0x29,0x0D,0x00,
+       0x00,0x00,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+       0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+};
+
+class FIFO;
+
+class KEYBOARD : public DEVICE
+{
+private:
+       DEVICE* d_pic;
+       
+       FIFO *key_buf;
+       uint8_t kbstat, kbdata, kbint, kbmsk;
+       uint8_t table[256];
+       
+public:
+       KEYBOARD(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) {}
+       ~KEYBOARD() {}
+       
+       // common functions
+       void initialize();
+       void release();
+       void reset();
+       void write_io8(uint32_t addr, uint32_t data);
+       uint32_t read_io8(uint32_t addr);
+       void event_frame();
+       void save_state(FILEIO* state_fio);
+       bool load_state(FILEIO* state_fio);
+       
+       // unique functions
+       void set_context_pic(DEVICE* device)
+       {
+               d_pic = device;
+       }
+       void key_down(int code);
+       void key_up(int code);
+};
+
+#endif
diff --git a/source/src/vm/fmtowns/memory.cpp b/source/src/vm/fmtowns/memory.cpp
new file mode 100644 (file)
index 0000000..45684ac
--- /dev/null
@@ -0,0 +1,1335 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.04.29 -
+
+       [ memory and crtc ]
+*/
+
+#include "memory.h"
+#if defined(HAS_I286)
+#include "../i286.h"
+#else
+#include "../i386.h"
+#endif
+
+static const uint8_t bios1[] = {
+       0xFA,                           // cli
+       0xDB,0xE3,                      // fninit
+       0xB8,0xA0,0xF7,                 // mov  ax,F7A0
+       0x8E,0xD0,                      // mov  ss,ax
+       0xBC,0x7E,0x05,                 // mov  sp,057E
+       // init i/o
+       0xB4,0x80,                      // mov  ah,80
+       0x9A,0x14,0x00,0xFB,0xFF,       // call far FFFB:0014
+       // boot from fdd
+       0xB4,0x81,                      // mov  ah,81
+       0x9A,0x14,0x00,0xFB,0xFF,       // call far FFFB:0014
+       0x73,0x0B,                      // jnb  $+11
+       0x74,0xF5,                      // jz   $-11
+       // boot from scsi-hdd
+       0xB4,0x82,                      // mov  ah,82
+       0x9A,0x14,0x00,0xFB,0xFF,       // call far FFFB:0014
+       0x72,0xEC,                      // jb   $-20
+       // goto ipl
+       0x9A,0x04,0x00,0x00,0xB0,       // call far B000:0004
+       0xEB,0xE7                       // jmp $-25
+};
+
+static const uint8_t bios2[] = {
+       0xEA,0x00,0x00,0x00,0xFC,       // jmp  FC00:0000
+       0x00,0x00,0x00,
+       0xcf                            // iret
+};
+
+#define SET_BANK(s, e, w, r) { \
+       int sb = (s) >> 11, eb = (e) >> 11; \
+       for(int i = sb; i <= eb; i++) { \
+               if((w) == wdmy) { \
+                       wbank[i] = wdmy; \
+               } else { \
+                       wbank[i] = (w) + 0x800 * (i - sb); \
+               } \
+               if((r) == rdmy) { \
+                       rbank[i] = rdmy; \
+               } else { \
+                       rbank[i] = (r) + 0x800 * (i - sb); \
+               } \
+       } \
+}
+
+void MEMORY::initialize()
+{
+       // init memory
+       memset(ram, 0, sizeof(ram));
+       memset(vram, 0, sizeof(vram));
+       memset(cvram, 0, sizeof(cvram));
+#ifdef _FMR60
+       memset(avram, 0, sizeof(avram));
+#else
+       memset(kvram, 0, sizeof(kvram));
+       memset(dummy, 0, sizeof(dummy));
+#endif
+       memset(ipl, 0xff, sizeof(ipl));
+#ifdef _FMR60
+       memset(ank24, 0xff, sizeof(ank24));
+       memset(kanji24, 0xff, sizeof(kanji24));
+#else
+       memset(ank8, 0xff, sizeof(ank8));
+       memset(ank16, 0xff, sizeof(ank16));
+       memset(kanji16, 0xff, sizeof(kanji16));
+#endif
+       memset(rdmy, 0xff, sizeof(rdmy));
+       
+       // load rom image
+       FILEIO* fio = new FILEIO();
+       if(fio->Fopen(create_local_path(_T("IPL.ROM")), FILEIO_READ_BINARY)) {
+               fio->Fread(ipl, sizeof(ipl), 1);
+               fio->Fclose();
+       } else {
+               // load pseudo ipl
+               memcpy(ipl + 0x0000, bios1, sizeof(bios1));
+               memcpy(ipl + 0x3ff0, bios2, sizeof(bios2));
+       }
+#ifdef _FMR60
+       if(fio->Fopen(create_local_path(_T("ANK24.ROM")), FILEIO_READ_BINARY)) {
+               fio->Fread(ank24, sizeof(ank24), 1);
+               fio->Fclose();
+       }
+       if(fio->Fopen(create_local_path(_T("KANJI24.ROM")), FILEIO_READ_BINARY)) {
+               fio->Fread(kanji24, sizeof(kanji24), 1);
+               fio->Fclose();
+       }
+#else
+       if(fio->Fopen(create_local_path(_T("ANK8.ROM")), FILEIO_READ_BINARY)) {
+               fio->Fread(ank8, sizeof(ank8), 1);
+               fio->Fclose();
+       }
+       if(fio->Fopen(create_local_path(_T("ANK16.ROM")), FILEIO_READ_BINARY)) {
+               fio->Fread(ank16, sizeof(ank16), 1);
+               fio->Fclose();
+       }
+       if(fio->Fopen(create_local_path(_T("KANJI16.ROM")), FILEIO_READ_BINARY)) {
+               fio->Fread(kanji16, sizeof(kanji16), 1);
+               fio->Fclose();
+       }
+#endif
+       delete fio;
+       
+       // set memory
+       SET_BANK(0x000000, 0xffffff, wdmy, rdmy);
+       SET_BANK(0x000000, sizeof(ram) - 1, ram, ram);
+#ifdef _FMR60
+       SET_BANK(0xff8000, 0xff9fff, cvram, cvram);
+       SET_BANK(0xffa000, 0xffbfff, avram, avram);
+#endif
+       SET_BANK(0xffc000, 0xffffff, wdmy, ipl);
+       
+       // set palette
+       for(int i = 0; i < 8; i++) {
+               dpal[i] = i;
+               apal[i][0] = (i & 1) ? 0xf0 : 0;
+               apal[i][1] = (i & 2) ? 0xf0 : 0;
+               apal[i][2] = (i & 4) ? 0xf0 : 0;
+       }
+       for(int i = 0; i < 16; i++) {
+               if(i & 8) {
+                       palette_cg[i] = RGB_COLOR(i & 2 ? 255 : 0, i & 4 ? 255 : 0, i & 1 ? 255 : 0);
+               } else {
+                       palette_cg[i] = RGB_COLOR(i & 2 ? 127 : 0, i & 4 ? 127 : 0, i & 1 ? 127 : 0);
+               }
+               palette_txt[i] = palette_cg[i];
+       }
+       palette_txt[0] = RGB_COLOR(63, 63, 63);
+       apalsel = 0;
+       
+       // register event
+       register_frame_event(this);
+}
+
+void MEMORY::reset()
+{
+       // reset memory
+       protect = rst = 0;
+       mainmem = rplane = wplane = 0;
+#ifndef _FMR60
+       pagesel = ankcg = 0;
+#endif
+       update_bank();
+       
+       // reset crtc
+       blink = 0;
+       apalsel = 0;
+       outctrl = 0xf;
+#ifndef _FMR60
+       dispctrl = 0x47;
+       mix = 8;
+       accaddr = dispaddr = 0;
+       kj_l = kj_h = kj_ofs = kj_row = 0;
+       
+       // reset logical operation
+       cmdreg = maskreg = compbit = bankdis = 0;
+       memset(compreg, sizeof(compreg), 0xff);
+#endif
+       dma_addr_reg = dma_wrap_reg = 0;
+       dma_addr_mask = 0x00ffffff;
+       d_cpu->set_address_mask(0x00ffffff);
+}
+
+void MEMORY::write_data8(uint32_t addr, uint32_t data)
+{
+       if(addr & 0xff000000) {
+               // > 16MB
+               return;
+       }
+       if(!mainmem) {
+#ifdef _FMR60
+               if(0xc0000 <= addr && addr < 0xe0000) {
+                       addr &= 0x1ffff;
+                       for(int pl = 0; pl < 4; pl++) {
+                               if(wplane & (1 << pl)) {
+                                       vram[addr + 0x20000 * pl] = data;
+                               }
+                       }
+               }
+#else
+               if(0xc0000 <= addr && addr < 0xc8000) {
+                       // vram
+                       uint32_t page;
+                       if(dispctrl & 0x40) {
+                               // 400 line
+                               addr = ((pagesel & 0x10) << 13) | (addr & 0x7fff);
+                               page = 0x8000;
+                       } else {
+                               // 200 line
+                               addr = ((pagesel & 0x18) << 13) | (addr & 0x3fff);
+                               page = 0x4000;
+                       }
+                       if(cmdreg & 0x80) {
+                               // logical operations
+                               if((cmdreg & 7) == 7) {
+                                       // compare
+                                       compbit = 0;
+                                       for(uint8_t bit = 1; bit <= 0x80; bit <<= 1) {
+                                               uint8_t val = 0;
+                                               for(int pl = 0; pl < 4; pl++) {
+                                                       if(vram[addr + page * pl] & bit) {
+                                                               val |= 1 << pl;
+                                                       }
+                                               }
+                                               for(int i = 0; i < 8; i++) {
+                                                       if((compreg[i] & 0x80) && (compreg[i] & 0xf) == val) {
+                                                               compbit |= bit;
+                                                       }
+                                               }
+                                       }
+                               } else {
+                                       uint8_t mask = maskreg | ~data, val[4];
+                                       for(int pl = 0; pl < 4; pl++) {
+                                               val[pl] = (imgcol & (1 << pl)) ? 0xff : 0;
+                                       }
+                                       switch(cmdreg & 7) {
+                                       case 2: // or
+                                               for(int pl = 0; pl < 4; pl++) {
+                                                       val[pl] |= vram[addr + page * pl];
+                                               }
+                                               break;
+                                       case 3: // and
+                                               for(int pl = 0; pl < 4; pl++) {
+                                                       val[pl] &= vram[addr + page * pl];
+                                               }
+                                               break;
+                                       case 4: // xor
+                                               for(int pl = 0; pl < 4; pl++) {
+                                                       val[pl] ^= vram[addr + page * pl];
+                                               }
+                                               break;
+                                       case 5: // not
+                                               for(int pl = 0; pl < 4; pl++) {
+                                                       val[pl] = ~vram[addr + page * pl];
+                                               }
+                                               break;
+                                       case 6: // tile
+                                               for(int pl = 0; pl < 4; pl++) {
+                                                       val[pl] = tilereg[pl];
+                                               }
+                                               break;
+                                       }
+                                       for(int pl = 0; pl < 4; pl++) {
+                                               if(!(bankdis & (1 << pl))) {
+                                                       vram[addr + page * pl] &= mask;
+                                                       vram[addr + page * pl] |= val[pl] & ~mask;
+                                               }
+                                       }
+                               }
+                       } else {
+                               for(int pl = 0; pl < 4; pl++) {
+                                       if(wplane & (1 << pl)) {
+                                               vram[addr + page * pl] = data;
+                                       }
+                               }
+                       }
+                       return;
+               } else if(0xcff80 <= addr && addr < 0xcffe0) {
+#ifdef _DEBUG_LOG
+//                     this->out_debug_log(_T("MW\t%4x, %2x\n"), addr, data);
+#endif
+                       // memory mapped i/o
+                       switch(addr & 0xffff) {
+                       case 0xff80:
+                               // mix register
+                               mix = data;
+                               break;
+                       case 0xff81:
+                               // update register
+                               wplane = data & 7;
+                               rplane = (data >> 6) & 3;
+                               update_bank();
+                               break;
+                       case 0xff82:
+                               // display ctrl register
+                               dispctrl = data;
+                               update_bank();
+                               break;
+                       case 0xff83:
+                               // page select register
+                               pagesel = data;
+                               update_bank();
+                               break;
+                       case 0xff88:
+                               // access start register
+                               accaddr = (accaddr & 0xff) | ((data & 0x7f) << 8);
+                               break;
+                       case 0xff89:
+                               // access start register
+                               accaddr = (accaddr & 0xff00) | (data & 0xfe);
+                               break;
+                       case 0xff8a:
+                               // display start register
+                               dispaddr = (dispaddr & 0xff) | ((data & 0x7f) << 8);
+                               break;
+                       case 0xff8b:
+                               // display start register
+                               dispaddr = (dispaddr & 0xff00) | (data & 0xfe);
+                               break;
+                       case 0xff8e:
+                               // crtc addr register
+                               d_crtc->write_io8(0, data);
+                               break;
+                       case 0xff8f:
+                               // crtc data register
+                               d_crtc->write_io8(1, data);
+                               break;
+                       case 0xff94:
+                               kj_h = data & 0x7f;
+                               break;
+                       case 0xff95:
+                               kj_l = data & 0x7f;
+                               kj_row = 0;
+                               if(kj_h < 0x30) {
+                                       kj_ofs = (((kj_l - 0x00) & 0x1f) <<  5) | (((kj_l - 0x20) & 0x20) <<  9) | (((kj_l - 0x20) & 0x40) <<  7) | (((kj_h - 0x00) & 0x07) << 10);
+                               } else if(kj_h < 0x70) {
+                                       kj_ofs = (((kj_l - 0x00) & 0x1f) <<  5) + (((kj_l - 0x20) & 0x60) <<  9) + (((kj_h - 0x00) & 0x0f) << 10) + (((kj_h - 0x30) & 0x70) * 0xc00) + 0x08000;
+                               } else {
+                                       kj_ofs = (((kj_l - 0x00) & 0x1f) <<  5) | (((kj_l - 0x20) & 0x20) <<  9) | (((kj_l - 0x20) & 0x40) <<  7) | (((kj_h - 0x00) & 0x07) << 10) | 0x38000;
+                               }
+                               break;
+                       case 0xff96:
+                               kanji16[(kj_ofs | ((kj_row & 0xf) << 1)) & 0x3ffff] = data;
+                               break;
+                       case 0xff97:
+                               kanji16[(kj_ofs | ((kj_row++ & 0xf) << 1) | 1) & 0x3ffff] = data;
+                               break;
+                       case 0xff99:
+                               ankcg = data;
+                               update_bank();
+                               break;
+                       case 0xffa0:
+                               cmdreg = data;
+                               break;
+                       case 0xffa1:
+                               imgcol = data;
+                               break;
+                       case 0xffa2:
+                               maskreg = data;
+                               break;
+                       case 0xffa3:
+                       case 0xffa4:
+                       case 0xffa5:
+                       case 0xffa6:
+                       case 0xffa7:
+                       case 0xffa8:
+                       case 0xffa9:
+                       case 0xffaa:
+                               compreg[addr & 7] = data;
+                               break;
+                       case 0xffab:
+                               bankdis = data;
+                               break;
+                       case 0xffac:
+                       case 0xffad:
+                       case 0xffae:
+                       case 0xffaf:
+                               tilereg[addr & 3] = data;
+                               break;
+                       case 0xffb0:
+                               lofs = (lofs & 0xff) | (data << 8);
+                               break;
+                       case 0xffb1:
+                               lofs = (lofs & 0xff00) | data;
+                               break;
+                       case 0xffb2:
+                               lsty = (lsty & 0xff) | (data << 8);
+                               break;
+                       case 0xffb3:
+                               lsty = (lsty & 0xff00) | data;
+                               break;
+                       case 0xffb4:
+                               lsx = (lsx & 0xff) | (data << 8);
+                               break;
+                       case 0xffb5:
+                               lsx = (lsx & 0xff00) | data;
+                               break;
+                       case 0xffb6:
+                               lsy = (lsy & 0xff) | (data << 8);
+                               break;
+                       case 0xffb7:
+                               lsy = (lsy & 0xff00) | data;
+                               break;
+                       case 0xffb8:
+                               lex = (lex & 0xff) | (data << 8);
+                               break;
+                       case 0xffb9:
+                               lex = (lex & 0xff00) | data;
+                               break;
+                       case 0xffba:
+                               ley = (ley & 0xff) | (data << 8);
+                               break;
+                       case 0xffbb:
+                               ley = (ley & 0xff00) | data;
+                               // start drawing line
+                               line();
+                               break;
+                       }
+                       return;
+               }
+#endif
+       }
+       if((addr & ~3) == 8 && (protect & 0x80)) {
+               return;
+       }
+       wbank[addr >> 11][addr & 0x7ff] = data;
+}
+
+uint32_t MEMORY::read_data8(uint32_t addr)
+{
+       if(addr & 0xff000000) {
+               // > 16MB
+               if(addr >= 0xffffc000) {
+                       return ipl[addr & 0x3fff];
+               }
+               return 0xff;
+       }
+#ifndef _FMR60
+       if(!mainmem) {
+               if(0xcff80 <= addr && addr < 0xcffe0) {
+#ifdef _DEBUG_LOG
+//                     this->out_debug_log(_T("MR\t%4x\n"), addr);
+#endif
+                       // memory mapped i/o
+                       switch(addr & 0xffff) {
+                       case 0xff80:
+                               // mix register
+                               return mix;
+                       case 0xff81:
+                               // update register
+                               return wplane | (rplane << 6);
+                       case 0xff83:
+                               // page select register
+                               return pagesel;
+                       case 0xff86:
+                               // status register
+                               return (disp ? 0x80 : 0) | (vsync ? 4 : 0) | 0x10;
+                       case 0xff8e:
+                               // crtc addr register
+                               return d_crtc->read_io8(0);
+                       case 0xff8f:
+                               // crtc data register
+                               return d_crtc->read_io8(1);
+                       case 0xff94:
+                               return 0x80;    // ???
+                       case 0xff96:
+                               return kanji16[(kj_ofs | ((kj_row & 0xf) << 1)) & 0x3ffff];
+                       case 0xff97:
+                               return kanji16[(kj_ofs | ((kj_row++ & 0xf) << 1) | 1) & 0x3ffff];
+                       case 0xffa0:
+                               return cmdreg;
+                       case 0xffa1:
+                               return imgcol | 0xf0;
+                       case 0xffa2:
+                               return maskreg;
+                       case 0xffa3:
+                               return compbit;
+                       case 0xffab:
+                               return bankdis & 0xf;
+                       }
+                       return 0xff;
+               }
+       }
+#endif
+       return rbank[addr >> 11][addr & 0x7ff];
+}
+
+void MEMORY::write_dma_data8(uint32_t addr, uint32_t data)
+{
+       write_data8(addr & dma_addr_mask, data);
+}
+
+uint32_t MEMORY::read_dma_data8(uint32_t addr)
+{
+       return read_data8(addr & dma_addr_mask);
+}
+
+void MEMORY::write_io8(uint32_t addr, uint32_t data)
+{
+       switch(addr & 0xffff) {
+       case 0x20:
+               // protect and reset
+               protect = data;
+               update_bank();
+               if(data & 0x40) {
+                       // power off
+                       emu->power_off();
+               }
+               if(data & 1) {
+                       // software reset
+                       rst |= 1;
+                       d_cpu->reset();
+               }
+               // protect mode
+#if defined(HAS_I286)
+               if(data & 0x20) {
+                       d_cpu->set_address_mask(0x00ffffff);
+               } else {
+                       d_cpu->set_address_mask(0x000fffff);
+               }
+#else
+               switch(data & 0x30) {
+               case 0x00:      // 20bit
+                       d_cpu->set_address_mask(0x000fffff);
+                       break;
+               case 0x20:      // 24bit
+                       d_cpu->set_address_mask(0x00ffffff);
+                       break;
+               default:        // 32bit
+                       d_cpu->set_address_mask(0xffffffff);
+                       break;
+               }
+#endif
+               update_dma_addr_mask();
+               break;
+       case 0x22:
+               dma_addr_reg = data;
+               update_dma_addr_mask();
+               break;
+       case 0x24:
+               dma_wrap_reg = data;
+               update_dma_addr_mask();
+               break;
+       case 0x400:
+               // video output control
+               break;
+       case 0x402:
+               // update register
+               wplane = data & 0xf;
+               break;
+       case 0x404:
+               // read out register
+               mainmem = data & 0x80;
+               rplane = data & 3;
+               update_bank();
+               break;
+       case 0x408:
+               // palette code register
+               apalsel = data & 0xf;
+               break;
+       case 0x40a:
+               // blue level register
+               apal[apalsel][0] = data & 0xf0;
+               palette_cg[apalsel] = RGB_COLOR(apal[apalsel][1], apal[apalsel][2], apal[apalsel][0]);
+               break;
+       case 0x40c:
+               // red level register
+               apal[apalsel][1] = data & 0xf0;
+               palette_cg[apalsel] = RGB_COLOR(apal[apalsel][1], apal[apalsel][2], apal[apalsel][0]);
+               break;
+       case 0x40e:
+               // green level register
+               apal[apalsel][2] = data & 0xf0;
+               palette_cg[apalsel] = RGB_COLOR(apal[apalsel][1], apal[apalsel][2], apal[apalsel][0]);
+               break;
+       case 0xfd98:
+       case 0xfd99:
+       case 0xfd9a:
+       case 0xfd9b:
+       case 0xfd9c:
+       case 0xfd9d:
+       case 0xfd9e:
+       case 0xfd9f:
+               // digital palette
+               dpal[addr & 7] = data;
+               if(data & 8) {
+                       palette_cg[addr & 7] = RGB_COLOR(data & 2 ? 255 : 0, data & 4 ? 255 : 0, data & 1 ? 255 : 0);
+               } else {
+                       palette_cg[addr & 7] = RGB_COLOR(data & 2 ? 127 : 0, data & 4 ? 127 : 0, data & 1 ? 127 : 0);
+               }
+               break;
+       case 0xfda0:
+               // video output control
+               outctrl = data;
+               break;
+       }
+}
+
+uint32_t MEMORY::read_io8(uint32_t addr)
+{
+       uint32_t val = 0xff;
+       
+       switch(addr & 0xffff) {
+       case 0x20:
+               // reset cause register
+               val = rst | (d_cpu->get_shutdown_flag() << 1);
+               rst = 0;
+               d_cpu->set_shutdown_flag(0);
+               return val | 0x7c;
+       case 0x21:
+//             return 0x1f;
+               return 0xdf;
+       case 0x24:
+               return dma_wrap_reg;
+       case 0x30:
+               // machine & cpu id
+               return machine_id;
+       case 0x400:
+               // system status register
+#ifdef _FMR60
+               return 0xff;
+#else
+               return 0xfe;
+//             return 0xf6;
+#endif
+       case 0x402:
+               // update register
+               return wplane | 0xf0;
+       case 0x404:
+               // read out register
+               return mainmem | rplane | 0x7c;
+       case 0x40a:
+               // blue level register
+               return apal[apalsel][0];
+       case 0x40c:
+               // red level register
+               return apal[apalsel][1];
+       case 0x40e:
+               // green level register
+               return apal[apalsel][2];
+       case 0xfd98:
+       case 0xfd99:
+       case 0xfd9a:
+       case 0xfd9b:
+       case 0xfd9c:
+       case 0xfd9d:
+       case 0xfd9e:
+       case 0xfd9f:
+               // digital palette
+               return dpal[addr & 7] | 0xf0;
+       case 0xfda0:
+               // status register
+               return (disp ? 2 : 0) | (vsync ? 1 : 0) | 0xfc;
+       }
+       return 0xff;
+}
+
+void MEMORY::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       if(id == SIG_MEMORY_DISP) {
+               disp = ((data & mask) != 0);
+       } else if(id == SIG_MEMORY_VSYNC) {
+               vsync = ((data & mask) != 0);
+       }
+}
+
+void MEMORY::event_frame()
+{
+       blink++;
+}
+
+void MEMORY::update_bank()
+{
+       if(!mainmem) {
+#ifdef _FMR60
+               int ofs = rplane * 0x20000;
+               SET_BANK(0xc0000, 0xdffff, vram + ofs, vram + ofs);
+               SET_BANK(0xe0000, 0xeffff, wdmy, rdmy);
+#else
+               if(dispctrl & 0x40) {
+                       // 400 line
+                       int ofs = (rplane | ((pagesel >> 2) & 4)) * 0x8000;
+                       SET_BANK(0xc0000, 0xc7fff, vram + ofs, vram + ofs);
+               } else {
+                       // 200 line
+                       int ofs = (rplane | ((pagesel >> 1) & 0xc)) * 0x4000;
+                       SET_BANK(0xc0000, 0xc3fff, vram + ofs, vram + ofs);
+                       SET_BANK(0xc4000, 0xc7fff, vram + ofs, vram + ofs);
+               }
+               SET_BANK(0xc8000, 0xc8fff, cvram, cvram);
+               SET_BANK(0xc9000, 0xc9fff, wdmy, rdmy);
+               if(ankcg & 1) {
+                       SET_BANK(0xca000, 0xca7ff, wdmy, ank8);
+                       SET_BANK(0xca800, 0xcafff, wdmy, rdmy);
+                       SET_BANK(0xcb000, 0xcbfff, wdmy, ank16);
+               } else {
+                       SET_BANK(0xca000, 0xcafff, kvram, kvram);
+                       SET_BANK(0xcb000, 0xcbfff, wdmy, rdmy);
+               }
+               SET_BANK(0xcc000, 0xeffff, wdmy, rdmy);
+#endif
+       } else {
+               SET_BANK(0xc0000, 0xeffff, ram + 0xc0000, ram + 0xc0000);
+       }
+       if(!(protect & 0x20)) {
+#ifdef _FMR60
+               SET_BANK(0xf8000, 0xf9fff, cvram, cvram);
+               SET_BANK(0xfa000, 0xfbfff, avram, avram);
+#else
+               SET_BANK(0xf8000, 0xfbfff, wdmy, rdmy);
+#endif
+               SET_BANK(0xfc000, 0xfffff, wdmy, ipl);
+       } else {
+               SET_BANK(0xf8000, 0xfffff, ram + 0xf8000, ram + 0xf8000);
+       }
+}
+
+void MEMORY::update_dma_addr_mask()
+{
+       switch(dma_addr_reg & 3) {
+       case 0:
+               dma_addr_mask = d_cpu->get_address_mask();
+               break;
+       case 1:
+               if(!(dma_wrap_reg & 1) && d_cpu->get_address_mask() == 0x000fffff) {
+                       dma_addr_mask = 0x000fffff;
+               } else {
+                       dma_addr_mask = 0x00ffffff;
+               }
+               break;
+       default:
+               if(!(dma_wrap_reg & 1) && d_cpu->get_address_mask() == 0x000fffff) {
+                       dma_addr_mask = 0x000fffff;
+               } else {
+                       dma_addr_mask = 0xffffffff;
+               }
+               break;
+       }
+}
+
+#ifndef _FMR60
+void MEMORY::point(int x, int y, int col)
+{
+       if(x < 640 && y < 400) {
+               int ofs = ((lofs & 0x3fff) + (x >> 3) + y * 80) & 0x7fff;
+               uint8_t bit = 0x80 >> (x & 7);
+               for(int pl = 0; pl < 4; pl++) {
+                       uint8_t pbit = 1 << pl;
+                       if(!(bankdis & pbit)) {
+                               if(col & pbit) {
+                                       vram[0x8000 * pl + ofs] |= bit;
+                               } else {
+                                       vram[0x8000 * pl + ofs] &= ~bit;
+                               }
+                       }
+               }
+       }
+}
+
+void MEMORY::line()
+{
+       int nx = lsx, ny = lsy;
+       int dx = abs(lex - lsx) * 2;
+       int dy = abs(ley - lsy) * 2;
+       int sx = (lex < lsx) ? -1 : 1;
+       int sy = (ley < lsy) ? -1 : 1;
+       int c = 0;
+       
+       point(lsx, lsy, (lsty & (0x8000 >> (c++ & 15))) ? imgcol : 0);
+       if(dx > dy) {
+               int frac = dy - dx / 2;
+               while(nx != lex) {
+                       if(frac >= 0) {
+                               ny += sy;
+                               frac -= dx;
+                       }
+                       nx += sx;
+                       frac += dy;
+                       point(nx, ny, (lsty & (0x8000 >> (c++ & 15))) ? imgcol : 0);
+               }
+       } else {
+               int frac = dx - dy / 2;
+               while(ny != ley) {
+                       if(frac >= 0) {
+                               nx += sx;
+                               frac -= dy;
+                       }
+                       ny += sy;
+                       frac += dx;
+                       point(nx, ny, (lsty & (0x8000 >> (c++ & 15))) ? imgcol : 0);
+               }
+       }
+//     point(lex, ley, (lsty & (0x8000 >> (c++ & 15))) ? imgcol : 0);
+}
+#endif
+
+void MEMORY::draw_screen()
+{
+       // render screen
+       memset(screen_txt, 0, sizeof(screen_txt));
+       memset(screen_cg, 0, sizeof(screen_cg));
+       if(outctrl & 1) {
+#ifdef _FMR60
+               draw_text();
+#else
+               if(mix & 8) {
+                       draw_text80();
+               } else {
+                       draw_text40();
+               }
+#endif
+       }
+       if(outctrl & 4) {
+               draw_cg();
+       }
+       
+       for(int y = 0; y < SCREEN_HEIGHT; y++) {
+               scrntype_t* dest = emu->get_screen_buffer(y);
+               uint8_t* txt = screen_txt[y];
+               uint8_t* cg = screen_cg[y];
+               
+               for(int x = 0; x < SCREEN_WIDTH; x++) {
+                       dest[x] = txt[x] ? palette_txt[txt[x] & 15] : palette_cg[cg[x]];
+               }
+       }
+       emu->screen_skip_line(false);
+}
+
+#ifdef _FMR60
+void MEMORY::draw_text()
+{
+       int src = ((chreg[12] << 9) | (chreg[13] << 1)) & 0x1fff;
+       int caddr = ((chreg[8] & 0xc0) == 0xc0) ? -1 : (((chreg[14] << 9) | (chreg[15] << 1)) & 0x1fff);
+       int ymax = (chreg[6] > 0) ? chreg[6] : 25;
+       int yofs = 750 / ymax;
+       
+       for(int y = 0; y < 25; y++) {
+               for(int x = 0; x < 80; x++) {
+                       bool cursor = ((src >> 1) == caddr);
+                       int cx = x;
+                       uint8_t codel = cvram[src];
+                       uint8_t attrl = avram[src];
+                       src = (src + 1) & 0x1ffff;
+                       uint8_t codeh = cvram[src];
+                       uint8_t attrh = avram[src];
+                       src = (src + 1) & 0x1ffff;
+                       uint8_t col = (attrl & 15) | 16;
+                       bool blnk = (attrh & 0x40) || ((blink & 32) && (attrh & 0x10));
+                       bool rev = ((attrh & 8) != 0);
+                       uint8_t xor_mask = (rev != blnk) ? 0xff : 0;
+                       
+                       if(codeh & 0x80) {
+                               // kanji
+                               int ofs = (codel | ((codeh & 0x7f) << 8)) * 72;
+                               for(int l = 3; l < 27 && l < yofs; l++) {
+                                       uint8_t pat0 = kanji24[ofs++] ^ xor_mask;
+                                       uint8_t pat1 = kanji24[ofs++] ^ xor_mask;
+                                       uint8_t pat2 = kanji24[ofs++] ^ xor_mask;
+                                       int yy = y * yofs + l;
+                                       if(yy >= 750) {
+                                               break;
+                                       }
+                                       uint8_t* d = &screen_txt[yy][x * 14];
+                                       
+                                       d[ 2] = (pat0 & 0x80) ? col : 0;
+                                       d[ 3] = (pat0 & 0x40) ? col : 0;
+                                       d[ 4] = (pat0 & 0x20) ? col : 0;
+                                       d[ 5] = (pat0 & 0x10) ? col : 0;
+                                       d[ 6] = (pat0 & 0x08) ? col : 0;
+                                       d[ 7] = (pat0 & 0x04) ? col : 0;
+                                       d[ 8] = (pat0 & 0x02) ? col : 0;
+                                       d[ 9] = (pat0 & 0x01) ? col : 0;
+                                       d[10] = (pat1 & 0x80) ? col : 0;
+                                       d[11] = (pat1 & 0x40) ? col : 0;
+                                       d[12] = (pat1 & 0x20) ? col : 0;
+                                       d[13] = (pat1 & 0x10) ? col : 0;
+                                       d[14] = (pat1 & 0x08) ? col : 0;
+                                       d[15] = (pat1 & 0x04) ? col : 0;
+                                       d[16] = (pat1 & 0x02) ? col : 0;
+                                       d[17] = (pat1 & 0x01) ? col : 0;
+                                       d[18] = (pat2 & 0x80) ? col : 0;
+                                       d[19] = (pat2 & 0x40) ? col : 0;
+                                       d[20] = (pat2 & 0x20) ? col : 0;
+                                       d[21] = (pat2 & 0x10) ? col : 0;
+                                       d[22] = (pat2 & 0x08) ? col : 0;
+                                       d[23] = (pat2 & 0x04) ? col : 0;
+                                       d[24] = (pat2 & 0x02) ? col : 0;
+                                       d[25] = (pat2 & 0x01) ? col : 0;
+                               }
+                               src = (src + 2) & 0x1fff;
+                               x++;
+                       } else if(codeh) {
+                       } else {
+                               // ank
+                               int ofs = codel * 48;
+                               for(int l = 3; l < 27 && l < yofs; l++) {
+                                       uint8_t pat0 = ank24[ofs++] ^ xor_mask;
+                                       uint8_t pat1 = ank24[ofs++] ^ xor_mask;
+                                       int yy = y * yofs + l;
+                                       if(yy >= 750) {
+                                               break;
+                                       }
+                                       uint8_t* d = &screen_txt[yy][x * 14];
+                                       
+                                       d[ 0] = (pat0 & 0x80) ? col : 0;
+                                       d[ 1] = (pat0 & 0x40) ? col : 0;
+                                       d[ 2] = (pat0 & 0x20) ? col : 0;
+                                       d[ 3] = (pat0 & 0x10) ? col : 0;
+                                       d[ 4] = (pat0 & 0x08) ? col : 0;
+                                       d[ 5] = (pat0 & 0x04) ? col : 0;
+                                       d[ 6] = (pat0 & 0x02) ? col : 0;
+                                       d[ 7] = (pat0 & 0x01) ? col : 0;
+                                       d[ 8] = (pat1 & 0x80) ? col : 0;
+                                       d[ 9] = (pat1 & 0x40) ? col : 0;
+                                       d[10] = (pat1 & 0x20) ? col : 0;
+                                       d[11] = (pat1 & 0x10) ? col : 0;
+                                       d[12] = (pat1 & 0x08) ? col : 0;
+                                       d[13] = (pat1 & 0x04) ? col : 0;
+                               }
+                       }
+/*
+                       if(cursor) {
+                               int bp = chreg[10] & 0x60;
+                               if(bp == 0 || (bp == 0x40 && (blink & 8)) || (bp == 0x60 && (blink & 0x10))) {
+                                       int st = chreg[10] & 15;
+                                       int ed = chreg[11] & 15;
+                                       for(int i = st; i < ed && i < yofs; i++) {
+                                               memset(&screen_txt[y * yofs + i][cx << 3], 7, 8);
+                                       }
+                               }
+                       }
+*/
+               }
+       }
+}
+#else
+void MEMORY::draw_text40()
+{
+       int src = ((chreg[12] << 9) | (chreg[13] << 1)) & 0xfff;
+       int caddr = ((chreg[8] & 0xc0) == 0xc0) ? -1 : (((chreg[14] << 9) | (chreg[15] << 1) | (mix & 0x20 ? 1 : 0)) & 0x7ff);
+       int ymax = (chreg[6] > 0) ? chreg[6] : 25;
+       int yofs = 400 / ymax;
+       
+       for(int y = 0; y < ymax; y++) {
+               for(int x = 0; x < 40; x++) {
+                       bool cursor = ((src >> 1) == caddr);
+                       int cx = x;
+                       uint8_t code = cvram[src];
+                       uint8_t h = kvram[src] & 0x7f;
+                       src = (src + 1) & 0xfff;
+                       uint8_t attr = cvram[src];
+                       uint8_t l = kvram[src] & 0x7f;
+                       src = (src + 1) & 0xfff;
+                       uint8_t col = ((attr & 0x20) >> 2) | (attr & 7) | 16;
+                       bool blnk = (blink & 32) && (attr & 0x10);
+                       bool rev = ((attr & 8) != 0);
+                       uint8_t xor_mask = (rev != blnk) ? 0xff : 0;
+                       
+                       if(attr & 0x40) {
+                               // kanji
+                               int ofs;
+                               if(h < 0x30) {
+                                       ofs = (((l - 0x00) & 0x1f) <<  5) | (((l - 0x20) & 0x20) <<  9) | (((l - 0x20) & 0x40) <<  7) | (((h - 0x00) & 0x07) << 10);
+                               } else if(h < 0x70) {
+                                       ofs = (((l - 0x00) & 0x1f) <<  5) + (((l - 0x20) & 0x60) <<  9) + (((h - 0x00) & 0x0f) << 10) + (((h - 0x30) & 0x70) * 0xc00) + 0x08000;
+                               } else {
+                                       ofs = (((l - 0x00) & 0x1f) <<  5) | (((l - 0x20) & 0x20) <<  9) | (((l - 0x20) & 0x40) <<  7) | (((h - 0x00) & 0x07) << 10) | 0x38000;
+                               }
+                               
+                               for(int l = 0; l < 16 && l < yofs; l++) {
+                                       uint8_t pat0 = kanji16[ofs + (l << 1) + 0] ^ xor_mask;
+                                       uint8_t pat1 = kanji16[ofs + (l << 1) + 1] ^ xor_mask;
+                                       int yy = y * yofs + l;
+                                       if(yy >= 400) {
+                                               break;
+                                       }
+                                       uint8_t* d = &screen_txt[yy][x << 4];
+                                       
+                                       d[ 0] = d[ 1] = (pat0 & 0x80) ? col : 0;
+                                       d[ 2] = d[ 3] = (pat0 & 0x40) ? col : 0;
+                                       d[ 4] = d[ 5] = (pat0 & 0x20) ? col : 0;
+                                       d[ 6] = d[ 7] = (pat0 & 0x10) ? col : 0;
+                                       d[ 8] = d[ 9] = (pat0 & 0x08) ? col : 0;
+                                       d[10] = d[11] = (pat0 & 0x04) ? col : 0;
+                                       d[12] = d[13] = (pat0 & 0x02) ? col : 0;
+                                       d[14] = d[15] = (pat0 & 0x01) ? col : 0;
+                                       d[16] = d[17] = (pat1 & 0x80) ? col : 0;
+                                       d[18] = d[19] = (pat1 & 0x40) ? col : 0;
+                                       d[20] = d[21] = (pat1 & 0x20) ? col : 0;
+                                       d[22] = d[23] = (pat1 & 0x10) ? col : 0;
+                                       d[24] = d[25] = (pat1 & 0x08) ? col : 0;
+                                       d[26] = d[27] = (pat1 & 0x04) ? col : 0;
+                                       d[28] = d[29] = (pat1 & 0x02) ? col : 0;
+                                       d[30] = d[31] = (pat1 & 0x01) ? col : 0;
+                               }
+                               src = (src + 2) & 0xfff;
+                               x++;
+                       } else {
+                               for(int l = 0; l < 16 && l < yofs; l++) {
+                                       uint8_t pat = ank16[(code << 4) + l] ^ xor_mask;
+                                       int yy = y * yofs + l;
+                                       if(yy >= 400) {
+                                               break;
+                                       }
+                                       uint8_t* d = &screen_txt[yy][x << 4];
+                                       
+                                       d[ 0] = d[ 1] = (pat & 0x80) ? col : 0;
+                                       d[ 2] = d[ 3] = (pat & 0x40) ? col : 0;
+                                       d[ 4] = d[ 5] = (pat & 0x20) ? col : 0;
+                                       d[ 6] = d[ 7] = (pat & 0x10) ? col : 0;
+                                       d[ 8] = d[ 9] = (pat & 0x08) ? col : 0;
+                                       d[10] = d[11] = (pat & 0x04) ? col : 0;
+                                       d[12] = d[13] = (pat & 0x02) ? col : 0;
+                                       d[14] = d[15] = (pat & 0x01) ? col : 0;
+                               }
+                       }
+                       if(cursor) {
+                               int bp = chreg[10] & 0x60;
+                               if(bp == 0 || (bp == 0x40 && (blink & 8)) || (bp == 0x60 && (blink & 0x10))) {
+                                       int st = chreg[10] & 15;
+                                       int ed = chreg[11] & 15;
+                                       for(int i = st; i < ed && i < yofs; i++) {
+                                               memset(&screen_txt[y * yofs + i][cx << 3], 7, 8);
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+void MEMORY::draw_text80()
+{
+       int src = ((chreg[12] << 9) | (chreg[13] << 1)) & 0xfff;
+       int caddr = ((chreg[8] & 0xc0) == 0xc0) ? -1 : (((chreg[14] << 9) | (chreg[15] << 1) | (mix & 0x20 ? 1 : 0)) & 0x7ff);
+       int ymax = (chreg[6] > 0) ? chreg[6] : 25;
+       int yofs = 400 / ymax;
+       
+       for(int y = 0; y < 25; y++) {
+               for(int x = 0; x < 80; x++) {
+                       bool cursor = ((src >> 1) == caddr);
+                       int cx = x;
+                       uint8_t code = cvram[src];
+                       uint8_t h = kvram[src] & 0x7f;
+                       src = (src + 1) & 0xfff;
+                       uint8_t attr = cvram[src];
+                       uint8_t l = kvram[src] & 0x7f;
+                       src = (src + 1) & 0xfff;
+                       uint8_t col = ((attr & 0x20) >> 2) | (attr & 7) | 16;
+                       bool blnk = (blink & 32) && (attr & 0x10);
+                       bool rev = ((attr & 8) != 0);
+                       uint8_t xor_mask = (rev != blnk) ? 0xff : 0;
+                       
+                       if(attr & 0x40) {
+                               // kanji
+                               int ofs;
+                               if(h < 0x30) {
+                                       ofs = (((l - 0x00) & 0x1f) <<  5) | (((l - 0x20) & 0x20) <<  9) | (((l - 0x20) & 0x40) <<  7) | (((h - 0x00) & 0x07) << 10);
+                               } else if(h < 0x70) {
+                                       ofs = (((l - 0x00) & 0x1f) <<  5) + (((l - 0x20) & 0x60) <<  9) + (((h - 0x00) & 0x0f) << 10) + (((h - 0x30) & 0x70) * 0xc00) + 0x08000;
+                               } else {
+                                       ofs = (((l - 0x00) & 0x1f) <<  5) | (((l - 0x20) & 0x20) <<  9) | (((l - 0x20) & 0x40) <<  7) | (((h - 0x00) & 0x07) << 10) | 0x38000;
+                               }
+                               
+                               for(int l = 0; l < 16 && l < yofs; l++) {
+                                       uint8_t pat0 = kanji16[ofs + (l << 1) + 0] ^ xor_mask;
+                                       uint8_t pat1 = kanji16[ofs + (l << 1) + 1] ^ xor_mask;
+                                       int yy = y * yofs + l;
+                                       if(yy >= 400) {
+                                               break;
+                                       }
+                                       uint8_t* d = &screen_txt[yy][x << 3];
+                                       
+                                       d[ 0] = (pat0 & 0x80) ? col : 0;
+                                       d[ 1] = (pat0 & 0x40) ? col : 0;
+                                       d[ 2] = (pat0 & 0x20) ? col : 0;
+                                       d[ 3] = (pat0 & 0x10) ? col : 0;
+                                       d[ 4] = (pat0 & 0x08) ? col : 0;
+                                       d[ 5] = (pat0 & 0x04) ? col : 0;
+                                       d[ 6] = (pat0 & 0x02) ? col : 0;
+                                       d[ 7] = (pat0 & 0x01) ? col : 0;
+                                       d[ 8] = (pat1 & 0x80) ? col : 0;
+                                       d[ 9] = (pat1 & 0x40) ? col : 0;
+                                       d[10] = (pat1 & 0x20) ? col : 0;
+                                       d[11] = (pat1 & 0x10) ? col : 0;
+                                       d[12] = (pat1 & 0x08) ? col : 0;
+                                       d[13] = (pat1 & 0x04) ? col : 0;
+                                       d[14] = (pat1 & 0x02) ? col : 0;
+                                       d[15] = (pat1 & 0x01) ? col : 0;
+                               }
+                               src = (src + 2) & 0xfff;
+                               x++;
+                       } else {
+                               for(int l = 0; l < 16 && l < yofs; l++) {
+                                       uint8_t pat = ank16[(code << 4) + l] ^ xor_mask;
+                                       int yy = y * yofs + l;
+                                       if(yy >= 400) {
+                                               break;
+                                       }
+                                       uint8_t* d = &screen_txt[yy][x << 3];
+                                       
+                                       d[0] = (pat & 0x80) ? col : 0;
+                                       d[1] = (pat & 0x40) ? col : 0;
+                                       d[2] = (pat & 0x20) ? col : 0;
+                                       d[3] = (pat & 0x10) ? col : 0;
+                                       d[4] = (pat & 0x08) ? col : 0;
+                                       d[5] = (pat & 0x04) ? col : 0;
+                                       d[6] = (pat & 0x02) ? col : 0;
+                                       d[7] = (pat & 0x01) ? col : 0;
+                               }
+                       }
+                       if(cursor) {
+                               int bp = chreg[10] & 0x60;
+                               if(bp == 0 || (bp == 0x40 && (blink & 8)) || (bp == 0x60 && (blink & 0x10))) {
+                                       int st = chreg[10] & 15;
+                                       int ed = chreg[11] & 15;
+                                       for(int i = st; i < ed && i < yofs; i++) {
+                                               memset(&screen_txt[y * yofs + i][cx << 3], 7, 8);
+                                       }
+                               }
+                       }
+               }
+       }
+}
+#endif
+
+void MEMORY::draw_cg()
+{
+#ifdef _FMR60
+       uint8_t* p0 = &vram[0x00000];
+       uint8_t* p1 = &vram[0x20000];
+       uint8_t* p2 = &vram[0x40000];
+       uint8_t* p3 = &vram[0x60000];
+       int ptr = 0;
+       
+       for(int y = 0; y < 750; y++) {
+               for(int x = 0; x < 1120; x += 8) {
+                       uint8_t r = p0[ptr];
+                       uint8_t g = p1[ptr];
+                       uint8_t b = p2[ptr];
+                       uint8_t i = p3[ptr++];
+                       ptr &= 0x1ffff;
+                       uint8_t* d = &screen_cg[y][x];
+                       
+                       d[0] = ((r & 0x80) >> 7) | ((g & 0x80) >> 6) | ((b & 0x80) >> 5) | ((i & 0x80) >> 4);
+                       d[1] = ((r & 0x40) >> 6) | ((g & 0x40) >> 5) | ((b & 0x40) >> 4) | ((i & 0x40) >> 3);
+                       d[2] = ((r & 0x20) >> 5) | ((g & 0x20) >> 4) | ((b & 0x20) >> 3) | ((i & 0x20) >> 2);
+                       d[3] = ((r & 0x10) >> 4) | ((g & 0x10) >> 3) | ((b & 0x10) >> 2) | ((i & 0x10) >> 1);
+                       d[4] = ((r & 0x08) >> 3) | ((g & 0x08) >> 2) | ((b & 0x08) >> 1) | ((i & 0x08) >> 0);
+                       d[5] = ((r & 0x04) >> 2) | ((g & 0x04) >> 1) | ((b & 0x04) >> 0) | ((i & 0x04) << 1);
+                       d[6] = ((r & 0x02) >> 1) | ((g & 0x02) >> 0) | ((b & 0x02) << 1) | ((i & 0x02) << 2);
+                       d[7] = ((r & 0x01) >> 0) | ((g & 0x01) << 1) | ((b & 0x01) << 2) | ((i & 0x01) << 3);
+               }
+       }
+#else
+       if(dispctrl & 0x40) {
+               // 400line
+               int pofs = ((dispctrl >> 3) & 1) * 0x20000;
+               uint8_t* p0 = (dispctrl & 0x01) ? &vram[pofs | 0x00000] : dummy;
+               uint8_t* p1 = (dispctrl & 0x02) ? &vram[pofs | 0x08000] : dummy;
+               uint8_t* p2 = (dispctrl & 0x04) ? &vram[pofs | 0x10000] : dummy;
+               uint8_t* p3 = (dispctrl & 0x20) ? &vram[pofs | 0x18000] : dummy;        // ???
+               int ptr = dispaddr & 0x7ffe;
+               
+               for(int y = 0; y < 400; y++) {
+                       for(int x = 0; x < 640; x += 8) {
+                               uint8_t r = p0[ptr];
+                               uint8_t g = p1[ptr];
+                               uint8_t b = p2[ptr];
+                               uint8_t i = p3[ptr++];
+                               ptr &= 0x7fff;
+                               uint8_t* d = &screen_cg[y][x];
+                               
+                               d[0] = ((r & 0x80) >> 7) | ((g & 0x80) >> 6) | ((b & 0x80) >> 5) | ((i & 0x80) >> 4);
+                               d[1] = ((r & 0x40) >> 6) | ((g & 0x40) >> 5) | ((b & 0x40) >> 4) | ((i & 0x40) >> 3);
+                               d[2] = ((r & 0x20) >> 5) | ((g & 0x20) >> 4) | ((b & 0x20) >> 3) | ((i & 0x20) >> 2);
+                               d[3] = ((r & 0x10) >> 4) | ((g & 0x10) >> 3) | ((b & 0x10) >> 2) | ((i & 0x10) >> 1);
+                               d[4] = ((r & 0x08) >> 3) | ((g & 0x08) >> 2) | ((b & 0x08) >> 1) | ((i & 0x08) >> 0);
+                               d[5] = ((r & 0x04) >> 2) | ((g & 0x04) >> 1) | ((b & 0x04) >> 0) | ((i & 0x04) << 1);
+                               d[6] = ((r & 0x02) >> 1) | ((g & 0x02) >> 0) | ((b & 0x02) << 1) | ((i & 0x02) << 2);
+                               d[7] = ((r & 0x01) >> 0) | ((g & 0x01) << 1) | ((b & 0x01) << 2) | ((i & 0x01) << 3);
+                       }
+               }
+       } else {
+               // 200line
+               int pofs = ((dispctrl >> 3) & 3) * 0x10000;
+               uint8_t* p0 = (dispctrl & 0x01) ? &vram[pofs | 0x0000] : dummy;
+               uint8_t* p1 = (dispctrl & 0x02) ? &vram[pofs | 0x4000] : dummy;
+               uint8_t* p2 = (dispctrl & 0x04) ? &vram[pofs | 0x8000] : dummy;
+               uint8_t* p3 = (dispctrl & 0x20) ? &vram[pofs | 0xc000] : dummy; // ???
+               int ptr = dispaddr & 0x3ffe;
+               
+               for(int y = 0; y < 400; y += 2) {
+                       for(int x = 0; x < 640; x += 8) {
+                               uint8_t r = p0[ptr];
+                               uint8_t g = p1[ptr];
+                               uint8_t b = p2[ptr];
+                               uint8_t i = p3[ptr++];
+                               ptr &= 0x3fff;
+                               uint8_t* d = &screen_cg[y][x];
+                               
+                               d[0] = ((r & 0x80) >> 7) | ((g & 0x80) >> 6) | ((b & 0x80) >> 5) | ((i & 0x80) >> 4);
+                               d[1] = ((r & 0x40) >> 6) | ((g & 0x40) >> 5) | ((b & 0x40) >> 4) | ((i & 0x40) >> 3);
+                               d[2] = ((r & 0x20) >> 5) | ((g & 0x20) >> 4) | ((b & 0x20) >> 3) | ((i & 0x20) >> 2);
+                               d[3] = ((r & 0x10) >> 4) | ((g & 0x10) >> 3) | ((b & 0x10) >> 2) | ((i & 0x10) >> 1);
+                               d[4] = ((r & 0x08) >> 3) | ((g & 0x08) >> 2) | ((b & 0x08) >> 1) | ((i & 0x08) >> 0);
+                               d[5] = ((r & 0x04) >> 2) | ((g & 0x04) >> 1) | ((b & 0x04) >> 0) | ((i & 0x04) << 1);
+                               d[6] = ((r & 0x02) >> 1) | ((g & 0x02) >> 0) | ((b & 0x02) << 1) | ((i & 0x02) << 2);
+                               d[7] = ((r & 0x01) >> 0) | ((g & 0x01) << 1) | ((b & 0x01) << 2) | ((i & 0x01) << 3);
+                       }
+                       memcpy(screen_cg[y + 1], screen_cg[y], 640);
+               }
+       }
+#endif
+}
+
+#define STATE_VERSION  1
+
+void MEMORY::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       
+       state_fio->Fwrite(ram, sizeof(ram), 1);
+       state_fio->Fwrite(vram, sizeof(vram), 1);
+       state_fio->Fwrite(cvram, sizeof(cvram), 1);
+#ifdef _FMR60
+       state_fio->Fwrite(avram, sizeof(avram), 1);
+#else
+       state_fio->Fwrite(kvram, sizeof(kvram), 1);
+#endif
+       state_fio->FputUint8(machine_id);
+       state_fio->FputUint8(protect);
+       state_fio->FputUint8(rst);
+       state_fio->FputUint8(mainmem);
+       state_fio->FputUint8(rplane);
+       state_fio->FputUint8(wplane);
+       state_fio->FputUint8(dma_addr_reg);
+       state_fio->FputUint8(dma_wrap_reg);
+       state_fio->FputUint32(dma_addr_mask);
+       state_fio->FputBool(disp);
+       state_fio->FputBool(vsync);
+       state_fio->FputInt32(blink);
+       state_fio->Fwrite(apal, sizeof(apal), 1);
+       state_fio->FputUint8(apalsel);
+       state_fio->Fwrite(dpal, sizeof(dpal), 1);
+       state_fio->FputUint8(outctrl);
+#ifndef _FMR60
+       state_fio->FputUint8(pagesel);
+       state_fio->FputUint8(ankcg);
+       state_fio->FputUint8(dispctrl);
+       state_fio->FputUint8(mix);
+       state_fio->FputUint16(accaddr);
+       state_fio->FputUint16(dispaddr);
+       state_fio->FputInt32(kj_h);
+       state_fio->FputInt32(kj_l);
+       state_fio->FputInt32(kj_ofs);
+       state_fio->FputInt32(kj_row);
+       state_fio->FputUint8(cmdreg);
+       state_fio->FputUint8(imgcol);
+       state_fio->FputUint8(maskreg);
+       state_fio->Fwrite(compreg, sizeof(compreg), 1);
+       state_fio->FputUint8(compbit);
+       state_fio->FputUint8(bankdis);
+       state_fio->Fwrite(tilereg, sizeof(tilereg), 1);
+       state_fio->FputUint16(lofs);
+       state_fio->FputUint16(lsty);
+       state_fio->FputUint16(lsx);
+       state_fio->FputUint16(lsy);
+       state_fio->FputUint16(lex);
+       state_fio->FputUint16(ley);
+#endif
+       state_fio->Fwrite(palette_cg, sizeof(palette_cg), 1);
+}
+
+bool MEMORY::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       if(state_fio->FgetInt32() != this_device_id) {
+               return false;
+       }
+       state_fio->Fread(ram, sizeof(ram), 1);
+       state_fio->Fread(vram, sizeof(vram), 1);
+       state_fio->Fread(cvram, sizeof(cvram), 1);
+#ifdef _FMR60
+       state_fio->Fread(avram, sizeof(avram), 1);
+#else
+       state_fio->Fread(kvram, sizeof(kvram), 1);
+#endif
+       machine_id = state_fio->FgetUint8();
+       protect = state_fio->FgetUint8();
+       rst = state_fio->FgetUint8();
+       mainmem = state_fio->FgetUint8();
+       rplane = state_fio->FgetUint8();
+       wplane = state_fio->FgetUint8();
+       dma_addr_reg = state_fio->FgetUint8();
+       dma_wrap_reg = state_fio->FgetUint8();
+       dma_addr_mask = state_fio->FgetUint32();
+       disp = state_fio->FgetBool();
+       vsync = state_fio->FgetBool();
+       blink = state_fio->FgetInt32();
+       state_fio->Fread(apal, sizeof(apal), 1);
+       apalsel = state_fio->FgetUint8();
+       state_fio->Fread(dpal, sizeof(dpal), 1);
+       outctrl = state_fio->FgetUint8();
+#ifndef _FMR60
+       pagesel = state_fio->FgetUint8();
+       ankcg = state_fio->FgetUint8();
+       dispctrl = state_fio->FgetUint8();
+       mix = state_fio->FgetUint8();
+       accaddr = state_fio->FgetUint16();
+       dispaddr = state_fio->FgetUint16();
+       kj_h = state_fio->FgetInt32();
+       kj_l = state_fio->FgetInt32();
+       kj_ofs = state_fio->FgetInt32();
+       kj_row = state_fio->FgetInt32();
+       cmdreg = state_fio->FgetUint8();
+       imgcol = state_fio->FgetUint8();
+       maskreg = state_fio->FgetUint8();
+       state_fio->Fread(compreg, sizeof(compreg), 1);
+       compbit = state_fio->FgetUint8();
+       bankdis = state_fio->FgetUint8();
+       state_fio->Fread(tilereg, sizeof(tilereg), 1);
+       lofs = state_fio->FgetUint16();
+       lsty = state_fio->FgetUint16();
+       lsx = state_fio->FgetUint16();
+       lsy = state_fio->FgetUint16();
+       lex = state_fio->FgetUint16();
+       ley = state_fio->FgetUint16();
+#endif
+       state_fio->Fread(palette_cg, sizeof(palette_cg), 1);
+       
+       // post process
+       update_bank();
+       return true;
+}
+
diff --git a/source/src/vm/fmtowns/memory.h b/source/src/vm/fmtowns/memory.h
new file mode 100644 (file)
index 0000000..1b94fc8
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.04.29 -
+
+       [ memory ]
+*/
+
+#ifndef _MEMORY_H_
+#define _MEMORY_H_
+
+#include "../vm.h"
+#include "../../emu.h"
+#include "../device.h"
+
+#define SIG_MEMORY_DISP                0
+#define SIG_MEMORY_VSYNC       1
+
+#if defined(HAS_I286)
+class I286;
+#else
+class I386;
+#endif
+
+class MEMORY : public DEVICE
+{
+private:
+#if defined(HAS_I286)
+       I286 *d_cpu;
+#else
+       I386 *d_cpu;
+#endif
+       DEVICE *d_crtc;
+       
+       uint8_t* rbank[8192];   // 16MB / 2KB
+       uint8_t* wbank[8192];
+       uint8_t wdmy[0x800];
+       uint8_t rdmy[0x800];
+       
+       uint8_t ram[0x400000];  // RAM 1+3MB
+#ifdef _FMR60
+       uint8_t vram[0x80000];  // VRAM 512KB
+       uint8_t cvram[0x2000];
+       uint8_t avram[0x2000];
+#else
+       uint8_t vram[0x40000];  // VRAM 256KB
+       uint8_t cvram[0x1000];
+       uint8_t kvram[0x1000];
+       uint8_t dummy[0x8000];  // dummy plane
+#endif
+       uint8_t ipl[0x4000];    // IPL 16KB
+#ifdef _FMR60
+       uint8_t ank24[0x3000];          // ANK(14x24)
+       uint8_t kanji24[0x240000];      // KANJI(24x24)
+#else
+       uint8_t ank8[0x800];    // ANK(8x8) 2KB
+       uint8_t ank16[0x1000];  // ANK(8x16) 4KB
+       uint8_t kanji16[0x40000];       // KANJI(16x16) 256KB
+#endif
+       uint8_t machine_id;     // MACHINE ID
+       
+       // memory
+       uint8_t protect, rst;
+       uint8_t mainmem, rplane, wplane;
+       uint8_t dma_addr_reg, dma_wrap_reg;
+       uint32_t dma_addr_mask;
+       
+       // crtc
+       uint8_t* chreg;
+       bool disp, vsync;
+       int blink;
+       
+       // video
+       uint8_t apal[16][3], apalsel, dpal[8];
+       uint8_t outctrl;
+       
+#ifndef _FMR60
+       // 16bit card
+       uint8_t pagesel, ankcg;
+       uint8_t dispctrl;
+       uint8_t mix;
+       uint16_t accaddr, dispaddr;
+       
+       // kanji
+       int kj_h, kj_l, kj_ofs, kj_row;
+       
+       // logical operation
+       uint8_t cmdreg, imgcol, maskreg, compreg[8], compbit, bankdis, tilereg[3];
+       uint16_t lofs, lsty, lsx, lsy, lex, ley;
+       void point(int x, int y, int col);
+       void line();
+#endif
+       
+       uint8_t screen_txt[SCREEN_HEIGHT][SCREEN_WIDTH + 14];
+       uint8_t screen_cg[SCREEN_HEIGHT][SCREEN_WIDTH];
+//     uint8_t screen_txt[400][648];
+//     uint8_t screen_cg[400][640];
+       scrntype_t palette_txt[16];
+       scrntype_t palette_cg[16];
+       
+       void update_bank();
+       void update_dma_addr_mask();
+#ifdef _FMR60
+       void draw_text();
+#else
+       void draw_text40();
+       void draw_text80();
+#endif
+       void draw_cg();
+       
+public:
+       MEMORY(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) {
+               set_device_name(_T("MEMORY"));
+       }
+       ~MEMORY() {}
+       
+       // common functions
+       void initialize();
+       void reset();
+       void write_data8(uint32_t addr, uint32_t data);
+       uint32_t read_data8(uint32_t addr);
+       void write_dma_data8(uint32_t addr, uint32_t data);
+       uint32_t read_dma_data8(uint32_t addr);
+       void write_io8(uint32_t addr, uint32_t data);
+       uint32_t read_io8(uint32_t addr);
+       void write_signal(int id, uint32_t data, uint32_t mask);
+       void event_frame();
+       void save_state(FILEIO* state_fio);
+       bool load_state(FILEIO* state_fio);
+       
+       // unique functions
+#if defined(HAS_I286)
+       void set_context_cpu(I286* device)
+#else
+       void set_context_cpu(I386* device)
+#endif
+       {
+               d_cpu = device;
+       }
+       void set_machine_id(uint8_t id)
+       {
+               machine_id = id;
+       }
+       void set_context_crtc(DEVICE* device)
+       {
+               d_crtc = device;
+       }
+       void set_chregs_ptr(uint8_t* ptr)
+       {
+               chreg = ptr;
+       }
+       uint8_t* get_vram()
+       {
+               return vram;
+       }
+       uint8_t* get_cvram()
+       {
+               return cvram;
+       }
+#ifdef _FMR60
+       uint8_t* get_avram()
+       {
+               return avram;
+       }
+#else
+       uint8_t* get_kvram()
+       {
+               return kvram;
+       }
+#endif
+       void draw_screen();
+};
+
+#endif
+
diff --git a/source/src/vm/fmtowns/scsi.cpp b/source/src/vm/fmtowns/scsi.cpp
new file mode 100644 (file)
index 0000000..7d07d17
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2016.03.03-
+
+       [ scsi ]
+*/
+
+#include "scsi.h"
+#include "../i8259.h"
+#include "../scsi_host.h"
+#include "../upd71071.h"
+
+// control register
+#define CTRL_WEN       0x80
+#define CTRL_IMSK      0x40
+#define CTRL_ATN       0x10
+#define CTRL_SEL       0x04
+#define CTRL_DMAE      0x02
+#define CTRL_RST       0x01
+
+#define STATUS_REQ     0x80
+#define STATUS_IO      0x40
+#define STATUS_MSG     0x20
+#define STATUS_CD      0x10
+#define STATUS_BSY     0x08
+#define STATUS_INT     0x02
+#define STATUS_PERR    0x01
+
+void SCSI::reset()
+{
+       ctrl_reg = CTRL_IMSK;
+       irq_status = false;
+}
+
+void SCSI::write_io8(uint32_t addr, uint32_t data)
+{
+       switch(addr & 0xffff) {
+       case 0xc30:
+               // data register
+               #ifdef _SCSI_DEBUG_LOG
+                       this->out_debug_log(_T("[SCSI] out %04X %02X\n"), addr, data);
+               #endif
+               if(ctrl_reg & CTRL_WEN) {
+                       d_host->write_dma_io8(addr, data);
+               }
+               break;
+               
+       case 0xc32:
+               // control register
+               #ifdef _SCSI_DEBUG_LOG
+                       this->out_debug_log(_T("[SCSI] out %04X %02X\n"), addr, data);
+               #endif
+               ctrl_reg = data;
+               if(ctrl_reg  & CTRL_WEN) {
+                       d_host->write_signal(SIG_SCSI_RST, data, CTRL_RST);
+                       d_host->write_signal(SIG_SCSI_SEL, data, CTRL_SEL);
+                       d_host->write_signal(SIG_SCSI_ATN, data, CTRL_ATN);
+               }
+               break;
+       }
+}
+
+uint32_t SCSI::read_io8(uint32_t addr)
+{
+       uint32_t value = 0;
+       
+       switch(addr & 0xffff) {
+       case 0xc30:
+               // data register
+               if(ctrl_reg & CTRL_WEN) {
+                       value = d_host->read_dma_io8(addr);
+               }
+               #ifdef _SCSI_DEBUG_LOG
+                       this->out_debug_log(_T("[SCSI] in  %04X %02X\n"), addr, value);
+               #endif
+               return value;
+               
+       case 0xc32:
+               // status register
+               value = (d_host->read_signal(SIG_SCSI_REQ) ? STATUS_REQ : 0) |
+                       (d_host->read_signal(SIG_SCSI_IO ) ? STATUS_IO  : 0) |
+                       (d_host->read_signal(SIG_SCSI_MSG) ? STATUS_MSG : 0) |
+                       (d_host->read_signal(SIG_SCSI_CD ) ? STATUS_CD  : 0) |
+                       (d_host->read_signal(SIG_SCSI_BSY) ? STATUS_BSY : 0) |
+                       (irq_status                        ? STATUS_INT : 0);
+               #ifdef _SCSI_DEBUG_LOG
+                       this->out_debug_log(_T("[SCSI] in  %04X %02X\n"), addr, value);
+               #endif
+               return value;
+       }
+       return 0xff;
+}
+
+void SCSI::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       switch(id) {
+       case SIG_SCSI_IRQ:
+               if(ctrl_reg & CTRL_IMSK) {
+                       d_pic->write_signal(SIG_I8259_CHIP1 | SIG_I8259_IR0, data, mask);
+               }
+               irq_status = ((data & mask) != 0);
+               break;
+               
+       case SIG_SCSI_DRQ:
+               if(ctrl_reg & CTRL_DMAE) {
+                       d_dma->write_signal(SIG_UPD71071_CH1, data, mask);
+               }
+               break;
+       }
+}
+
+#define STATE_VERSION  1
+
+void SCSI::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       
+       state_fio->FputUint8(ctrl_reg);
+       state_fio->FputBool(irq_status);
+}
+
+bool SCSI::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       if(state_fio->FgetInt32() != this_device_id) {
+               return false;
+       }
+       ctrl_reg = state_fio->FgetUint8();
+       irq_status = state_fio->FgetBool();
+       return true;
+}
+
diff --git a/source/src/vm/fmtowns/scsi.h b/source/src/vm/fmtowns/scsi.h
new file mode 100644 (file)
index 0000000..22d738f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2016.03.03-
+
+       [ scsi ]
+*/
+
+#ifndef _SCSI_H_
+#define _SCSI_H_
+
+#include "../vm.h"
+#include "../../emu.h"
+#include "../device.h"
+
+#define SIG_SCSI_IRQ   0
+#define SIG_SCSI_DRQ   1
+
+class SCSI : public DEVICE
+{
+private:
+       DEVICE *d_dma, *d_pic, *d_host;
+       
+       uint8_t ctrl_reg;
+       bool irq_status;
+       
+public:
+       SCSI(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) {
+               set_device_name(_T("FMR50 SCSI"));
+       }
+       ~SCSI() {}
+       
+       // common functions
+       void reset();
+       void write_io8(uint32_t addr, uint32_t data);
+       uint32_t read_io8(uint32_t addr);
+       void write_signal(int id, uint32_t data, uint32_t mask);
+       
+       // unique functions
+       void set_context_dma(DEVICE* device)
+       {
+               d_dma = device;
+       }
+       void set_context_pic(DEVICE* device)
+       {
+               d_pic = device;
+       }
+       void set_context_host(DEVICE* device)
+       {
+               d_host = device;
+       }
+       void save_state(FILEIO* state_fio);
+       bool load_state(FILEIO* state_fio);
+};
+
+#endif
+
diff --git a/source/src/vm/fmtowns/timer.cpp b/source/src/vm/fmtowns/timer.cpp
new file mode 100644 (file)
index 0000000..4afc043
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.04.30 -
+
+       [ timer ]
+*/
+
+#include "timer.h"
+#include "../i8259.h"
+#include "../msm58321.h"
+#include "../pcm1bit.h"
+
+void TIMER::initialize()
+{
+       free_run_counter = 0;
+       intr_reg = rtc_data = 0;
+       tmout0 = tmout1 = false;
+}
+
+void TIMER::write_io8(uint32_t addr, uint32_t data)
+{
+       switch(addr) {
+       case 0x60:
+               if(data & 0x80) {
+                       tmout0 = false;
+               }
+               intr_reg = data;
+               update_intr();
+               d_pcm->write_signal(SIG_PCM1BIT_ON, data, 4);
+               break;
+       case 0x70:
+               d_rtc->write_signal(SIG_MSM58321_DATA, data, 0x0f);
+               break;
+       case 0x80:
+               d_rtc->write_signal(SIG_MSM58321_CS, data, 0x80);
+               d_rtc->write_signal(SIG_MSM58321_READ, data, 0x04);
+               d_rtc->write_signal(SIG_MSM58321_WRITE, data, 0x02);
+               d_rtc->write_signal(SIG_MSM58321_ADDR_WRITE, data, 0x01);
+               break;
+       }
+}
+
+uint32_t TIMER::read_io8(uint32_t addr)
+{
+       switch(addr) {
+       case 0x26:
+               free_run_counter = (uint16_t)get_passed_usec(0);
+               return free_run_counter & 0xff;
+       case 0x27:
+               return free_run_counter >> 8;
+       case 0x60:
+               return (tmout0 ? 1 : 0) | (tmout1 ? 2 : 0) | ((intr_reg & 7) << 2) | 0xe0;
+       case 0x70:
+               return rtc_data;
+       }
+       return 0xff;
+}
+
+void TIMER::write_signal(int id, uint32_t data, uint32_t mask)
+{
+       if(id == SIG_TIMER_CH0) {
+               if(data & mask) {
+                       tmout0 = true;
+               }
+               update_intr();
+       } else if(id == SIG_TIMER_CH1) {
+               tmout1 = ((data & mask) != 0);
+               update_intr();
+       } else if(id == SIG_TIMER_RTC) {
+               rtc_data = (data & mask) | (rtc_data & ~mask);
+       }
+}
+
+void TIMER::update_intr()
+{
+       if((tmout0 && (intr_reg & 1)) || (tmout1 && (intr_reg & 2))) {
+               d_pic->write_signal(SIG_I8259_CHIP0 | SIG_I8259_IR0, 1, 1);
+       } else {
+               d_pic->write_signal(SIG_I8259_CHIP0 | SIG_I8259_IR0, 0, 1);
+       }
+}
+
+#define STATE_VERSION  1
+
+void TIMER::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       
+       state_fio->FputUint16(free_run_counter);
+       state_fio->FputUint8(intr_reg);
+       state_fio->FputUint8(rtc_data);
+       state_fio->FputBool(tmout0);
+       state_fio->FputBool(tmout1);
+}
+
+bool TIMER::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       if(state_fio->FgetInt32() != this_device_id) {
+               return false;
+       }
+       free_run_counter = state_fio->FgetUint16();
+       intr_reg = state_fio->FgetUint8();
+       rtc_data = state_fio->FgetUint8();
+       tmout0 = state_fio->FgetBool();
+       tmout1 = state_fio->FgetBool();
+       return true;
+}
+
diff --git a/source/src/vm/fmtowns/timer.h b/source/src/vm/fmtowns/timer.h
new file mode 100644 (file)
index 0000000..e2b6c27
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+       FUJITSU FMR-50 Emulator 'eFMR-50'
+       FUJITSU FMR-60 Emulator 'eFMR-60'
+
+       Author : Takeda.Toshiya
+       Date   : 2008.04.30 -
+
+       [ timer ]
+*/
+
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+#include "../vm.h"
+#include "../../emu.h"
+#include "../device.h"
+
+#define SIG_TIMER_CH0  0
+#define SIG_TIMER_CH1  1
+#define SIG_TIMER_RTC  2
+
+class TIMER : public DEVICE
+{
+private:
+       DEVICE *d_pcm, *d_pic, *d_rtc;
+       
+       uint16_t free_run_counter;
+       uint8_t intr_reg, rtc_data;
+       bool tmout0, tmout1;
+       void update_intr();
+       
+public:
+       TIMER(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) {}
+       ~TIMER() {}
+       
+       // common functions
+       void initialize();
+       void write_io8(uint32_t addr, uint32_t data);
+       uint32_t read_io8(uint32_t addr);
+       void write_signal(int id, uint32_t data, uint32_t mask);
+       void save_state(FILEIO* state_fio);
+       bool load_state(FILEIO* state_fio);
+       
+       // unique functions
+       void set_context_pcm(DEVICE* device)
+       {
+               d_pcm = device;
+       }
+       void set_context_pic(DEVICE* device)
+       {
+               d_pic = device;
+       }
+       void set_context_rtc(DEVICE* device)
+       {
+               d_rtc = device;
+       }
+};
+
+#endif
+
diff --git a/source/src/vm/fmtowns/towns_crtc.cpp b/source/src/vm/fmtowns/towns_crtc.cpp
new file mode 100644 (file)
index 0000000..85452cb
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Kyuma Ohta <whatisthis.sowhat _at_ gmail.com>
+       Date   : 2016.12.28 -
+
+       [ FM-Towns CRTC ]
+       History: 2016.12.28 Initial from HD46505 .
+*/
+
+#include "towns_crtc.h"
+
+#define EVENT_DISPLAY  0
+#define EVENT_HSYNC_S  1
+#define EVENT_HSYNC_E  2
+
+void TOWNS_CRTC::initialize()
+{
+       memset(regs, 0, sizeof(regs));
+       memset(regs_written, 0, sizeof(regs_written));
+       
+       // register events
+       register_frame_event(this);
+       register_vline_event(this);
+}
+
+void TOWNS_CRTC::reset()
+{
+       // initialize
+       display = false;
+       vblank = vsync = hsync = true;
+       
+//     memset(regs, 0, sizeof(regs));
+       ch = 0;
+       
+       // initial settings for 1st frame
+#ifdef CHARS_PER_LINE
+       hz_total = (CHARS_PER_LINE > 54) ? CHARS_PER_LINE : 54;
+#else
+       hz_total = 54;
+#endif
+       hz_disp = (hz_total > 80) ? 80 : 40;
+       hs_start = hz_disp + 4;
+       hs_end = hs_start + 4;
+       
+       vt_total = LINES_PER_FRAME;
+       vt_disp = (SCREEN_HEIGHT > LINES_PER_FRAME) ? (SCREEN_HEIGHT >> 1) : SCREEN_HEIGHT;
+       vs_start = vt_disp + 16;
+       vs_end = vs_start + 16;
+       
+       timing_changed = false;
+       disp_end_clock = 0;
+       
+#if defined(TOWNS_CRTC_CHAR_CLOCK)
+       char_clock = 0;
+       next_char_clock = TOWNS_CRTC_CHAR_CLOCK;
+#elif defined(TOWNS_CRTC_HORIZ_FREQ)
+       horiz_freq = 0;
+       next_horiz_freq = TOWNS_CRTC_HORIZ_FREQ;
+#endif
+}
+
+void TOWNS_CRTC::write_io8(uint32_t addr, uint32_t data)
+{
+       if(addr & 1) {
+               if(ch < 32) {
+                       if(ch < 16 && regs[ch] != data) {
+                               timing_changed = true;
+                       }
+                       regs[ch] = data;
+                       regs_written[ch] = true;
+               }
+       } else {
+               ch = data;
+       }
+}
+
+uint32_t TOWNS_CRTC::read_io8(uint32_t addr)
+{
+       if(addr & 1) {
+               return (12 <= ch && ch < 18) ? regs[ch] : 0xff;
+       } else {
+               return ch;
+       }
+}
+
+void TOWNS_CRTC::event_pre_frame()
+{
+       if(timing_changed) {
+               if(regs_written[0] && regs_written[1] && regs_written[2] && regs_written[3] && regs_written[4] && regs_written[5] && regs_written[6] && regs_written[7] && regs_written[9]) {
+                       int ch_height = (regs[9] & 0x1f) + 1;
+                       
+                       hz_total = regs[0] + 1;
+                       hz_disp = regs[1];
+                       hs_start = regs[2];
+                       hs_end = hs_start + (regs[3] & 0x0f);
+                       
+                       int new_vt_total = ((regs[4] & 0x7f) + 1) * ch_height + (regs[5] & 0x1f);
+                       vt_disp = (regs[6] & 0x7f) * ch_height;
+                       vs_start = ((regs[7] & 0x7f) + 1) * ch_height;
+                       vs_end = vs_start + ((regs[3] & 0xf0) ? (regs[3] >> 4) : 16);
+                       
+                       if(vt_total != new_vt_total) {
+                               vt_total = new_vt_total;
+                               set_lines_per_frame(vt_total);
+                       }
+                       timing_changed = false;
+                       disp_end_clock = 0;
+#if defined(TOWNS_CRTC_CHAR_CLOCK)
+                       char_clock = 0;
+#elif defined(TOWNS_CRTC_HORIZ_FREQ)
+                       horiz_freq = 0;
+#endif
+               }
+       }
+#if defined(TOWNS_CRTC_CHAR_CLOCK)
+       if(char_clock != next_char_clock) {
+               char_clock = next_char_clock;
+               frames_per_sec = char_clock / (double)vt_total / (double)hz_total;
+               if(regs[8] & 1) {
+                       frames_per_sec *= 2; // interlace mode
+               }
+               set_frames_per_sec(frames_per_sec);
+       }
+#elif defined(TOWNS_CRTC_HORIZ_FREQ)
+       if(horiz_freq != next_horiz_freq) {
+               horiz_freq = next_horiz_freq;
+               frames_per_sec = horiz_freq / (double)vt_total;
+               if(regs[8] & 1) {
+                       frames_per_sec *= 2; // interlace mode
+               }
+               set_frames_per_sec(frames_per_sec);
+       }
+#endif
+}
+
+void TOWNS_CRTC::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
+{
+       cpu_clocks = new_clocks;
+#if !defined(TOWNS_CRTC_CHAR_CLOCK) && !defined(TOWNS_CRTC_HORIZ_FREQ)
+       frames_per_sec = new_frames_per_sec;
+#endif
+       
+       // update event clocks
+       disp_end_clock = 0;
+}
+
+void TOWNS_CRTC::event_frame()
+{
+       // update envet clocks after update_timing() is called
+       if(disp_end_clock == 0 && vt_total != 0) {
+               disp_end_clock = (int)((double)cpu_clocks * (double)hz_disp / frames_per_sec / (double)vt_total / (double)hz_total);
+               hs_start_clock = (int)((double)cpu_clocks * (double)hs_start / frames_per_sec / (double)vt_total / (double)hz_total);
+               hs_end_clock = (int)((double)cpu_clocks * (double)hs_end / frames_per_sec / (double)vt_total / (double)hz_total);
+       }
+}
+
+void TOWNS_CRTC::event_vline(int v, int clock)
+{
+       // if vt_disp == 0, raise vblank for one line
+       bool new_vblank = ((v < vt_disp) || (v == 0 && vt_disp == 0));
+       
+       // display
+       if(outputs_disp.count) {
+               set_display(new_vblank);
+               if(new_vblank && hz_disp < hz_total) {
+                       register_event_by_clock(this, EVENT_DISPLAY, disp_end_clock, false, NULL);
+               }
+       }
+       
+       // vblank
+       set_vblank(new_vblank); // active low
+       
+       // vsync
+       set_vsync(vs_start <= v && v < vs_end);
+       
+       // hsync
+       if(outputs_hsync.count && hs_start < hs_end && hs_end < hz_total) {
+               set_hsync(false);
+               register_event_by_clock(this, EVENT_HSYNC_S, hs_start_clock, false, NULL);
+               register_event_by_clock(this, EVENT_HSYNC_E, hs_end_clock, false, NULL);
+       }
+}
+
+void TOWNS_CRTC::event_callback(int event_id, int err)
+{
+       if(event_id == EVENT_DISPLAY) {
+               set_display(false);
+       } else if(event_id == EVENT_HSYNC_S) {
+               set_hsync(true);
+       } else if(event_id == EVENT_HSYNC_E) {
+               set_hsync(false);
+       }
+}
+
+void TOWNS_CRTC::set_display(bool val)
+{
+       if(display != val) {
+               write_signals(&outputs_disp, val ? 0xffffffff : 0);
+               display = val;
+       }
+}
+
+void TOWNS_CRTC::set_vblank(bool val)
+{
+       if(vblank != val) {
+               write_signals(&outputs_vblank, val ? 0xffffffff : 0);
+               vblank = val;
+       }
+}
+
+#define STATE_VERSION  1
+
+void TOWNS_CRTC::save_state(FILEIO* state_fio)
+{
+       state_fio->FputUint32(STATE_VERSION);
+       state_fio->FputInt32(this_device_id);
+       state_fio->Fwrite(regs, sizeof(regs), 1);
+       state_fio->Fwrite(regs_written, sizeof(regs_written), 1);
+       state_fio->FputInt32(ch);
+       state_fio->FputBool(timing_changed);
+       state_fio->FputInt32(cpu_clocks);
+#if defined(TOWNS_CRTC_CHAR_CLOCK)
+       state_fio->FputDouble(char_clock);
+       state_fio->FputDouble(next_char_clock);
+#elif defined(TOWNS_CRTC_HORIZ_FREQ)
+       state_fio->FputDouble(horiz_freq);
+       state_fio->FputDouble(next_horiz_freq);
+#endif
+       state_fio->FputDouble(frames_per_sec);
+       state_fio->FputInt32(hz_total);
+       state_fio->FputInt32(hz_disp);
+       state_fio->FputInt32(hs_start);
+       state_fio->FputInt32(hs_end);
+       state_fio->FputInt32(vt_total);
+       state_fio->FputInt32(vt_disp);
+       state_fio->FputInt32(vs_start);
+       state_fio->FputInt32(vs_end);
+       state_fio->FputInt32(disp_end_clock);
+       state_fio->FputInt32(hs_start_clock);
+       state_fio->FputInt32(hs_end_clock);
+       state_fio->FputBool(display);
+       state_fio->FputBool(vblank);
+       state_fio->FputBool(vsync);
+       state_fio->FputBool(hsync);
+}
+
+bool TOWNS_CRTC::load_state(FILEIO* state_fio)
+{
+       if(state_fio->FgetUint32() != STATE_VERSION) {
+               return false;
+       }
+       if(state_fio->FgetInt32() != this_device_id) {
+               return false;
+       }
+       state_fio->Fread(regs, sizeof(regs), 1);
+       state_fio->Fread(regs_written, sizeof(regs_written), 1);
+       ch = state_fio->FgetInt32();
+       timing_changed = state_fio->FgetBool();
+       cpu_clocks = state_fio->FgetInt32();
+#if defined(TOWNS_CRTC_CHAR_CLOCK)
+       char_clock = state_fio->FgetDouble();
+       next_char_clock = state_fio->FgetDouble();
+#elif defined(TOWNS_CRTC_HORIZ_FREQ)
+       horiz_freq = state_fio->FgetDouble();
+       next_horiz_freq = state_fio->FgetDouble();
+#endif
+       frames_per_sec = state_fio->FgetDouble();
+       hz_total = state_fio->FgetInt32();
+       hz_disp = state_fio->FgetInt32();
+       hs_start = state_fio->FgetInt32();
+       hs_end = state_fio->FgetInt32();
+       vt_total = state_fio->FgetInt32();
+       vt_disp = state_fio->FgetInt32();
+       vs_start = state_fio->FgetInt32();
+       vs_end = state_fio->FgetInt32();
+       disp_end_clock = state_fio->FgetInt32();
+       hs_start_clock = state_fio->FgetInt32();
+       hs_end_clock = state_fio->FgetInt32();
+       display = state_fio->FgetBool();
+       vblank = state_fio->FgetBool();
+       vsync = state_fio->FgetBool();
+       hsync = state_fio->FgetBool();
+
+       return true;
+}
diff --git a/source/src/vm/fmtowns/towns_crtc.h b/source/src/vm/fmtowns/towns_crtc.h
new file mode 100644 (file)
index 0000000..704772f
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Kyuma Ohta <whatisthis.sowhat _at_ gmail.com>
+       Date   : 2016.12.28 -
+
+       [ FM-Towns CRTC ]
+       History: 2016.12.28 Initial from HD46505 .
+*/
+
+#ifndef _TOWNS_CRTC_H_
+#define _TOWNS_CRTC_H_
+
+#include "../vm.h"
+#include "../emu.h"
+#include "device.h"
+
+class TOWNS_CRTC : public DEVICE
+{
+private:
+       // output signals
+       outputs_t outputs_disp;
+       outputs_t outputs_vblank;
+       outputs_t outputs_vsync;
+       outputs_t outputs_hsync;
+       
+       uint8_t regs[32];
+       bool regs_written[32];
+       int ch;
+       bool timing_changed;
+       
+       int cpu_clocks;
+#if defined(TOWNS_CRTC_CHAR_CLOCK)
+       double char_clock, next_char_clock;
+#elif defined(TOWNS_CRTC_HORIZ_FREQ)
+       double horiz_freq, next_horiz_freq;
+#endif
+       double frames_per_sec;
+       
+       int hz_total, hz_disp;
+       int hs_start, hs_end;
+       
+       int vt_total, vt_disp;
+       int vs_start, vs_end;
+       
+       int disp_end_clock;
+       int hs_start_clock, hs_end_clock;
+       
+       bool display, vblank, vsync, hsync;
+       
+       void set_display(bool val);
+       void set_vblank(bool val);
+       void set_vsync(bool val);
+       void set_hsync(bool val);
+       
+public:
+       TOWNS_CRTC(VM *parent_vm, EMU *parent_emu) : DEVICE(parent_vm, parent_emu)
+       {
+               initialize_output_signals(&outputs_disp);
+               initialize_output_signals(&outputs_vblank);
+               initialize_output_signals(&outputs_vsync);
+               initialize_output_signals(&outputs_hsync);
+               set_device_name(_T("FM-Towns CRTC"));
+       }
+       ~TOWNS_CRTC() {}
+
+       void initialize();
+       void reset();
+       void write_io8(uint32_t addr, uint32_t data);
+       uint32_t read_io8(uint32_t addr);
+       void event_pre_frame();
+       void event_frame();
+       void event_vline(int v, int clock);
+       void event_callback(int event_id, int err);
+       void update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame);
+       void save_state(FILEIO* state_fio);
+       bool load_state(FILEIO* state_fio);
+       // unique function
+       void set_context_disp(DEVICE* device, int id, uint32_t mask)
+       {
+               register_output_signal(&outputs_disp, device, id, mask);
+       }
+       void set_context_vblank(DEVICE* device, int id, uint32_t mask)
+       {
+               register_output_signal(&outputs_vblank, device, id, mask);
+       }
+       void set_context_vsync(DEVICE* device, int id, uint32_t mask)
+       {
+               register_output_signal(&outputs_vsync, device, id, mask);
+       }
+       void set_context_hsync(DEVICE* device, int id, uint32_t mask)
+       {
+               register_output_signal(&outputs_hsync, device, id, mask);
+       }
+#if defined(TOWNS_CRTC_CHAR_CLOCK)
+       void set_char_clock(double clock)
+       {
+               next_char_clock = clock;
+       }
+#elif defined(TOWNS_CRTC_HORIZ_FREQ)
+       void set_horiz_freq(double freq)
+       {
+               next_horiz_freq = freq;
+       }
+#endif
+       uint8_t* get_regs()
+       {
+               return regs;
+       }
+
+};
+
+#endif
+
diff --git a/source/src/vm/fmtowns/towns_vram.h b/source/src/vm/fmtowns/towns_vram.h
new file mode 100644 (file)
index 0000000..199ad90
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+       Skelton for retropc emulator
+
+       Author : Kyuma Ohta <whatisthis.sowhat _at_ gmail.com>
+       Date   : 2016.12.28 -
+
+       [ FM-Towns VRAM ]
+       History: 2016.12.28 Initial.
+*/
+
+#ifndef _TOWNS_VRAM_H_
+#define _TOWNS_VRAM_H_
+
+#include "../vm.h"
+#include "../emu.h"
+#include "device.h"
+
+// Older Towns.
+#define TOWNS_VRAM_ADDR_MASK 0x7ffff
+// VRAM DIRECT ACCESS: For Sprite. You should access with 16bit
+// You can write raw data, drawing with colorkey is automatically.
+#define SIG_TOWNS_TRANSFER_SPRITE_DATA 0x100000
+#define SIG_TOWNS_SET_SPRITE_BANK      0x140000
+#define SIG_TOWNS_CLEAR_SPRITE_BUFFER  0x140001
+// Do render with any mode. You should set vline to arg.
+#define SIG_TOWNS_RENDER_RASTER    0x01
+#define SIG_TOWNS_RENDER_FULL      0x02
+#define SIG_TOWNS_VRAM_VSTART      0x03
+#define SIG_TOWNS_VRAM_VBLANK      0x04
+#define SIG_TOWNS_VRAM_VSYNC       0x05
+#define SIG_TOWNS_VRAM_HSYNC       0x06
+#define SIG_TOWNS_VRAM_SET_VLINE   0x07
+#define SIG_TOWNS_RENDER_FLAG      0x08
+
+class TOWNS_VRAM : public DEVICE
+{
+protected:
+       scrntype_t *render_buffer;
+       uint32_t page_modes[4];
+       uint32_t masks;
+       uint8_t vram[0x80000]; // Related by machine.
+public:
+       TOWNS_VRAM(VM* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu)
+       {
+               memset(vram, 0x00, sizeof(vram));
+               render_buffer = NULL;
+               page_modes[0] = page_modes[1] = page_modes[2] = page_modes[3] = 0;
+               masks = 0;
+       }
+       ~TOWNS_VRAM() {}
+       
+       uint32_t read_data8(uint32_t addr)
+       {
+               if(addr < 0x80000000) {
+                       // Plane Access
+                       uint32_t data32;
+                       uint8_t data8;
+                       uint32_t n_plane = (addr & 0x18000) / 0x8000;
+                       uint32_t n_addr = addr & 0x7fff;
+                       uint32_t x_addr = n_addr << 2;
+                       uint32_t *p;
+                       if(plane_page1) x_addr += 0x20000;
+                       p = (uint32_t *)(&(vram[x_addr]));
+                       data32 = *p;
+                       data32 >>= (3 - n_plane);
+                       data8 =
+                               ((data32 & 0x10000000) ? 0x80 : 0) |
+                               ((data32 & 0x01000000) ? 0x40 : 0) | 
+                               ((data32 & 0x00100000) ? 0x20 : 0) | 
+                               ((data32 & 0x00010000) ? 0x10 : 0) | 
+                               ((data32 & 0x00001000) ? 0x08 : 0) |
+                               ((data32 & 0x00000100) ? 0x04 : 0) | 
+                               ((data32 & 0x00000010) ? 0x02 : 0) | 
+                               ((data32 & 0x00000001) ? 0x01 : 0);
+                       return (uint32_t) data8;
+               } else {
+                       return vram[addr & TOWNS_VRAM_ADDR_MASK];
+               }
+       };
+                               
+       uint32_t read_data16(uint32_t addr)
+       {
+               pair_t n;
+               n.d = 0;
+               if(addr < 0x80000000) {
+                       addr = addr & 0x1fffe;
+                       n.b.h = read_data8(addr + 1);
+                       n.b.l = read_data8(addr);
+               } else {
+                       addr = addr & (TOWNS_VRAM_ADDR_MASK & 0xfffffffe);
+                       n.b.h = vram[addr + 1];
+                       n.b.l = vram[addr];
+               }
+               return n.d;
+       }
+
+       uint32_t read_data32(uint32_t addr)
+       {
+               if(addr < 0x80000000) {
+                       addr = addr & 0x1fffc;
+                       n.b.h3 = read_data8(addr + 3);
+                       n.b.h2 = read_data8(addr + 2);
+                       n.b.h  = read_data8(addr + 1);
+                       n.b.l  = read_data8(addr);
+               } else {
+                       addr = addr & (TOWNS_VRAM_ADDR_MASK & 0xfffffffc);
+                       n.b.h3 = vram[addr + 3];
+                       n.b.h2 = vram[addr + 2];
+                       n.b.h  = vram[addr + 1];
+                       n.b.l  = vram[addr];
+               }
+               return n.d;
+       }
+
+       void write_data8(uint32_t addr, uint32_t data)
+       {
+               if(addr < 0x80000000) {
+                       // Plane Access
+                       pair_t data_p;
+                       uint32_t n_plane = (addr & 0x18000) / 0x8000;
+                       uint32_t n_addr = addr & 0x7fff;
+                       uint32_t x_addr = n_addr << 2;
+                       
+                       pair_t *p;
+                       if(plane_page1) x_addr += 0x20000;
+                       p = (pair_t *)(&(vram[x_addr]));
+                       data_p.d = *p;
+                       // ToDo
+               } else {
+                       // ToDo: Scroll
+                       uint32_t mask2 = (masks >> ((3 - (addr & 3)) * 8)) & 0xff;
+                       uint32_t d_s = vram[addr & TOWNS_VRAM_ADDR_MASK] & ~mask2;
+                       uint32_t d_d = data & mask2;
+                       vram[addr & TOWNS_VRAM_ADDR_MASK] = d_s | d_d;
+               }
+       }
+       
+       void write_data16(uint32_t addr, uint32_t data)
+       {
+               if(addr < 0x80000000) {
+                       // Plane Access
+                       pair_t data_p;
+                       uint32_t n_plane = (addr & 0x18000) / 0x8000;
+                       uint32_t n_addr = addr & 0x7fff;
+                       uint32_t x_addr = n_addr << 2;
+                       
+                       pair_t *p;
+                       if(plane_page1) x_addr += 0x20000;
+                       p = (pair_t *)(&(vram[x_addr]));
+                       data_p.d = *p;
+                       // ToDo
+               } else {
+                       pair_t n;
+                       uint16_t *p;
+                       n.d = 0;
+                       p = (uint16_t *)(&(vram[addr & TOWNS_VRAM_ADDR_MASK & 0xfffffffe]));
+                       n.sw.l = *p;
+                       uint32_t mask2 = (masks >> ((2 - (addr & 2)) * 16)) & 0xffff;
+                       uint32_t d_s = n.d & ~mask2;
+                       uint32_t d_d = data & mask2;
+                       // ToDo: Scroll
+                       n.d = d_s | d_d;
+                       *p = n.sw.l;
+               }
+       }
+
+       void write_data32(uint32_t addr, uint32_t data)
+       {
+               if(addr < 0x80000000) {
+                       // Plane Access
+                       pair_t data_p;
+                       uint32_t n_plane = (addr & 0x18000) / 0x8000;
+                       uint32_t n_addr = addr & 0x7fff;
+                       uint32_t x_addr = n_addr << 2;
+                       
+                       pair_t *p;
+                       if(plane_page1) x_addr += 0x20000;
+                       p = (pair_t *)(&(vram[x_addr]));
+                       data_p.d = *p;
+                       // ToDo
+               } else {
+                       uint32_t n;
+                       uint32_t *p;
+
+                       p = (uint32_t *)(&(vram[addr & TOWNS_VRAM_ADDR_MASK & 0xfffffffc]));
+                       n = *p;
+                       uint32_t mask2 = masks;
+                       uint32_t d_s = n & ~mask2;
+                       uint32_t d_d = data & mask2;
+                       // ToDo: Scroll
+                       *p = d_s | d_d;
+               }
+       }
+
+       uint32_t read_io8(uint32_t addr);
+       void write_io8(uint32_t addr, uint32_t data);
+       void draw_screen();
+       void write_signal(int id, uint32_t data, uint32_t mask); // Do render
+
+       void set_context_renderbuffer(scrntype_t *p){
+               render_buffer = p;
+       };
+};
+
+#endif