和製MESSでPC-6001 その5

DISK関連は「PC6001V」をベースにすることにしました。

http://www.papicom.net/p6v/

メモリマップ、I/Oマップはここら辺を参考にしました。

http://p6ers.net/hashi/n6xbasicref.html

f:id:tanam:20130729002740p:image:w360

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