目次

リアルタイムモニタ移植

 自作のリアルタイムモニタであるUSO
 (Unvoiced Shadow Operating system)を
 Z80に移植してみます。

 USOは、すべてC言語で記述可能ですが
 10msごとのタイマー割込みが必要と
 なるのでモード2割込みを利用。

 リアルタイムモニタは、高速なことが求められる
 ので、ここではアセンブリ言語で記述します。

 リアルタイムモニタは、タスクの状態を切替える
 ことが仕事なので、タスクに関係する情報を格納
 するエリア=TCBを使わずに処理。

 ひとつのタスクに、ブロックエリアを割り当てしないで
 3バイトの変数で管理します。

 1バイトで管理できるのは8ビットとなるので、最大で
 8タスクを管理します。

 4タスクや5タスクの場合でも
 ダミータスクを用意し8タスクに
 します。

 USOではタスクは、次の4状態のどれかになります。

 これらの状態は、システムコールを利用して
 切り替えます。状態遷移は、下図に従います。



 USOでは、WAIT状態は「時間待ち」としています。
 「時間待ち」でない「待ち」はSUSPENDとします。

 「時間待ち」は、10msのN倍(N=1~65535)とします。

 ここまで仕様を決めると、ディスパッチャ、スケジューラ
 は、次のような単純なアセンブリ言語コードになります。

TSK_ID0	EQU 0
TSK_ID1	EQU 1
TSK_ID2	EQU 2
TSK_ID3	EQU 3
TSK_ID4	EQU 4
TSK_ID5	EQU 5
TSK_ID6	EQU 6
TSK_ID7	EQU 7

	call 	INIT_USR
	call 	INIT_USO
	; set pointer
	ld	hl,READY
	ld	ix,RUN_TSK
	; set first task ID
	xor	a
	ld	(ix),a
	; enable interrupt
	ei
MAIN:
	; 10ms handling
	ld	a,(XFLAGS)
	; check WFALG
	bit	WFLAG,a
	jr	z,MAIN1
	; update WCOUNTER
	call	THANDLER
	; perform task
MAIN1:
	; get READY
	ld	c,(hl)
	; TASK0
	bit	TSK_ID0,c
	call	nz,TSK0_PROC
	inc	(ix)
	; TASK1
	bit	TSK_ID1,c
	call	nz,TSK1_PROC
	inc	(ix)
	; TASK2
	bit	TSK_ID2,c
	call	nz,TSK2_PROC
	inc	(ix)
	; TASK3
	bit	TSK_ID3,c
	call	nz,TSK3_PROC
	inc	(ix)
	; TASK4
	bit	TSK_ID4,c
	call	nz,TSK4_PROC
	inc	(ix)
	; TASK5
	bit	TSK_ID5,c
	call	nz,TSK5_PROC
	inc	(ix)
	; TASK6
	bit	TSK_ID6,c
	call	nz,TSK6_PROC
	inc	(ix)
	; TASK7
	bit	TSK_ID7,c
	call	nz,TSK7_PROC
	; return first state
	xor	a
	ld	(ix),a
	;
	jr	MAIN

 やっていることは、単純。

 READYに保存されているタスク番号を取得して
 対応するタスクにCPUを使わせます。該当する
 タスクがREADYなら、ビット位置で1が入って
 いると仕様を決めました。

 USOでは、タスクは8個としているので
 タスク0から7のタスク状態をスキャンし
 READYならCPUを使わせます。

 タスク動作は、サブルーチンとして記述。

 タスク7の状態を調べ、対応処理を実行したなら
 また、タスク0から状態を調べていきます。

 タスク状態をスキャンするため、1バイト
 の変数領域TSKNUMを利用。

 タスク状態を調べる前に、タイマー割込みで10ms経過が
 通知されていないかを調べています。

 10ms経過は、フラグTFLAGの論理値で判定します。
 フラグがセットされていると、10ms経過している
 ので、タスクごとに用意してある、カウント値を
 デクリメント。

 カウント値が0になったタスクは、状態をWAITから
 READYに遷移します。

 システムコールは、以下の4種を用意。

 アセンブリ言語では、コーリングシーケンスを
 決めておかないと、CPUの暴走になります。

 各システムコールのコーリングシーケンスを
 逐次定義してみます。

 RSM_TSK

  システムコールRSM_TSKは、タスク番号を
  指定して、状態をSUSPENDからREADYに遷移。

  タスクの番号を与えます。

  次のように番号を与えてからコール。

		ld	a,TSK_ID2
		ld	(GR0),a
		call	RSM_TSK

  GR0からGR3の4バイトをシステムコールする場合に
  使います。4バイトは、以下のように役割を決めて
  あります。

 SUS_TSK

  システムコールSUS_TSKは、タスク番号を
  指定して、状態をREADYからSUSPENDに遷移。

  タスクの番号を与えます。

  次のように番号を与えてからコール。

		ld	a,TSK_ID4
		ld	(GR0),a
		call	SUS_TSK

 SLP_TSK

  システムコールSLP_TSKは、タスク自身が
  状態をREADYからSUSPENDに遷移させます。

  パラメータなし。

  パラメータなしは、タスクの中でシステムコールを
  使う前に設定しないでよいということでディパッチ
  の担当部分では、RUN_TSKという変数でタスクIDを
  管理します。

      SLP_TSK

 WAI_TSK

  システムコールWAI_TSKは、タスク自身が
  状態をREADYからWAITに遷移させます。

  10msの倍数を与えます。

  次のように番号を与えてからコール。

		; set counter
		ld	de,400h
		; lower
		ld	(GR1),e
		; upper
		ld	(GR2),d
		call	SUS_TSK

 コーリングシーケンスを定義したので、アセンブリ言語の
 コードを定義します。

 RSM_TSK

  タスクIDをビット位置とみなして、変数READYの該当ビットをセット。
  変数SUSPEND、WAITQの該当ビットをリセットして実現。

  ビット位置でのセット状態は、次の8種。

  01h 02h 04h 08h 10h 20h 40h 80h

  これらをメモリ上にテーブルで用意しておき、それを
  引出してきて、論理演算でREADY、SUSPEND、WAITQの
  該当ビットの論理値を操作。

RSM_TSK:
	; store
	push	af
	push	bc
	push	de
	push	hl
	; get task ID
	ld	hl,GR0
	ld	a,(hl)
	ld	c,a
	; get bit pattern
	ld	hl,BPAT
	ld	d,0
	ld	e,a
	add	hl,de
	ld	b,(hl)
	; update READY (set target bit)
	ld	a,(READY)
	or	b
	ld	(READY),a
	; update SUSPEND (reset target bit)
	ld	a,(SUSPEND)
	xor	b
	ld	(SUSPEND),a
	; update WAITQ (reset target bit)
	ld	a,(WAITQ)
	xor	b
	ld	(WAITQ),a
	; resume
	pop	hl
	pop	de
	pop	bc
	pop	af
	;
	ret

 SUS_TSK

  タスクIDをビット位置とみなして、変数SUSPENDの該当ビットをセット。
  変数READY、WAITQの該当ビットをリセットして実現。

  処理としては、RSM_TSKとほぼ同じで、該当ビットのセットを
  変数SUSPENDにするだけ。

SUS_TSK:
	; store
	push	af
	push	bc
	push	de
	push	hl
	; get task ID
	ld	a,(GR0)
	ld	c,a
	; get bit pattern
	ld	hl,BPAT
	ld	d,0
	ld	e,a
	add	hl,de
	ld	b,(hl)
	; update READY (reset target bit)
	ld	a,(READY)
	xor	b
	ld	(READY),a
	; update SUSPEND (set target bit)
	ld	a,(SUSPEND)
	or	b
	ld	(SUSPEND),a
	; update WAITQ (reset target bit)
	ld	a,(WAITQ)
	xor	b
	ld	(WAITQ),a
	; resume
	pop	hl
	pop	de
	pop	bc
	pop	af
	;
	ret

 SLP_TSK

  タスクIDが現在実行中のタスクなので、それを
  RUN_TSKから引き出して、SUS_TSKをコールする
  だけで実現可能。

  すでに定義してあるシステムコールを利用して
  手間を省きました。

SLP_TSK:
	; store
	push	af
	; get task ID
	ld	a,(RUN_TSK)
	ld	(GR0),a
	;
	call	SUS_TSK
	; resume
	pop	af
	;
	ret

 WAI_TSK

  タスクIDは、RUN_TSKでわかるので、GR1、GR2の2バイトに
  下位、上位の順で入れられている値を、指定のエリアに転送
  します。

  時間待ちをするので、WCOUNTというエントリーラベルを使い
  8ワード=16バイトの中の1ワードに値転送と考えました。

WAI_TSK:
	; store
	push	af
	push	bc
	push	de
	push	hl
	; get task ID
	ld	a,(RUN_TSK)
	ld	c,a
	; get bit pattern
	ld	hl,BPAT
	ld	d,0
	ld	e,a
	add	hl,de
	ld	b,(hl)
	; update READY (reset target bit)
	ld	a,(READY)
	xor	b
	ld	(READY),a
	; update SUSPEND (reset target bit)
	ld	a,(SUSPEND)
	xor	b
	ld	(SUSPEND),a
	; update WAITQ (set target bit)
	ld	a,(WAITQ)
	or	b
	ld	(WAITQ),a
	; store counter
	; get task ID
	ld	a,(RUN_TSK)
	; x2
	sla	a
	; set pointer
	ld	b,0
	ld	c,a
	ld	hl,WCOUNT
	add	hl,bc
	; store
	ld	a,(GR1)
	ld	(hl),a
	inc	hl
	ld	a,(hl)
	ld	(hl),a
	; resume
	pop	hl
	pop	de
	pop	bc
	pop	af
	;
	ret

  タスクIDの値を2倍すると、WCOUNTからのオフセット値を
  計算できることを使いました。

  10msごとに、WCOUNT中の該当ワードをデクリメントする処理は
  サブルーチンで、次のように定義。

THANDLER:
	; store
	push	af
	push	bc
	push	de
	push	hl
	push	ix
	; store task ID
	xor	a
	ld	(GR3),a
	; get information
	ld	a,(WAITQ)
	ld	c,a
	; set counter
	ld	b,1
THANDLER0:
	; get WAITQ information
	ld	a,c
	; judge
	bit	0,a
	jr	z,THANDLER1
	; set pointer
	ld	hl,WCOUNT
	; get task ID and calculate offset
	ld	a,(GR3)
	sla	a
	ld	e,a
	ld	d,0
	; calculate pointer
	add	hl,de
	; exchange value
	push	hl
	pop	ix
	; get counter value
	ld	l,(ix)
	ld	h,(ix+1)
	; decrement
	dec	hl
	; store counter value
	ld	(ix),l
	ld	(ix+1),h
	; judge
	ld	a,l
	cp	h
	jr	nz,THANDLER1
	cp	0
	jr	nz,THANDLER1
	; update READY
	ld	a,(READY)
	or	b
	ld	(READY),a
	; update SUSPEND
	ld	a,(SUSPEND)
	xor	b
	ld	(SUSPEND),a
	; update WAITQ
	ld	a,(WAITQ)
	xor	b
	ld	(WAITQ),a
THANDLER1:
	ld	a,(GR3)
	inc	a
	ld	(GR3),a
	srl	c
	sla	b
	jr	c,THANDLER2
	jr	THANDLER0
THANDLER2:
	; resume
	pop	ix
	pop	hl
	pop	de
	pop	bc
	pop	af
	;
	ret

  変数WAITQの中の該当ビットが1かを判断し、1だったときに
  変数WAITQの該当ビットを0に。変数READYの該当ビットを1に。
  念のため、変数SUSPENDの該当ビットも0にしておきます。

 USOは、μITRONのシステムコールを使っているので
 タスクの登録と開始で、CRE_TSK、STA_TSKが必要に
 なります。

 TCB(Task Control Block)を用意していないので、モニタを
 初期化するときに、変数READY、SUSPENDの値を指定する方式
 で、タスクの登録と開始を代用してみました。

	; set task state (READY)
	; CRE_TSK , STA_TSK
	; TSK7 TSK0
	ld	a,81h
	ld	(READY),a

	; set task state (SUSPEND)
	; CRE_TSK , STA_TSK
	; TSK6 TSK5 TSK4 TSK3 TSK2 TSK1
	cpl
	ld	(SUSPEND),a

 変数READY、SUSPEND、WAITQの初期化や論理演算に使う
 ビットパターンも必要なので、サブルーチンにまとめて
 単純にしてあります。

INIT_USO:
	; store
	push	af
	push	bc
	push	de
	push	hl
	; clear
	xor	a

	; clear READY
	ld	(READY),a

	; clear SUSPEND
	ld	(SUSPEND),a

	; clear WAITQ
	ld	(WAITQ),a

	; generate bit battern
	ld	hl,BPAT
	ld	a,1
	ld	b,8
INIT_USO1:
	ld	(hl),a
	sla	a
	inc	hl
	djnz	INIT_USO1

	; set task state (READY)
	; CRE_TSK , STA_TSK
	; TSK7 TSK0
	ld	a,81h
	ld	(READY),a

	; set task state (SUSPEND)
	; CRE_TSK , STA_TSK
	; TSK6 TSK5 TSK4 TSK3 TSK2 TSK1
	cpl
	ld	(SUSPEND),a

	; resume
	pop	hl
	pop	de
	pop	bc
	pop	af
	;
	ret


 タスクは、次のように記述します。
TSK3_PROC:
	;
	; ?
	;
	ret

 USOでは、タスク内で無限ループは禁止です。
 無限ループを使うと、他のタスクにCPUを渡せ
 なくなります。

 このリアルタイムモニタは、Z84C015を使った
 ボードに実装することにします。

 タイマー割込みは、Z80CTCを使いCTC3を10ms
 のインターバル生成に利用します。

 Z80ファミリを使うので、モード2割込みで
 10msを生成します。

 モード2割込みでは、レジスタIに割込み
 ハンドラの上位8ビットを、下位8ビット
 はZ80ファミリICから出力するので7F80に
 割込みハンドラのエントリアドレスを置く
 ことにします。

 ターゲットのZ80CPUボードは、32kバイトの
 ROMを載せられるので、割込みハンドラは
 32kバイトの最後の128バイトに格納します。

 割込みハンドラを次のように定義しました。

CTCxH:
	; store
	push	af
	push	hl
	; load counter
	ld	a,(XTIM)
	; increment
	inc	a
	; judge
	cp	10
	jr	z,CTCxH1
	; store counter
	ld	(XTIM),a
	jr	CTCxH2
CTCxH1:
	; clear
	xor	a
	; store counter
	ld	(XTIM),a
	; set WFLAG
	ld	a,(XFLAGS)
	set	WFLAG,a
	ld	(XFLAGS),a
CTCxH2:
	; resume
	pop	hl
	pop	af
	;
	ei
	reti

 割込み周期を2msとして、2msx5=10ms
 ごとに、TFLAGをセット。
 10msごとの判断に、カウンタICNTを
 利用しています。

 割込み、システムコールを利用した
 簡単なRTOS対応のコードを作成して
 動作テストしてみます。仕様は以下
 としました。

  • ひとつのスイッチ、2つのLEDを基板に接続
  • スイッチを押すたびに、2つのLEDは個別周期で点滅
  • LED_0は、周期2秒で点滅
  • LED_1は、周期3秒で点滅  タイマー割込みを使えば、リアルタイムモニタを  採用するまでもないファームウエアですが、動作  テストのため、この仕様を決めました。  4つのタスクを用意し、各タスクの担当を  次のように機能割当てしています。
    • タスク0 内部状態設定と3つのタスク管理
    • タスク1 LED_0を周期2秒で点滅
    • タスク2 LED_0を周期3秒で点滅
    • タスク3 スイッチのチャタリング除去
     ファームウエアのソースコードは、以下です。 ;**************************** ; USO test code ;**************************** STKT EQU 0a000h TFLAG EQU 0 UFLAG EQU 1 EFLAG EQU 2 SFLAG EQU 3 WFLAG EQU 4 TSK_ID0 EQU 0 TSK_ID1 EQU 1 TSK_ID2 EQU 2 TSK_ID3 EQU 3 TSK_ID4 EQU 4 TSK_ID5 EQU 5 TSK_ID6 EQU 6 TSK_ID7 EQU 7 PACON EQU 54h PBCON EQU 55h PCCON EQU 56h PDCON EQU 34h PECON EQU 44h PADAT EQU 50h PBDAT EQU 51h PCDAT EQU 53h PDDAT EQU 30h PEDAT EQU 40h PAINIT EQU 00h ; inputs PBINIT EQU 0ffh ; outputs PCINIT EQU 0ffh ; outputs PDINIT EQU 00h ; inputs PEINIT EQU 00h ; inputs CTC0 EQU 10h CTC1 EQU 11h CTC2 EQU 12h CTC3 EQU 13h ;*************************** ; startup routine ;*************************** org 0h ; set stack pointer ld sp,STKT xor a ld i,a im 2 jp START org 50h dw CTCxD dw CTCxD dw CTCxD dw CTCxH ;*************************** ; dispatch ;*************************** org 100h START: call INIT_USR call INIT_USO ; set pointer ld hl,READY ld ix,RUN_TSK ; set first task ID xor a ld (ix),a ; enable interrupt ei MAIN: ; 10ms handling ld a,(XFLAGS) ; check WFALG bit WFLAG,a jr z,MAIN1 ; update WCOUNTER call THANDLER ; perform task MAIN1: ; get READY ld c,(hl) ; TASK0 bit TSK_ID0,c call nz,TSK0_PROC inc (ix) ; TASK1 bit TSK_ID1,c call nz,TSK1_PROC inc (ix) ; TASK2 bit TSK_ID2,c call nz,TSK2_PROC inc (ix) ; TASK3 bit TSK_ID3,c call nz,TSK3_PROC inc (ix) ; TASK4 bit TSK_ID4,c call nz,TSK4_PROC inc (ix) ; TASK5 bit TSK_ID5,c call nz,TSK5_PROC inc (ix) ; TASK6 bit TSK_ID6,c call nz,TSK6_PROC inc (ix) ; TASK7 bit TSK_ID7,c call nz,TSK7_PROC ; return first state xor a ld (ix),a ; jr MAIN ;***************************** ; initialize user environment ;***************************** INIT_USR: ; store push af ; initialize CTC call INIT_CTC ; initialize parallel I/O ld a,PAINIT ld c,PACON out (c),a ; ld a,PBINIT ld c,PBCON out (c),a ; ld a,PCINIT ld c,PCCON out (c),a ; ld a,PDINIT ld c,PDCON out (c),a ; ld a,PEINIT ld c,PECON out (c),a ; clear flags xor a ld (XFLAGS),a ; clear counter ld (XTIM),a ; resume pop af ; ret ;***************************** ; initialize CTC ;***************************** INIT_CTC: ; store push af push bc ; put CTC interrupt vector ld a,50h ; xx50h out (CTC0),a ; channel 0 control word ld a,37h out (CTC0),a ; channel 0 time constant ld a,128 out (CTC0),a ; channel 1 control word ld a,37h out (CTC1),a ; channel 1 time constant xor a out (CTC1),a ; update pointer inc c ; channel 2 control word ld a,17h out (CTC2),a ; channel 2 time constant xor a out (CTC2),a ; update pointer inc c ; channel 3 control word ld a,0b7h out (CTC3),a ; channel 3 time constant ; generate 1ms interrupt ld a,15 out (CTC3),a ; resume pop bc pop bc ; ret ;***************************** ; CTCx handler dummy ;***************************** CTCxD: ei reti ;***************************** ; CTCx handler generate 10ms ;***************************** CTCxH: ; store push af push hl ; load counter ld a,(XTIM) ; increment inc a ; judge cp 10 jr z,CTCxH1 ; store counter ld (XTIM),a jr CTCxH2 CTCxH1: ; clear xor a ; store counter ld (XTIM),a ; set WFLAG ld a,(XFLAGS) set WFLAG,a ld (XFLAGS),a CTCxH2: ; resume pop hl pop af ; ei reti ;**************************** ; initialize USO parameters ;**************************** INIT_USO: ; store push af push bc push de push hl ; clear xor a ; clear READY ld (READY),a ; clear SUSPEND ld (SUSPEND),a ; clear WAITQ ld (WAITQ),a ; generate bit battern ld hl,BPAT ld a,1 ld b,8 INIT_USO1: ld (hl),a sla a inc hl djnz INIT_USO1 ; set task state (READY) ; CRE_TSK , STA_TSK ; TSK7 TSK0 ld a,81h ld (READY),a ; set task state (SUSPEND) ; CRE_TSK , STA_TSK ; TSK6 TSK5 TSK4 TSK3 TSK2 TSK1 cpl ld (SUSPEND),a ; resume pop hl pop de pop bc pop af ; ret ;**************************** ; USO timer handler ;**************************** THANDLER: ; store push af push bc push de push hl push ix ; store task ID xor a ld (GR3),a ; get information ld a,(WAITQ) ld c,a ; set counter ld b,1 THANDLER0: ; get WAITQ information ld a,c ; judge bit 0,a jr z,THANDLER1 ; set pointer ld hl,WCOUNT ; get task ID and calculate offset ld a,(GR3) sla a ld e,a ld d,0 ; calculate pointer add hl,de ; exchange value push hl pop ix ; get counter value ld l,(ix) ld h,(ix+1) ; decrement dec hl ; store counter value ld (ix),l ld (ix+1),h ; judge ld a,l cp h jr nz,THANDLER1 cp 0 jr nz,THANDLER1 ; update READY ld a,(READY) or b ld (READY),a ; update SUSPEND ld a,(SUSPEND) xor b ld (SUSPEND),a ; update WAITQ ld a,(WAITQ) xor b ld (WAITQ),a THANDLER1: ld a,(GR3) inc a ld (GR3),a srl c sla b jr c,THANDLER2 jr THANDLER0 THANDLER2: ; resume pop ix pop hl pop de pop bc pop af ; ret ;**************************** ; system call ;**************************** RSM_TSK: ; store push af push bc push de push hl ; get task ID ld a,(GR0) ld c,a ; get bit pattern ld hl,BPAT ld d,0 ld e,a add hl,de ld b,(hl) ; update READY (set target bit) ld a,(READY) or b ld (READY),a ; update SUSPEND (reset target bit) ld a,(SUSPEND) xor b ld (SUSPEND),a ; update WAITQ (reset target bit) ld a,(WAITQ) xor b ld (WAITQ),a ; resume pop hl pop de pop bc pop af ; ret SUS_TSK: ; store push af push bc push de push hl ; get task ID ld a,(GR0) ld c,a ; get bit pattern ld hl,BPAT ld d,0 ld e,a add hl,de ld b,(hl) ; update READY (reset target bit) ld a,(READY) xor b ld (READY),a ; update SUSPEND (set target bit) ld a,(SUSPEND) or b ld (SUSPEND),a ; update WAITQ (reset target bit) ld a,(WAITQ) xor b ld (WAITQ),a ; resume pop hl pop de pop bc pop af ; ret SLP_TSK: ; store push af ; get task ID ld a,(RUN_TSK) ld (GR0),a ; call SUS_TSK ; resume pop af ; ret WAI_TSK: ; store push af push bc push de push hl ; get task ID ld a,(RUN_TSK) ld c,a ; get bit pattern ld hl,BPAT ld d,0 ld e,a add hl,de ld b,(hl) ; update READY (reset target bit) ld a,(READY) xor b ld (READY),a ; update SUSPEND (reset target bit) ld a,(SUSPEND) xor b ld (SUSPEND),a ; update WAITQ (set target bit) ld a,(WAITQ) or b ld (WAITQ),a ; store counter ; get task ID ld a,(RUN_TSK) ; x2 sla a ; set pointer ld b,0 ld c,a ld hl,WCOUNT add hl,bc ; store ld a,(GR1) ld (hl),a inc hl ld a,(hl) ld (hl),a ; resume pop hl pop de pop bc pop af ; ret ;**************************** ; task code ;**************************** TSK0_PROC: ; ret TSK1_PROC: ; store push af push bc push de push hl ; get counter ld hl,XCNT1 ld e,(hl) inc hl ld d,(hl) ; get LSB ld a,e and 01h ; counter increment ld b,h ld c,l ld hl,0 add hl,bc inc hl ; store ld b,h ld c,l ld hl,XCNT1 ld (hl),c inc hl ld (hl),b ; get pointer ld hl,PORTB ; get data ld b,(hl) ld a,b and 01h ; judge cp 01h jr z,TSK1_PROC1 res 0,b jr TSK1_PROC2 TSK1_PROC1: set 0,b TSK1_PROC2: ld (hl),b ; impress ld c,PBDAT ld a,b out (c),a ; period 2000ms ld hl,GR2 ld de,200 ld (hl),d dec hl ld (hl),e call WAI_TSK ; resume pop hl pop de pop bc pop af ; ret TSK2_PROC: ; store push af push bc push de push hl ; get counter ld hl,XCNT2 ld e,(hl) inc hl ld d,(hl) ; get LSB ld a,e and 01h ; increment ld hl,0 add hl,de inc hl ; store ld e,l ld d,h ld hl,XCNT2 ld (hl),e inc hl ld (hl),d ; get pointer ld hl,PORTB ; get data ld b,(hl) ld a,b and 02h ; judge cp 02h jr z,TSK2_PROC1 res 1,b jr TSK2_PROC2 TSK2_PROC1: set 1,b TSK2_PROC2: ld (hl),b ; impress ld c,PBDAT ld a,b out (c),a ; period 3000ms ld hl,GR2 ld de,300 ld (hl),d dec hl ld (hl),e call WAI_TSK ; resume pop hl pop de pop bc pop af ; ret TSK3_PROC: ; ret TSK4_PROC: ; ret TSK5_PROC: ; ret TSK6_PROC: ; ret TSK7_PROC: ; ret ;**************************** ; data area ;**************************** org 8000h GR0 DS 1 GR1 DS 1 GR2 DS 1 GR3 DS 1 READY DS 1 SUSPEND DS 1 WAITQ DS 1 XFLAGS DS 1 RUN_TSK DS 1 XTIM DS 1 WCOUNT DW 8 BPAT DS 8 PORTB DS 1 XCNT1 DW 1 XCNT2 DW 1 END  アセンブリ言語の場合、Copy and Pasteで内容を更新すると  いろいろなバグが入込むことになるので、コンフィグレータ  を作成した方がよいでしょう。  Tcl/Tkは、UNIX、Windowsを問わずにGUIの  インタフェースを持つアプリケーションを  作成できます。Tcl/Tkでコンフィグレータ  を作成します。  コンフィグレータの動作イメージは、以下。  生成ファイル名、利用タスク数、エントリーアドレス  TCBテーブルの先頭アドレス等を確定してから、make  ボタンをクリックします。
  • 目次

    inserted by FC2 system