DISK関連は「PC6001V」をベースにすることにしました。
メモリマップ、I/Oマップはここら辺を参考にしました。
http://p6ers.net/hashi/n6xbasicref.html
vm.h
(省略) // NEC PC6001 #ifdef _PC6001 #include "pc6001/pc6001.h" #endif (省略)
emu.cpp
(省略) EMU::EMU(HWND hwnd, HINSTANCE hinst) { #ifdef _DEBUG_LOG // open debug logfile open_debug(); #endif message_count = 0; // store main window handle main_window_handle = hwnd; instance_handle = hinst; // get module path GetModuleFileName(NULL, app_path, _MAX_PATH); int pt = _tcslen(app_path); while(pt >= 0 && app_path[pt] != _T('\\')) { pt--; } app_path[pt + 1] = _T('\0'); #ifdef _PC6001 FILE *fp; if(NULL != (fp=fopen("/sdcard/ROM/BASICROM.60", "rb"))) { strcpy(app_path, "/sdcard/"); fclose(fp); } #endif (省略)
i8255.cpp
(省略) uint32 I8255::read_io8(uint32 addr) { int ch = addr & 3; switch(ch) { case 0: case 1: case 2: if(ch == 0) { if(port[0].mode == 1 || port[0].mode == 2) { uint32 val = port[2].wreg & ~BIT_IBF_A; if(port[2].wreg & BIT_STB_A) { val &= ~BIT_INTR_A; } write_io8(2, val); } } else if(ch == 1) { if(port[1].mode == 1) { uint32 val = port[2].wreg & ~BIT_IBF_B; if(port[2].wreg & BIT_STB_B) { val &= ~BIT_INTR_B; } write_io8(2, val); } #ifdef _PC6001 } else { return 0xff; #endif } return (port[ch].rreg & port[ch].rmask) | (port[ch].wreg & ~port[ch].rmask); } return 0xff; } (省略)
win32_input.cpp
(省略) // update joystick status #ifdef _PC6001 memset(joy_status, 0, 8); #else memset(joy_status, 0, sizeof(joy_status)); #endif (省略) if(!(code == VK_SHIFT || code == VK_CONTROL || code == VK_MENU)) { code = keycode_conv[code]; } #ifdef _PC6001 #define STICK0_SPACE 0x80 #define STICK0_LEFT 0x20 #define STICK0_RIGHT 0x10 #define STICK0_DOWN 0x08 #define STICK0_UP 0x04 #define STICK0_STOP 0x02 #define STICK0_SHIFT 0x01 if (code==VK_SPACE) joy_status[2] |= STICK0_SPACE; if (code==VK_LEFT) joy_status[2] |= STICK0_LEFT; if (code==VK_RIGHT) joy_status[2] |= STICK0_RIGHT; if (code==VK_DOWN) joy_status[2] |= STICK0_DOWN; if (code==VK_UP) joy_status[2] |= STICK0_UP; if (code==VK_F9) joy_status[2] |= STICK0_STOP; if (code==VK_SHIFT) joy_status[2] |= STICK0_SHIFT; #endif #ifdef DONT_KEEEP_KEY_PRESSED if(!(code == VK_SHIFT || code == VK_CONTROL || code == VK_MENU)) { key_status[code] = KEY_KEEP_FRAMES; } else #endif (省略) if(!(code == VK_SHIFT || code == VK_CONTROL || code == VK_MENU)) { code = keycode_conv[code]; } #ifdef _PC6001 if (code==VK_SPACE) joy_status[2] &= ~STICK0_SPACE; if (code==VK_LEFT) joy_status[2] &= ~STICK0_LEFT; if (code==VK_RIGHT) joy_status[2] &= ~STICK0_RIGHT; if (code==VK_DOWN) joy_status[2] &= ~STICK0_DOWN; if (code==VK_UP) joy_status[2] &= ~STICK0_UP; if (code==VK_F9) joy_status[2] &= ~STICK0_STOP; if (code==VK_SHIFT) joy_status[2] &= ~STICK0_SHIFT; #endif if(key_status[code]) { key_status[code] &= 0x7f; #ifdef NOTIFY_KEY_DOWN if(!key_status[code]) { vm->key_up(code); } #endif } } (省略)
d88.cpp
#include <string.h> #include "d88.h" char D88_LOG[1024]; //////////////////////////////////////////////////////////////// // コンストラクタ //////////////////////////////////////////////////////////////// cD88::cD88( void ) : Protected(false) { PRINTD( D88_LOG, "[D88][cD88]\n" ) INITARRAY( FileName, '\0' ); } //////////////////////////////////////////////////////////////// // デストラクタ //////////////////////////////////////////////////////////////// cD88::~cD88( void ) { if( d88.fp ) fclose( d88.fp ); } //////////////////////////////////////////////////////////////// // 初期化 //////////////////////////////////////////////////////////////// bool cD88::Init( char *fname ) { PRINTD( D88_LOG, "[D88][Init] %s\n", fname ) strncpy( FileName, fname, PATH_MAX ); /// 読取り専用属性ならプロテクト状態で開く /// if( OSD_FileReadOnly( FileName ) ){ /// d88.fp = FOPENEN( FileName, "rb" ); /// Protected = true; // プロテクトシールあり /// }else{ d88.fp = fopen( FileName, "rb+" ); Protected = false; // プロテクトシールなし /// } if( !d88.fp ){ *FileName = 0; Protected = false; return false; } ReadHeader88(); // D88 ヘッダ読込み return true; } //////////////////////////////////////////////////////////////// // D88 ヘッダ読込み //////////////////////////////////////////////////////////////// void cD88::ReadHeader88( void ) { PRINTD( D88_LOG, "[D88][ReadHeader88]\n" ) if( d88.fp ){ // DISK名 fread( d88.name, sizeof(BYTE), 17, d88.fp ); d88.name[16] = '\0'; // リザーブ空読み fread( d88.reserve, sizeof(BYTE), 9, d88.fp ); // ライトプロテクト d88.protect = fgetc( d88.fp ); if( d88.protect ) Protected = true; else if( Protected ) d88.protect = 0x10; // DISKの種類 d88.type = fgetc( d88.fp ); // DISKのサイズ d88.size = FGETDWORD( d88.fp ); // トラック部のオフセットテーブル for( int i=0; i<164; i++ ) d88.table[i] = FGETDWORD( d88.fp ); // アクセス中のトラックNo d88.trkno = 0; PRINTD( D88_LOG, " FileName : %s\n", d88.name ) PRINTD( D88_LOG, " Protect : %s\n", d88.protect ? "ON" : "OFF" ) PRINTD( D88_LOG, " Format : %d\n", d88.type ) PRINTD( D88_LOG, " Size : %d\n", (int)d88.size ) } } //////////////////////////////////////////////////////////////// // D88 セクタ情報読込み //////////////////////////////////////////////////////////////// void cD88::ReadSector88( void ) { PRINTD( D88_LOG, "[D88][ReadSector88]\n" ); if( d88.fp && d88.table[d88.trkno] ){ d88.secinfo.c = fgetc( d88.fp ); // ID の C (シリンダNo 片面の場合は=トラックNo) d88.secinfo.h = fgetc( d88.fp ); // ID の H (ヘッダアドレス 片面の場合は=0) d88.secinfo.r = fgetc( d88.fp ); // ID の R (トラック内のセクタNo) d88.secinfo.n = fgetc( d88.fp ); // ID の N (セクタサイズ 0:256 1:256 2:512 3:1024) d88.secinfo.sec_nr = FGETWORD( d88.fp ); // このトラック内に存在するセクタの数 d88.secinfo.density = fgetc( d88.fp ); // 記録密度 0x00:倍密度 0x40:単密度 d88.secinfo.deleted = fgetc( d88.fp ); // DELETED MARK 0x00:ノーマル 0x10:DELETED d88.secinfo.status = fgetc( d88.fp ); // ステータス fread( d88.secinfo.reserve, sizeof(BYTE), 5, d88.fp ); // リザーブ空読み d88.secinfo.size = FGETWORD( d88.fp ); // このセクタ部のデータサイズ d88.secinfo.data = ftell( d88.fp ); // データへのオフセット d88.secinfo.offset = 0; // 次に読込むデータのセクタ先頭からのオフセット d88.secinfo.secno++; // アクセス中のセクタNo // Dittのバグ対応 // 吸出し時,データサイズ=0の時に00Hが256バイト格納されるバグがあるらしい // データサイズが0だったら続く256バイトを調べ,全て00Hだったら読み飛ばす // 00H以外のデータが現れたら次のセクタのデータと判断し,読み飛ばさない。 // 256バイト先がトラックorディスクの末尾に到達する場合も読み飛ばさない if( (d88.secinfo.size == 0) && (((d88.trkno < 163) && (d88.table[d88.trkno+1] >= d88.secinfo.data+256)) || (d88.size >= d88.secinfo.data+256) ) ){ for( int i=0; i<256; i++ ){ if( fgetc( d88.fp ) != 0 ){ fseek( d88.fp, d88.secinfo.data, SEEK_SET ); break; } } } PRINTD( D88_LOG, " C : %d\n", d88.secinfo.c ) PRINTD( D88_LOG, " H : %d\n", d88.secinfo.h ) PRINTD( D88_LOG, " R : %d\n", d88.secinfo.r ) PRINTD( D88_LOG, " N : %d\n", d88.secinfo.n ) PRINTD( D88_LOG, " SectNum: %d/%d\n", d88.secinfo.secno, d88.secinfo.sec_nr ) PRINTD( D88_LOG, " Density: %s\n", d88.secinfo.density&0x40 ? "S" : "D" ) PRINTD( D88_LOG, " Del : %s\n", d88.secinfo.deleted&0x10 ? "DELETED" : "NORMAL" ) PRINTD( D88_LOG, " Stat : %02X\n", d88.secinfo.status ) PRINTD( D88_LOG, " Size : %d\n", d88.secinfo.size ) PRINTD( D88_LOG, " Offset : %d\n", (int)d88.secinfo.data ) } } //////////////////////////////////////////////////////////////// // 1byte 読込み //////////////////////////////////////////////////////////////// BYTE cD88::Get8( void ) { BYTE dat; PRINTD( D88_LOG, "[D88][Get8] -> " ) if( d88.fp && d88.table[d88.trkno] ){ // セクタの終わりに到達したら次のセクタをシークする // 最終セクタの次は同一トラックの先頭セクタに移動 // エラーセクタの場合は次のセクタに移動しない(Ditt!のエラー対応) if( d88.secinfo.offset >= d88.secinfo.size && !d88.secinfo.status ){ if( d88.secinfo.secno > d88.secinfo.sec_nr ) Seek( d88.trkno ); else ReadSector88(); } dat = fgetc( d88.fp ); d88.secinfo.offset++; PRINTD( D88_LOG, "%02X\n", dat ); return dat; } PRINTD( D88_LOG, "false(0xff)\n" ); return 0xff; } //////////////////////////////////////////////////////////////// // 1byte 書込み //////////////////////////////////////////////////////////////// bool cD88::Put8( BYTE dat ) { PRINTD( D88_LOG, "[D88][Put8] -> %02X(%02d:%02d:%02d:%02d)", dat, d88.secinfo.c, d88.secinfo.h, d88.secinfo.r, d88.secinfo.n ); if( d88.fp && d88.table[d88.trkno] && !d88.protect ){ // セクタの終わりに到達したら次のセクタをシークする // 最終セクタの次は同一トラックの先頭セクタに移動 if( d88.secinfo.offset >= d88.secinfo.size ){ if( d88.secinfo.secno > d88.secinfo.sec_nr ) Seek( d88.trkno ); else ReadSector88(); } // r+,w+,a+ で開いたファイルに対して読込みと書込みを切り替える場合は // 必ず fsetpos,fseek,rewind のいずれかの関数を実行する必要があるらしい fseek( d88.fp, 0, SEEK_CUR ); fputc( dat, d88.fp ); fseek( d88.fp, 0, SEEK_CUR ); d88.secinfo.offset++; PRINTD( D88_LOG, " ->OK\n" ); return true; } PRINTD( D88_LOG, " ->NG\n" ); return false; } //////////////////////////////////////////////////////////////// // シーク //////////////////////////////////////////////////////////////// bool cD88::Seek( int trackno, int sectno ) { PRINTD( D88_LOG, "[D88][Seek] Track : %d Sector : %d ", trackno, sectno ); if( d88.fp ){ d88.trkno = trackno; d88.secinfo.secno = 0; d88.secinfo.status = BIOS_MISSING_IAM; // トラックが無効ならUnformat扱い if( !d88.table[d88.trkno] ){ PRINTD( D88_LOG, "-> Unformat\n" ); return false; } PRINTD( D88_LOG, "-> Track:%d\n", d88.trkno ); // トラックの先頭をシーク fseek( d88.fp, d88.table[d88.trkno], SEEK_SET ); // 最初のセクタ情報読込み ReadSector88(); // 目的のセクタを頭出し if( sectno > 1 ){ fseek( d88.fp, (long)d88.secinfo.size, SEEK_CUR ); ReadSector88(); } PRINTD( D88_LOG, "-> OK\n" ); d88.secinfo.status = BIOS_READY; return true; } PRINTD( D88_LOG, "-> false\n" ); return false; } //////////////////////////////////////////////////////////////// // セクタを探す //////////////////////////////////////////////////////////////// bool cD88::SearchSector( BYTE c, BYTE h, BYTE r, BYTE n ) { PRINTD( D88_LOG, "[D88][SearchSector] C:%02X H:%02X R:%02X N:%02X ", c, h, r, n ); if( Seek( d88.trkno ) ){ // 目的のセクタが現れるまで空読み while( d88.secinfo.secno <= d88.secinfo.sec_nr ){ // IDをチェック if( ( d88.secinfo.c == c ) && ( d88.secinfo.h == h ) && ( d88.secinfo.r == r ) && ( d88.secinfo.n == n ) ){ PRINTD( D88_LOG, "-> Found\n" ); return true; } // 一致しなければ次のセクタ情報読込み fseek( d88.fp, (long)d88.secinfo.size, SEEK_CUR ); ReadSector88(); } } PRINTD( D88_LOG, "-> false\n" ); return false; } //////////////////////////////////////////////////////////////// // 次のセクタに移動する //////////////////////////////////////////////////////////////// bool cD88::NextSector( void ) { PRINTD( D88_LOG, "[D88][NextSector] Sector:%d ", d88.secinfo.secno ); if( d88.secinfo.sec_nr ){ int ssize = d88.secinfo.size - d88.secinfo.offset; // 現在のセクタ終端までのデータ数 if( d88.secinfo.secno == d88.secinfo.sec_nr ) // 最終セクタの次は同一トラックの先頭セクタに移動 Seek( d88.trkno ); else{ // 次のセクタ先頭まで移動してセクタ情報を読込み fseek( d88.fp, (long)ssize, SEEK_CUR ); ReadSector88(); } PRINTD( D88_LOG, "-> %d\n", d88.secinfo.secno ); return true; } PRINTD( D88_LOG, "-> false\n" ); return false; } //////////////////////////////////////////////////////////////// // 現在のCHRN取得 //////////////////////////////////////////////////////////////// void cD88::GetID( BYTE *C, BYTE *H, BYTE *R, BYTE *N ) { PRINTD( D88_LOG, "[D88][GetID] %02X %02X %02X %02X\n", d88.secinfo.c, d88.secinfo.h, d88.secinfo.r, d88.secinfo.n ); if( C ) *C = d88.secinfo.c; if( H ) *H = d88.secinfo.h; if( R ) *R = d88.secinfo.r; if( N ) *N = d88.secinfo.n; } //////////////////////////////////////////////////////////////// // 現在のセクタサイズ取得 //////////////////////////////////////////////////////////////// WORD cD88::GetSecSize( void ) { PRINTD( D88_LOG, "[D88][GetSecSize]\n" ); return d88.secinfo.size; } //////////////////////////////////////////////////////////////// // 現在のトラック番号取得 //////////////////////////////////////////////////////////////// BYTE cD88::Track( void ) { PRINTD( D88_LOG, "[D88][Track]\n" ); return d88.trkno; } //////////////////////////////////////////////////////////////// // 現在のセクタ番号取得 //////////////////////////////////////////////////////////////// BYTE cD88::Sector( void ) { PRINTD( D88_LOG, "[D88][Sector]\n" ); return (BYTE)d88.secinfo.secno; } //////////////////////////////////////////////////////////////// // 現在のトラック内に存在するセクタ数取得 //////////////////////////////////////////////////////////////// WORD cD88::SecNum( void ) { PRINTD( D88_LOG, "[D88][SecNum]\n" ); return d88.secinfo.sec_nr; } //////////////////////////////////////////////////////////////// // 現在のステータス取得 //////////////////////////////////////////////////////////////// BYTE cD88::GetSecStatus( void ) { PRINTD( D88_LOG, "[D88][GetStatus]\n" ); return d88.secinfo.status; } //////////////////////////////////////////////////////////////// // ファイル名取得 //////////////////////////////////////////////////////////////// char *cD88::GetFileName( void ) { return FileName; } //////////////////////////////////////////////////////////////// // DISKイメージ名取得 //////////////////////////////////////////////////////////////// char *cD88::GetDiskImgName( void ) { return (char *)d88.name; } //////////////////////////////////////////////////////////////// // プロテクトシール状態取得 //////////////////////////////////////////////////////////////// bool cD88::IsProtect( void ) { return Protected; }
d88.h
#ifndef D88_H_INCLUDED #define D88_H_INCLUDED #include <stdio.h> #define PRINTD(...) sprintf(__VA_ARGS__); typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; #define PATH_MAX 260 #define FGETBYTE(fp) ((BYTE)fgetc(fp)) #define FGETWORD(fp) ((WORD)(((BYTE)fgetc(fp))|((BYTE)fgetc(fp)<<8))) #define FGETDWORD(fp) ((DWORD)(((BYTE)fgetc(fp))|((BYTE)fgetc(fp)<<8)|((BYTE)fgetc(fp)<<16)|((BYTE)fgetc(fp)<<24))) #define FPUTBYTE(data,fp) fputc((data)&0xff,fp) #define FPUTWORD(data,fp) { fputc((data)&0xff,fp); fputc(((data)>>8)&0xff,fp); } #define FPUTDWORD(data,fp) { fputc((data)&0xff,fp); fputc(((data)>>8)&0xff,fp); fputc(((data)>>16)&0xff,fp); fputc(((data)>>24)&0xff,fp); } #define INITARRAY(arr,val) {for(int i=0; i<COUNTOF(arr); i++) arr[i] = val;} #define COUNTOF(arr) (int)(sizeof(arr)/sizeof((arr)[0])) // Disk BIOS Status #define BIOS_READY (0x00) #define BIOS_WRITE_PROTECT (0x70) #define BIOS_ID_CRC_ERROR (0xa0) #define BIOS_DATA_CRC_ERROR (0xb0) #define BIOS_NO_DATA (0xc0) #define BIOS_MISSING_IAM (0xe0) #define BIOS_MISSING_DAM (0xf0) //////////////////////////////////////////////////////////////// // クラス定義 //////////////////////////////////////////////////////////////// class cD88 { private: // D88 セクタ情報構造体 struct D88SECTOR { BYTE c; // ID の C (シリンダNo 片面の場合は=トラックNo) BYTE h; // ID の H (ヘッダアドレス 片面の場合は=0) BYTE r; // ID の R (トラック内のセクタNo) BYTE n; // ID の N (セクタサイズ 0:128 1:256 2:512 3:1024) WORD sec_nr; // このトラック内に存在するセクタの数 BYTE density; // 記録密度 0x00:倍密度 0x40:単密度 BYTE deleted; // DELETED MARK 0x00:ノーマル 0x10:DELETED BYTE status; // ステータス BYTE reserve[5]; // リザーブ WORD size; // このセクタ部のデータサイズ DWORD data; // データへのオフセット WORD offset; // 次に読込むデータのセクタ先頭からのオフセット WORD secno; // アクセス中のセクタNo D88SECTOR() : c(0), h(0), r(0), n(0), sec_nr(0), density(0), deleted(0), status(0), size(0), data(0), offset(0), secno(0) { INITARRAY( reserve, 0 ); } }; // D88 情報構造体 struct D88INFO { BYTE name[17]; // ディスクの名前(ASCII + '\0') BYTE reserve[9]; // リザーブ BYTE protect; // ライトプロテクト 0x00:なし 0x10:あり BYTE type; // ディスクの種類 0x00:2D 0x10:2DD 0x20:2HD DWORD size; // ディスクのサイズ(ヘッダ部+全トラック部) DWORD table[164]; // トラック部のオフセットテーブル(Track 0-163) D88SECTOR secinfo; // セクタ情報 FILE *fp; // FILE ポインタ int trkno; // アクセス中のトラックNo D88INFO() : protect(0), type(0), size(0), fp(NULL), trkno(0) { INITARRAY( name, 0 ); INITARRAY( reserve, 0 ); INITARRAY( table, 0 ); } }; D88INFO d88; // D88 情報 char FileName[PATH_MAX]; // ファイル名バッファ bool Protected; // プロテクトシール void ReadHeader88(); // D88 ヘッダ読込み void ReadSector88(); // D88 セクタ情報読込み public: cD88(); // コンストラクタ ~cD88(); // デストラクタ bool Init( char * ); // 初期化 BYTE Get8(); // 1byte 読込み bool Put8( BYTE ); // 1byte 書込み bool Seek( int, int = -1 ); // シーク bool SearchSector( BYTE, BYTE, BYTE, BYTE ); // セクタを探す bool NextSector(); // 次のセクタに移動する void GetID( BYTE *, BYTE *, BYTE *, BYTE * ); // 現在のCHRN取得 WORD GetSecSize(); // 現在のセクタサイズ取得 BYTE GetSecStatus(); // 現在のステータス取得 char *GetFileName(); // ファイル名取得 char *GetDiskImgName(); // DISKイメージ名取得 bool IsProtect(); // プロテクトシール状態取得 BYTE Track(); // 現在のトラック番号取得 BYTE Sector(); // 現在のセクタ番号取得 WORD SecNum(); // 現在のトラック内に存在するセクタ数取得 }; #endif // D88_H_INCLUDED