マルチスレッドOS for Z80

Z80で動く、Windows APIライクなマルチスレッドOSです。


○ Z80開発に新感覚のプログラミングを!

 これは、Z80CPUを、マルチスレッド制御するためのライブラリです。最大の特徴は、API郡が、Windowsライクだということです。そのため、Windows環境でのマルチスレッド・プログラムの経験があれば、誰にでも使うことができます。スリープ、イベント、ミューテックスなども実装しています。ハードウエアも、インターバル割り込み(NMIは不可)が可能であれば、動作します。ライブラリのサイズは約6.4キロバイトです。

 使用には、別途、Z80のCコンパイラが必要です。開発にはHITECH-Cコンパイラを使用しましたが、インラインアセンブラの使用法について修正すれば、他のCコンパイラでも動くと思います。(SYSTEM-LOADのMini-Cコンパイラではダメ。構造体が使えないといけないので。)

○ マルチスレッド制御の利点

 複雑な制御を行う場合、マルチスレッドでは、プログラムのトレースや、ポーリングなどが楽になり、開発の能率が向上する場合があります。

○ 問題点

 スレッドごとに、スタック領域を確保してやらなくてはならないので、最大64KBしかメモリを積めないZ80では厳しいです。メモリが足らない場合は、バンク切り替え方式で追加して、いろいろ書き換える必要があります。

 実はOSというのは言い過ぎで、メモリ管理すらやってくれません。実は、コンパイラに付属のライブラリのmallocをそのまま使っています。長時間使っていると、メモリ分断が起きて確保に失敗する場合も出てくるのかもしれません。実装してみようと思ったけど、ヒープの先頭アドレスとか分からなかったし・・・。

 リアルタイム性が低いです。理由は、速くなくてはいけないところにC言語を使っているのと、API郡がリエントライトでないということです。APIの中では、割り込み禁止をかけてスレッド切り替えが起こらないようにしてから、内部のデータを操作しているので、このようになります。

○ API郡

Windows API の関数と、引数や機能などは完全に一致してはいません。

typedef void *HANDLE;
typedef WORD (*THREAD_PROC)(void *pParam);

BOOL PrepareForThread(BYTE CurrentPriority);
HANDLE CreateThread(THREAD_PROC pThreadProc, void *pThreadParam, WORD StackSize, BYTE Priority, BOOL IsSuspended);
WORD ResumeThread(HANDLE hThread);
WORD SuspendThread(HANDLE hThread);
void ExitThread(WORD ExitCode);
BOOL TerminateThread(HANDLE hThread, WORD ExitCode);
WORD GetExitCodeThread(HANDLE hThread);

void Sleep(WORD Miliseconds);
void WaitForSingleObject(HANDLE hObject, WORD Timeout);
void WaitForMultipleObjects(BYTE Objects, const HANDLE *phObjects, BOOL IsWaitAll, WORD Timeout);
BOOL CloseHandle(HANDLE hObject);
HANDLE CreateEvent(BOOL IsManualReset, BOOL InitialState);
BOOL ResetEvent(HANDLE hEvent);
BOOL SetEvent(HANDLE hEvent);
HANDLE CreateMutex(BOOL IsInitialOwner);
BOOL ReleaseMutex(HANDLE hMutex);

○ コーディング例

#include "thread.c"

WORD ThreadProc(void *pParam)
{
	int i;
	int Count = (int)pParam;	/* 引数を繰り返し回数として使います */

	for(i = 0; i<Count; i++){
		out(PIOA, 0x00);	/* (例えば)LEDの消灯 */
		Sleep(1000);	/* 1秒待つ */
		out(PIOA, 0xFF);	/* (例えば)LEDの点灯 */
		Sleep(1000);
	}

	return 0;	/* スレッドの終了コード */
}

void main(void)
{
	HANDLE hThread;
/*
	マルチスレッドのための準備。
	現在の、このスレッドを優先順位0(最も高い)に設定します。
*/
	PrepareForThread(0);
/*
	スレッドを生成します。
	LEDの点滅を30回繰り返します。(引数で指定)
	スタックサイズは256バイト、優先順位は1で、即実行。
*/
	hThread = CreateThread(ThreadProc, (void *)30, 0x100, 1, FALSE);

	/* スレッドが終了するのを待ちます。 */
	WaitForSingleObject(hThread, WAIT_INFINITE);

	if(GetExitCodeThread(hThread)){
		/* 異常終了の場合の処理 */
	}

	/* 使い終わったハンドルはクローズします。 */
	CloseHandle(hThread);
}

 スレッドに渡す引数は、必ずvoid *型のポインタでなくてはいけないので、数値を渡す場合にはキャストするか、構造体へのポインタを渡します。この例では、マルチスレッドの利点を生かしたプログラムではありません。(スレッドを作って、その後すぐに終了を待っているので、無駄。)あと、この例ではスレッドは常に正常終了しています。

○ 優先順位について

 数字が小さいほど、優先順位が高いです。優先順位が自分より高いスレッドがひとつでも動作している(眠っていない)ときには、それより優先順位が低いスレッドは一切実行されません。同じ優先順位のスレッドは交互に実行されます。

○ 必要なハードウエア

 インターバル割り込みのために、Z80-CTCを使います。チャネル0のアドレスは、CTC0とします。秋月電子の AKI-80 なら、そのまま動きます。

○ 必要なソフトウエア

 Z80 対応のCコンパイラが必要です。開発環境は HI-TECH C COMPILER (Z80) Ver 7.11ですが、他のコンパイラでも動くと思います。(未確認)ただし、インラインアセンブラの文法などは、違う場合があるので、変更が必要です。また、プログラム中で、C 言語標準関数の malloc, free を使っていますので、これも必要です。(または、同等の関数を作るか)

○ 必要な改造(カスタマイズ)

 使用するCPUのクロック周波数によって、インターバル割り込みの割り込み間隔が1msになるように、CTCの時定数(ダウンカウンタ)を書き換える必要があります。

○ カスタマイズ

○ Z80-CTCのポート・アドレスの変更

 CTC0というマクロに、Z80-CTCのポート・アドレスを束縛してください。(初期状態ではAKI80.hで定義しています。)

○ 使用するクロック周波数に合わせての変更

 使用するクロック周波数に合わせて、Z80-CTCが1ms間隔でインターバル割り込みするように、CTCの時定数を変更する必要があります。PrepareForThread関数中を変更してください。

○ 割り込みの変更

 インターバル割り込みの方法は変更してもいいです。PrepareForThread関数中を変更してください。割り込み処理のハンドラには、SwitchThread関数を指定して下さい。さらに、1ms間隔で割り込まなくてもいいのですが、1msでないと、Sleep関数による待ち時間にくるいがでます。(CTC を使った場合でも、正確に1msでないので、Sleep関数は正確な待ちはできません。)

○ マクロによるカスタマイズ

マクロの定義を変更してコンパイルすることにより、多少のカスタマイズはできます。

thread.h
#define PRIORITY_COUNT 14
#define IDLE_THREAD_PRIORITY 13 /* Lowest Priority */
#define MAX_PRIORITY 12

thread.c
#define MAX_WAIT_THREAD 10

○ PRIORITY_COUNT

 スレッド優先順位の数を定義します。その際に、IDLE_THREAD_PRIORITYにはPRIORITY_COUNT-1の値をMAX_PRIORITYにはPRIORITY_COUNT-2の値を設定して下さい。

○ MAX_WAIT_THREAD

 複数のスレッドで、WaitForSingleObject や、WaitForMultipleObjectsを実行する場合、一度に待たせられるスレッドの数には限度があります。このマクロではその数を定義します。数が多いとメモリを消費します。(ソース参照)

○ 使わないAPIをソースからカットしてコンパイルすればサイズを小さくできます。ただし、APIの中から他のAPIを呼び出している場合もあるので、気をつけましょう。

○ ダウンロード

z80os.lzh

(内容)


〜〜〜 2000年10月 〜〜〜