Jeans & Development / プログラミング https://www.rad51.net/jeans/ コンピューターのことなどを綴ったメモ (旧:目から鱗 w/SQLite) / プログラミング言語について ja Jeans CMS © Weblog http://backend.userland.com/rss https://www.rad51.net/jeans/skins/jeans/images/jeans2.gif Jeans & Development https://www.rad51.net/jeans/ MachiKania webの紹介 https://www.rad51.net/jeans/?itemid=965

MachiKania webは、MachiKania type M相当の物を、webアプリケーションとして実装したものです。以下のリンク先で、実行できます。

USキーボード通常画面
USキーボード大画面
JPキーボード通常画面
JPキーボード大画面

MachiKaniaはMicrochip製の32ビットのマイコンを利用した小サイズのマイクロコンピューターで、ケンケンさんと私Katsumiの共同開発です。type Ntype Ztype Mと三種類あります。NTSCのビデオモニターに、カラーの文字やグラフィックスを表示することが可能です。また、オブジェクト指向プログラミングが出来るBASICコンパイラーを搭載しており、類縁のマイコンと比較して、非常に高速にプログラムを実行することができます。

ここでは、MachiKaniaのシリーズの中から、現時点で最も高機能なtype Mとほぼ同様の動作を行うものをwebアプリケーションとして実装したものを、紹介したいと思います。記事最初の方にリンクがありますので、実行する際はそちらをクリックしてください。

32ビットのRISCのCPUであるMIPS32を、JavaScriptでエミュレートしています。MachiKaniaはMIPS32用のコードを作成するコンパイラー言語ですが、このMIPS32エミュレーターにより、このコンパイラーの作成するコードをそのまま実行できるようになっています。

基本的には、MachiKania type Mと使い方はほぼ同じです。ただし、以下の点でtype Mと異なります。

 1.実行速度が、type M のおよそ 1/20。
 2.I/O が使えない。
 3.80 文字幅表示でのフォントサイズが、6x8 ドット。
 4.MMC カードの代わりに、ZIP アーカイブを用いる。

BASIC プログラムを入力した後、「 F4 」キーを押せば、実行することができます。「 F1 」キーや「 F2 」キーで、プログラムをロードしたりセーブしたりすることができます。

MMC カードの代替になる ZIP アーカイブのやり取りは、「 Load Disk Image 」と「 Save Disk Image 」の二つのボタンで行います。MachiKania 上で SAVE したファイルを取り出したい場合は「 Save Disk Image 」を、PC 側で用意したファイルを MachiKania で使いたい場合は、「 Load Disk Image 」を押してください。

「 KanaLock 」ボタンは、JP-キーボードで使用する際、カナ文字を入力するときに押してください。

「 Break 」ボタンは、プログラムの実行を停止する場合に押してください。

「 Links 」を押せば、MachiKania のユーザーマニュアルの閲覧などの、さまざまなリンクが表示されます。

ソースコードはGitHubに上げています。ほとんどのファイルのライセンスはLGPLですが、一部LGPLライセンスでないもの(MachiKania ライセンスなど)が含まれているので、ご注意ください。]]>
プログラミング https://www.rad51.net/jeans/?itemid=965 Sat, 22 Aug 2020 16:57:20 PDT ITEM965_20200822
CP/KM web の紹介 https://www.rad51.net/jeans/?itemid=920 2015-12-24-cpm.png
CP/KM webは、ここから実行できます

cpm.htmlにブラウザでアクセスすると、以下のように表示されます。

start

黒い画面が、80文字×24行の、ディスプレイです。 その下に、CPUの動作速度を示しています。最速で、2 MHzで動作します。 PCではほとんどの場合2 MHzで動作しますが、モバイル環境では少し遅くなるかもしれません。 その下、「Set File」のボタンは、ディスクイメージファイル及び、CP/Mファイルをセットする場合に使用します(後ほど説明します)。 「Help」は、ヘルプファイルを表示させるときに使います。


ディスプレイに表示されているとおり、この状態ではまだCP/Mが使用できません。CP/Mの実行には、別途ディスクイメージファイルを準備する必要があります。表示の通り、 http://www.rad51.net/projects/cpm/ を開き、そのページで指定されている方法でディスクイメージファイルを取得します。


なお、ここで得られるディスクイメージファイルは、 CpmtoolsGUI というツールで編集することが出来ます。CpmtoolsGUIをダウンロードした時に添付されているdiskdefsファイルが、そのまま使えます。


ディスクイメージファイルが取得できれば、"Set File"を押します。ファイル選択ダイアログになるので、取得したディスクイメージファイルを選択します。このファイルは、上記サイトからダウンロードしたZIPアーカイブそのままでも、解凍した約8M bytesのファイルでも、どちらでも構いません。次のような画面になるはずです。

disksloaded

この状態で、CP/M ver 2.2が起動しています。A, B, C, Dの4つの仮想ディスクドライブがあり、容量はそれぞれ1952K bytesです。ただし、Aドライブのうち8192 bytesは、CPM.SYSの読み込み用に使用されています。


別途用意したファイルを仮想ディスクに取り込みたい場合、上述のCpmtoolsGUIを利用することが出来ますが、CP/KM上で行なうことも出来ます。CCP(シェル: Console Command Processor)が実行中であることを確認して、"Set File"ボタンを押して下さい。ファイル選択ダイアログになるので、任意のファイルを選択します。ただしファイル名は、8+3規則に合致しているものを用いて下さい。


CP/Mを使用した後、ディスクイメージファイルを保存することも出来ます。"Save Disks"ボタンを押すと、ZIPアーカイブダウンロードの状態になります。このZIPアーカイブは、起動時にディスクイメージとして、"Set File"して利用することが可能です。


ここで実行されているCP/Mは、ver 2.2で、62k systemと呼ばれているものです。以下に、メモリーマップを示します。

start

PCとしては、以下のような概念図で構成されるものを使用しています。CPUは、Z80です。DisplayとKeyboardについては8255(パラレルI/Oインターフェース)の機能は使用しておらず、Z80 CPUとHTML5との間で直接やりとりすることで行なっています。従って、8255のうちこのエミュレーターで使用している機能は、Port C2のみです。Port C2はプルアップされており、リセット時はアドレス0x0000-0x7FFFの領域にROMが選択されるようになっています。ROM中のコードではBIOSを0xF200からの領域に構築し制御をBIOSに移します。CP/Mの実行中は、Port C2に0を出力することにより、0x0000-0x7FFFの領域にRAMを使用するような仕組みです。

start

もともと、このCP/KM webは、CP/MをケンケンさんのPIC32MXの1ボードコンピューター上や、自作のZ80マイコンボードで実行させるプロジェクトを遂行する途中で、副産物として出来たものです。このHTML5によるエミュレーターが完成したことで、このプロジェクトの成功が現実的になってきたように思っています。来年は、私の趣味の時間にとってCP/M年になりそうな気配です。

CP/KMを開発するにあたり、以下のページからライブラリーを取得したり、参考にしたりさせて頂いています。有用な情報やソフトウェアーの提供、どうも有り難うございます。

CP/M Main Page(John Elliott さん)
 BIOSの構造などについて、詳しい解説があり、参考にさせて頂きました。

CP/Mコーナー(nekoさん)
 CP/Mのディスク構造について、詳しく書かれています。CP/KMでも、同一のフォーマットを利用させて頂いています。

6×8 ドット日本語フォント「k6x8」(門真 なむさん)
 80x24文字のディスプレイ用のフォントとして、使わせて頂きました。

JSZip(Stuart Knightleyさん)
 大容量(8M bytes)のファイルを快適に使用するために、このJavascriptによるZIPアーカイバーを使わせて頂いています。]]>
プログラミング https://www.rad51.net/jeans/?itemid=920 Thu, 24 Dec 2015 21:47:48 PST ITEM920_20151224
KM-Z80 web ver 1.0 https://www.rad51.net/jeans/?itemid=917 http://hp.vector.co.jp/authors/VA016157/kmz80web10/

2015-11-28-kmz80web.png

変更点
 ・FireFoxのバージョンアップに伴い、音が鳴らなくなっていたのを、鳴るように変更。
 ・KM-BASIC のバージョンを、0.6.8から0.7.0に変更。
 ・隠しコマンド ?ramsize=xx 及び ?dumplist=1 を追加。]]>
プログラミング https://www.rad51.net/jeans/?itemid=917 Sat, 28 Nov 2015 19:50:27 PST ITEM917_20151128
簡単にReg-Free COMを使うためのDLL https://www.rad51.net/jeans/?itemid=912 LabVIEWでJavaScriptを使う為の記事を書いた。これはもともとLabVIEWで機器用のAPI(DLLに記述されている)を大量に呼び出すためにやろうとしていることなので、JavaScript中でDLLが呼び出す必要があり、拙作のSFC miniを用いようとしている(仕事でこれを使うのは、始めて)。

通常、SFC miniを使う際には、これをレジストリーに登録して用いなければならない(管理者権限が必要)。ところが、職場のコンピューター(顕微鏡に繋がっている)の管理者権限を持っていないので、何かの変更のたびに管理者を呼んで作業してもらう必要があり、これは面倒である。なので、Registration-Free-COMとして、SFC miniを用いたい。

この用途のために、Script Users' and Programmer's Private Object Navigation(Suppon)というツールを作成したのだが、残念ながらこれは、suppon.exeというこのツールのexeのスレッド内でReg-Free COMを使うための物であり、LabVIEWから呼び出せない。

そこで、Supponの該当部分のコードだけを含むDLLを作成することにした。

コードは、以下の通り(ライセンスは、LGPL 2.1)。

regfreecom.cpp:
#include "./regfreecom.h"
#pragma comment(linker, "/entry:\"DllMain\"")
#pragma comment(linker, "/export:CreateObjectA=_CreateObjectA@8,PRIVATE")
#pragma comment(linker, "/export:CreateObjectW=_CreateObjectW@8,PRIVATE")

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

STDAPI CreateObjectA(LPCSTR file, LPCSTR clsidstr){
	return (HRESULT) CreateObject(file,clsidstr);
}

STDAPI CreateObjectW(LPWSTR file, LPWSTR clsidstr){
	return (HRESULT) CreateObject(file,clsidstr);
}

createobject.cpp:
#include "./regfreecom.h"

IDispatch* CreateObject(LPCSTR file, LPCSTR clsidstr){
	GUID clsid;
	int cchWideChar;
	LPWSTR lpWideCharStr;
	IDispatch* result;
	// clsidstr: Calculate UNICODE string length
	cchWideChar=MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,clsidstr,-1,0,0);
	if (!cchWideChar) return NULL;
	// clsidstr: Prepare memory for UNICODE
	lpWideCharStr=new wchar_t[cchWideChar+2];
	// clsidstr: Receive UNICODE
	if (!MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,clsidstr,-1,lpWideCharStr,cchWideChar)) {
		// Failed to construct UNICODE.
		return NULL;
	}
	// clsidstr: Receive CLSID
	if (NOERROR!=CLSIDFromString(lpWideCharStr,&clsid)) return NULL;
	delete lpWideCharStr;
	// file: Calculate UNICODE string length
	cchWideChar=MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,file,-1,0,0);
	if (!cchWideChar) return NULL;
	// file: Prepare memory for UNICODE
	lpWideCharStr=new wchar_t[cchWideChar+2];
	// file: Receive UNICODE
	if (!MultiByteToWideChar(CP_ACP,MB_ERR_INVALID_CHARS,file,-1,lpWideCharStr,cchWideChar)) {
		// Failed to construct UNICODE.
		return NULL;
	}
	// All done let's create the object
	result=CreateObject(lpWideCharStr,&clsid);
	delete lpWideCharStr;
	return result;
}


IDispatch* CreateObject(LPWSTR file, LPWSTR clsidstr){
	GUID clsid;
	if (NOERROR!=CLSIDFromString(clsidstr,&clsid)) return NULL;
	return CreateObject(file,&clsid);
}


IDispatch* CreateObject(LPWSTR file, const GUID* clsid){
	// General variables
	int i;

	// Load DLL & check DllGetClassObject
	HINSTANCE hDLL;
	void* Address;
	if (!(hDLL=LoadLibraryW(file))) return 0;
	if (!(Address=GetProcAddress(hDLL,"DllGetClassObject"))) return 0;

	// DllGetClassObject(&clsid,IID_IClassFactory,(LPVOID*)&ppv);
	IClassFactory* ppv=0;
	void *Stack1,*Stack2,*Stack3;
	Stack3=&ppv;
	Stack2=(void*)&IID_IClassFactory;
	Stack1=(void*)clsid;
	_asm push Stack3;
	_asm push Stack2;
	_asm push Stack1;
	_asm call Address;
	_asm mov i,eax;
	if (i!=S_OK || !ppv) return 0;

	// Class factory was succesfully obteined.
	// Lets get the object.

	IDispatch* ppvObject=0;
	if (S_OK!=ppv->QueryInterface(IID_IClassFactory, (void**) &ppv)) return 0;
	ppv->Release();
	if (S_OK!=ppv->CreateInstance(NULL,IID_IUnknown,(void**)&ppvObject)) return 0;
	ppv->Release();
	if (!ppvObject) return 0;
	if (S_OK!=ppvObject->QueryInterface(IID_IDispatch,(void**)&ppvObject)) return 0;
	ppvObject->Release();

	return ppvObject;
}

実装しているのは、CreateObjectAとCreateObjectWの2つの関数。それぞれ、ANSI文字列とUNICODE文字列での呼び出しに対応している。引数は、DLLファイルのファイル名とCLSIDで、CLSIDは"{1303599D-3028-4eac-97BB-D9370B88B2DA}"のような文字列で与える(この文字列が不明の場合は、COMをインストールした後にレジストリーを調べること)。戻り値は、オブジェクトのポインター。エラーの場合は、0が返る。

VS2008のファイル一式は、こちら(ライセンスは、LGPL v 2.1)。]]>
プログラミング https://www.rad51.net/jeans/?itemid=912 Wed, 22 Apr 2015 16:57:42 PDT ITEM912_20150422
LabVIEWでJavaScriptを使う https://www.rad51.net/jeans/?itemid=911
一般的な方法は、DLLを作成して、それをLabVIEWから呼び出すやり方のようだ。DLLは、CでもVBでも.NETでも、何でも良いようである。加えて、LabVIEW独自のCIN(Code Interface Node)という埋め込み機能もあるらしい。いずれの方法でも、LabVIEW以外のプログラミングソフト(Visual Studio等)を立ち上げて、そちらでプログラミングしておき、これをLabVIEWに取り込む方式である。これだと、2つのプログラミングソフトウェアを同時に扱わなければならず、かなり煩雑な操作になる。

テキストスタイルのプログラミングそのものも、LabVIEW上で出来れば、色々と便利そうだ。色々と調べてみると、どうやらJScriptとVBScriptなら、LabVIEWに取り込んで実行できるらしいということが分かった。作成したテストプログラムは、以下の通り。まずは、フロントパネル。
2015-04-19-front.png

次に、ダイアグラム(クリックで、拡大)。
2015-04-19-diagram.png
興味のある方は、こちらをダウンロード(LabVIEW ver 6.1用です)

JScriptを使うための操作は、以下の通り。

1. Front Panelに、Active X containerを作成。
2. 右クリックし、Insert Active X objectから、ScriptControl Objectを選択。
3. Diagramで"Property Node"を挿入し、IScriptControlを選択。PropertyにLanguageを指定して、文字列"Jscript"のシグナルを入力しておく。
4. "Invoke Node"を挿入し、IScriptControlを選択。PropertyにAddCodeを選択。メインプログラム文字列を入力
5. "Invoke Node"を挿入し、IScriptControlを選択。PropertyにEvalを選択。実行したい命令の文字列を作成して、入力。
6. Evalの実行結果は、右側から出力される。Variant値なので、Dataに変換して使用する必要がある。

このテストはLabVIEW ver 6.1で行なった。Invoke Nodeにバグがあるらしく、propertyにRunを選択すると、実行時にLabVIEWが落ちる。この不具合はLabVIEW 2010では見られないので、最新版のLabVIEWならEvalだけでなくRunも使える。ただし、parameterはなぜだか、一つしか取れない。引数を複数与えたい場合は、文字列をconcatenateして与える必要があるようだ。

テストプログラムでは、SFC miniを用いたDLLの呼び出しの確認コードも挿入されている。SFC miniを使わない場合は、該当部分(MessageBox云々の所)を削除もしくはコメントアウトして、実行して頂きたい。]]>
プログラミング https://www.rad51.net/jeans/?itemid=911 Sun, 19 Apr 2015 18:36:41 PDT ITEM911_20150419
32ビット整数の、定数での除算 https://www.rad51.net/jeans/?itemid=902
コンパイラーを用いて、10で割るコードを記述すると、アセンブリーを見たときに0xcccccccdを掛けて右35ビットシフトするコードになっていることがある。割り算はCPUにとって非常に高負荷な演算なので、掛け算とシフトに置き換えることで、高速化を図っているようだ。32ビットのほとんどのCPUでは、32ビットどうしの掛け算の結果を64ビット値として得、上位32ビットと下位32ビットを2つのレジスターに格納するようになっているので、「0xcccccccdを掛けて右35ビットシフト」は非常に効率がよい。

では、10以外の数値での割り算はどうかと、ネットで検索してみたが、包括的に解説している記事は見つからなかった。そこで、ちょっと調べてみた。

0xcccccccdという値は何かというと、0x100000000(4294967296)を10で割り、左3ビットシフトした値である。正確には3435973836.8であるが、整数値しか扱えなので切り上げて、3435973837(0xcccccccd)にしてある。右35ビットシフトは、0x800000000(34359738368)での割り算に等しい。従って、「0xcccccccdを掛けて右35ビットシフト」は、3435973837を掛けた後に34359738368で割る演算である。ここで、「3435973837を掛ける」のではなく「3435973836.8を掛ける」のであれば、数学的に正しい演算をしていることになるが、実際には0.2の誤差がある。この誤差が結果に影響を及ぼさなければ、「0xcccccccdを掛けて右35ビットシフト」は問題なく使えることになる。

「3435973836.8を掛ける」ではなく「3435973837を掛ける」場合、値が少し大きくなる可能性がある。コンピューターにおける整数型の演算では、割り算の結果を切り捨てにするから、数学的な演算の結果の少数部分が0.9になる場合に、異なる結果を生み出す可能性がある。逆に言えば、数学的な演算の結果の少数部分が0.9の場合でも常に同じ結果を生み出すのであれば、すべての整数値に関して正確に演算できることになる。

32ビット整数では、4294967296より小さな値しか扱わないから、この範囲内での最大の値、4294967289で同じ値になるかどうかを判断すればよい。4294967289*3435973837/34359738368=429496728.925であるので、切り捨てると同じ429496728になることが分かる。

では、他の整数値ではどうだろうかと、調べてみた。次のコードを、C++で実行。
#define div32(x,y,z) ((((unsigned long long)x)*((unsigned long long)y))>>z)

int _tmain(int argc, _TCHAR* argv[])
{
	unsigned int i,j,num,div,rem,mul,shift,valid,mul2,shift2;
	unsigned int res1,res2;
	FILE* fh;
	// Open a file to store the result.
	fh=fopen("result.txt","w");
	fprintf(fh,"num,mul,shift,valid\n");
	// Test several numbers used for div.
	for(num=1;num<65536;num++){
		// Determine the parameters
		div=(unsigned long long)0x100000000/(unsigned long long)num;
		rem=(unsigned long long)0x100000000%(unsigned long long)num;
		if (rem==0) {
			// num==2^n
			// Just shifting is the equlvalent of div.
			div=num;
			for(shift=0;(div=div>>1);shift++);
			fprintf(fh,"%d,0x00000001,%d,32\n",num,shift);	
		} else {
			// Determine values to use.
			mul=div;
			shift=32;
			while(mul<0x80000000){
				shift++;
				mul=((unsigned long long)((unsigned long long)1<<shift))/(unsigned long long)num;
			}
			mul++;
			// Check validity
			for(valid=32;valid;valid--){
				j=0xFFFFFFFF>>(32-valid);
				for(i=0;i<num;i++){
					// Check values "num" times from the largest ones.
					if (div32(j,mul,shift)!=j/num) break;
					j--;
				}
				if (i==num) break;
			}
			if (valid!=32) {
				// Try the other ways
				mul2=div;
				shift2=31;
				while(mul2<0x80000000){
					shift2++;
					mul2=((unsigned long long)((unsigned long long)1<<shift2))/(unsigned long long)num;
					mul2++;
					// Check validity
					for(i=0;i<num;i++){
						// Check values "num" times from the largest ones.
						j=0xFFFFFFFF-i;
						if (div32(j,mul2,shift2)!=j/num) break;
					}
					if (i==num) {
						// Found an available combination
						mul=mul2;
						shift=shift2;
						valid=32;
					}
				}
			}
			fprintf(fh,"%d,0x%x,%d,%d\n",num,mul,shift,valid);	
		}
	}
	fclose(fh);
	return 0;
}

結果の一部を、下に示す。
num,mul,shift,valid
1,0x00000001,0,32
2,0x00000001,1,32
3,0xaaaaaaab,33,32
4,0x00000001,2,32
5,0xcccccccd,34,32
6,0xaaaaaaab,34,32
7,0x92492493,34,31
8,0x00000001,3,32
9,0xe38e38e4,35,32
10,0xcccccccd,35,32
11,0xba2e8ba3,35,32
12,0xaaaaaaab,35,32
13,0x9d89d89e,35,32
14,0x92492493,35,31
15,0x88888889,35,32
16,0x00000001,4,32
17,0xf0f0f0f1,36,32
18,0xe38e38e4,36,32
19,0xd79435e6,36,31
20,0xcccccccd,36,32

"num"で割る場合は、"mul"の値を掛けて、右"shift"ビットシフトすればよい。"valid"で示されたビット幅の整数値に関して、エラー無く演算できる。65536までの結果は、こちらを参照されたい

結果を見て分かるように、すべてのケースに於いて、31ビット長の符号無し整数に関して、有効である。符号付き整数で考えれば、数値はすべて31ビット長で表現されるから、32ビット符号付き整数値のすべてに対応できることになる。C++での確認では、少なくとも、65536までの割り算には対応できる事が確認できた。

この方法が、32ビット符号付き整数・31ビット符号無し整数のすべてに関して有効であることは、数学的には、以下のように証明できる。
2014-07-22-math1.png
2014-07-22-math2.png
]]>
プログラミング https://www.rad51.net/jeans/?itemid=902 Tue, 22 Jul 2014 13:54:24 PDT ITEM902_20140722
KM-Z80 web ver 0.9 https://www.rad51.net/jeans/?itemid=881 ver0.9を公開。

2013-06-18-kmz80web.png

以前公開したバージョンは、PC上では動作するものの、ハードウェアキーが無くソフトウェアキーだけを用いている多くのMobile環境では、キー入力を受け付けることが出来なかった。

そういった環境でもソフトウェアキーを表示させれば、キー入力を受け付けるだろうと、Twitter上でyasuho68kさんに手伝って頂いてうまく行くかどうか調べた結果、どうやら駄目だということに。キーの入力は、JavaScriptでonkeydownとonkeyupの2つのイベントをトラックしているが、ソフトキーからの入力では、これらのイベントは発生しないらしい。

他方で、ZK-80 webの方は、Mobile環境で動作確認済み。ここで使ったのと同じテクニックを、KM-Z80 webにも使えばよいだろうと言うことで、そのように。要するに、ソフトウェアキーをJavaScriptで実装してしまおうというのものだ。MZ-80Kのキーボードは16x5のサイズなので、1キーを20x20の大きさにすれば、320ピクセルで構成されているディスプレイと横幅がぴたり合う。しかも、一つ一つのキーの印字も、ディスプレイで用いているフォントを用いることが出来、ちょうど良い大きさで綺麗に仕上がった。

このソフトウェアキーを実装したことで、別の改善点も。私はUSキーボードを用いていて、それ用にキーの割り当てをしてあったのだが、Twitter上でair_variableさんから、JISキーボードでは「=」が入力できないとのご指摘が。なるほど、これは考えておかないといけなかったと思ったが、ソフトウェアキーを実装したことで、そういった場合でもすべての文字の入力に対応することが出来た。

これで、Mobile環境でも実行できるようになったし、MZ-80Kのすべてのキー入力(カナや絵も含む)も出来るようになったので、ほぼ完成と言ったところ。

KM-Z80 web ver 0.9 の実行は、こちらから。 ]]>
プログラミング https://www.rad51.net/jeans/?itemid=881 Tue, 18 Jun 2013 18:55:03 PDT ITEM881_20130618
TK-80のHTML5によるエミュレーター https://www.rad51.net/jeans/?itemid=868 2012-11-04-zk80web.png
ZK-80 web ver 0.4 スナップショット(起動はこちら
先月、HTML5を用いてMZ-80Kのエミュレーターを作成した記事を書いた。

キモの部分は、CPUのエミュレーションとディスプレイの表示なので、そこがクリアできていればいろいろと応用が利く。CPUは当時の流行のZ80で、PC-8001・PC-8801などがそれに相当する。

また、Z80は、8080という当時の主流だったCPUのアッパーコンパチブルなので、そういったコンピューターにも応用可能である。

そんな中に、TK-80という、30年以上前に発売されていた1ボードマイコンがある。

TK-80といえば、私が中学生だったか高校生だった頃、雑誌の広告に載せられていたのを見て、購入寸前の所まで行っていたマイコンである(実際には、TK-80E)。触ったことはないのだが、どんな物なのだろうかという興味はずっとあった。

で、手元にZ80 CPUのエミュレーターがある。TK-80の仕様も、小松さんのNEC TK-80というページとか、AVRとZ80でTK-80のレプリカを制作なさった方の資料ページとか、中日電工の菱田さんのZ80マイコン組立キットND80ZⅢのページなどを参考にすると、だいたい分かる。なら、作ってしまおうということになった。

初期のバージョンでは、中日電工のND80ZⅢの取り扱いマニュアルを参考にモニタープログラムを作成し、使わせていただいた。そうして出来たエミュレーターを触ったのが、私の初めてのTK-80の体験である。

10年ほど前に、アスキー出版社から「復活!TK-80」という本が出版されていて、それを通じてTK-80に触れた方も、大勢いらっしゃると思う。残念ながら私は、それが出版された時点でその本の存在に気がつかず、今となっては絶版となり手に入らなくなっている。

そんな中で、自作のエミュレーターでTK-80に触れたときの感覚は新鮮で、もしこれを中学生・高校生の時に手にしていれば、人生が変わっていた可能性が大である。エンジニアになっていたかも知れない。

閑話休題、最初に出来たTK-80のバージョンでは、ほぼすべてのコードがスクラッチから書いた物なので、GPLもしくはLGPLで公開したくなった。それには、モニタープログラムの著作権の問題がある。MZ-80の場合は、フリーのライセンスのモニタープログラムが存在したので、それを使ったが、TK-80ではそういった物は公開されていない。

そこで、モニタープログラムも自作することにした。仕様はだいたい分かっているし、そんなに大規模な物でもないので、それほど時間をかけずに完成することが出来た。決して褒められたアセンブリではないかも知れないけれど、一応動くので、良し。そんなで出来た最初の公開バージョンが、これである
2012-11-04-mk80web.png
これで楽しんでいるうち、ふと思って、5x5キーボードの部分の画像をWikipediaのTK-80高解像度写真の物と取り替えてみた(非公開バージョン)。これが、素晴らしかった!当然ながら、次の公開バージョンではこういった物にしたくなる。それに、キーを叩くときの音も入れば最高である。そこで、TwitterでLGPLで使えるキー音をどなたかから頂けないか、募集してみた。
2012-11-04-wanted.png

すると、さすがTwitter。本当にそれを供与していただけたのだ。有り難うございました、@triringさん(それに、RTしていただいた方々も)。加えて、供給していただいたファイルの中には、キーボードも含めた画像もある。これらを用いて、この記事の冒頭にあるインターフェースのエミュレーターを作成することが出来た。しかも、著作権はすべてのファイルに関して、LGPLにすることが出来た。11月4日現在の最新版は、ver 0.4

これは、いずれPIC(PIC32MX)を使った1チップで、ワンボードに仕上げる予定である。同様の物はYouTube上に見つけることが出来るが、私の場合はあくまで1チップが目標。(追記:PIC32MX120F032Bを使ったボードの製作例は、こちらです。)

ところで、今やTK-80の実機はほとんど手に入れることが出来ない物になっているが、それと同等以上のマイコンが、現在でも販売されていて入手することが出来る。実機に興味のある方は、中日電工のZ80マイコン組立キットND80ZⅢの購入を考えてみるのも、良いかも知れない。]]>
プログラミング https://www.rad51.net/jeans/?itemid=868 Sun, 04 Nov 2012 19:23:51 PST ITEM868_20121104
HTML5を使ってみた https://www.rad51.net/jeans/?itemid=867
最近、MZ-80K(SHARPが30年少し前に発表したパソコン)のレプリカを作ったり、それ用のBASICを開発したりで、これに少しはまりこんでいる。

そこで、出来て1年ほどの技術を用いて30年以上前のコンピューターを再現するのもおもしろかろうと思い、HTML5の勉強を兼ねてMZ-80Kのエミュレーターを作ってみた。「KM-Z80 web」と名付けている。

2012-10-05-km-basic.png

現物及び使用方法の説明、ソースコードのダウンロードなどはこちら

使い方などは別のページに譲るとして、ここではHTML5の技術に関することなどを書いてみたい。

HTML5のうち、利用した技術は次の通り。
  • Canvas
  • FileReader
  • Web Audio API もしくは Audio Data API
Canvasはたぶん、HTML5の基本ともいえる技術のようだ。ここでは、基本中の基本である2次元データーとして扱っている。KM-Z80 webでのソースコードでは、以下の部分がそれに相当する。
display.init=function(){
	// Set the contexts.
	this.context=document.getElementById("display").getContext("2d");
	this.ledContext=document.getElementById("led").getContext("2d");
	// Load the font data. See also fonts.onload event.
	this.fonts.src="./fonts.png?"+ new Date().getTime();
};
display.fonts.onload=function(){
	display.onload();
}
// display.onload will be called after sucessfull loading of font PNG image.
display.onload=function(){
	// Show the PNG image
	this.context.drawImage(display.fonts,0,0);
	// Construction of images for font
	for (h8=0;h8<16;h8++) {
		for (l8=0;l8<16;l8++) {
			this.font[h8*16+l8]=this.context.getImageData(l8*8,h8*8,8,8);
		}
	}
	// Construction of images for LED
	this.ledImage[1]=display.context.getImageData(128,0,16,16);
	this.ledImage[0]=display.context.getImageData(128,16,16,16);
	// Clear display
	var i;
	for (i=0;i<1000;i++) {
		this.write(i,0);
	}
	// Construct blank canvas
	var blank=document.getElementById("blank");
	blank.getContext("2d").putImageData(this.context.getImageData(0,0,320,200),0,0);
	// Show LED (green)
	this.led(1);
	// All done let's start Z80
	start();
};
display.write=function(addr,data){
	var posy=0;
	var posx=addr & 0x3ff;
	if (1000<=posx) return;
	while(40<=posx) {
		posy++;
		posx-=40;
	}
	this.context.putImageData(this.font[data],posx*8,posy*8);
};
display.led=function(green){
	this.ledContext.putImageData(this.ledImage[green?1:0],0,0);
}
要するに、次のような画像をキャンバス上に貼り付けて、その一部を256回、フォントデーターとして取り込んで保存(getImageData)している。表示するときはputImageDataを用いれば、高速に処理がなされる。
2012-10-05-fonts.png

FileReaderでは、動的HTMLページを用いなくても、クライアントサイドのファイルをデーターとしてJavaScriptの中に取り込むことが出来るのが面白い。この特性を利用して、KM-Z80 webは、静的なHTMLファイルとして構築することが出来た。セキュリティーに関してはブラウザ側に丸投げすることが出来るので、応用範囲は広いだろうと思う。ここでは、以下のように使っている。
file.loaded=function(obj){
	// This will be called when a file is uploaded.
	// If FileReader API is not supported, following code will fail.
	var fr = new FileReader();
	fr.onload = function () {
		var data=new Uint8Array(fr.result);
		file.update(data);
	};
	fr.readAsArrayBuffer(obj.files[0]);
	obj.style.display='none';
};
file.update=function(data){
	// This will be called when a file is sucessfully loaded by FileReader API.
	var header=new Array();
	var body=new Array();
	var i;
	// First 128 bytes contain header info.
	for (i=0;i<128;i++) {
		header[i]=data[i];
	}
ここでは、バイナリファイルを扱うので「readAsArrayBuffer」を用いた少し複雑なコードになっているが、テキストでよいのであれば、「readAsText」を用いてもう少し簡単に記述できるようだ。あと、これはHTML5ではないのだろうけれど、以下のようなリンクを作成することで、バイナリオブジェクトをローカルに保存することを可能にしている。
<a href="data:application/octet-stream,%00%01%02">download</a>

最後は音源関連。これは残念ながら、対応しているブラウザが限られており、ブラウザごとにAPIが異なる。今のところ、FireFoxとChromeで動作確認をしているが、それぞれ、Audio Data API と Web Audio API という、2つの異なるAPIを使うことになった。詳しくは、ソースコードのaudio.jsを参照されたい。MZ-80Kの場合は矩形波で良いので、そこの部分は楽にプログラミングできた。

ところで、このKM-Z80 webには、これ用にスクラッチから開発した、JavaScriptによるZ80エミュレーターが搭載されている。以下、ソースコード(Z80.js)から。
/*
	Public methods:
	z80.reset();
	z80.setSpeed(clk);
	z80.getMicroSec();
	z80.exec(msec);
	z80.interrupt();
	z80.nmr();
	
	Required outside methods:
	Methods written in z80functions.js.
	memory.read(addr);
	memory.write(addr,data);
	io.read(addrL,addrH);
	io.write(addrL,addrH,data);
	z80.events(); // optional
*/

使い方としてはわかりやすくできていると思うので、興味のある方はどうぞ。LGPLです。]]>
プログラミング https://www.rad51.net/jeans/?itemid=867 Fri, 05 Oct 2012 13:19:28 PDT ITEM867_20121005
PHPにおけるビット演算と、bcand(), bcor(), bcxor()関数 https://www.rad51.net/jeans/?itemid=813
そこで、PHPにおいて32ビット整数のビット演算がちゃんとできるのかどうかを調べることにした。

確認は、下記コードにて。ビット演算すると、32ビット整数として扱ったような符号の反転が起こるようだ。結果が負の数になった場合に4294967296を加えることで、正確に演算できるらしい。4294967296を足した後は64ビット浮動小数点として扱われているように見える。

なお、検証コードでは、bcadd, bcor, bcxorという3つのBC Math関数を作成して、結果の比較を行った。
<?php

for($i=0;$i<100000;$i++){
	$r1=rand(0,0x7fffffff)+rand(0,0x7fffffff)+rand(0,1);
	$r2=rand(0,0x7fffffff)+rand(0,0x7fffffff)+rand(0,1);
	test($r1,$r2);
}
function test($r1,$r2){
	echo "$r1 $r2\n";

	$a1=$r1 & $r2;
	if ($a1<0) $a1+=4294967296;
	$a1=(string)$a1;
	$a2=bcand($r1,$r2);
	if ($a1!=$a2) echo('error!');

	$a1=$r1 | $r2;
	if ($a1<0) $a1+=4294967296;
	$a1=(string)$a1;
	$a2=bcor($r1,$r2);
	if ($a1!=$a2) echo('error!');

	$a1=$r1 ^ $r2;
	if ($a1<0) $a1+=4294967296;
	$a1=(string)$a1;
	$a2=bcxor($r1,$r2);
	if ($a1!=$a2) echo('error!');
}

function bcand($a,$b){
	return _bc($a,$b,'&');
}
function bcor($a,$b){
	return _bc($a,$b,'|');
}
function bcxor($a,$b){
	return _bc($a,$b,'^');
}
function _bc($a,$b,$mode){
	$a=(string)$a;
	$b=(string)$b;
	$res='0';
	$op='1';
	while($a!='0' || $b!='0'){
		$aa=(int)bcmod($a,'65536');
		$a=bcsub($a,$aa);
		$a=bcdiv($a,'65536');
		$bb=(int)bcmod($b,'65536');
		$b=bcsub($b,$bb);
		$b=bcdiv($b,'65536');
		switch($mode){
			case '&':
				$temp=$aa & $bb;
				break;
			case '|':
				$temp=$aa | $bb;
				break;
			case '^':
				$temp=$aa ^ $bb;
				break;
			default:
				exit(__FILE__.__LINE__);
		}
		$temp=bcmul((string)$temp,$op);
		$res=bcadd($res,$temp);
		$op=bcmul($op,'65536');
	}
	return $res;
}
]]>
プログラミング https://www.rad51.net/jeans/?itemid=813 Mon, 11 Oct 2010 13:08:17 PDT ITEM813_20101011