プログラミング
Gigatronのプログラミング
Programming the Gigatron (iwriteiam.nl)
要約
この記事は、筆者がGigatron TTLカラーマイクロコンピュータの理解とプログラミングを試みた個人的な記録です。CPUのハーバードアーキテクチャや命令セットの詳細な解説に加え、`theloop.asm`ファイルで使用されている命令を分析し、その使用頻度とニーモニックをリストアップしています。
全文翻訳
Gigatronのプログラミング
この記事は、Gigatron TTLカラーマイクロコンピュータの理解とプログラミングを試みた私の個人的な試みについてです。GitHubのkervinck/gigatron-romにあるファイルを利用しました。免責事項: これは公式のGigatronページではなく、含まれるエラーについて私は責任を負いません。ROMの新版がリリースされた場合、このページは古くなる可能性があります。
CPU
GigatronのTTL ICで実装されているCPUはハーバードアーキテクチャを採用しており、ROMとRAMで共有バスを持たないことを意味します。実際、ROMは命令を格納するためだけに用いられ、ROMに直接アクセスする命令はありません。(それでも、ROMからデータを「読み取る」ことが可能な巧妙なトリックがあります。)CPUは8ビットプロセッサで、14ビットのプログラムカウンタと15ビットのRAM範囲を持ちます。3つの8ビットレジスタと、8ビットの入出力があります。
CPU命令
gtemu.cプログラムを書き直し、異なる命令が実際に行うことを擬似C言語で出力するようにしました。多くの改訂を経て、以下の表になりました。行と列の数字を足し合わせることで命令番号が得られます。類似した(または同じ)命令がグループ化されるように行の順序を変更しました。「function」hiは値の上位バイトを返します。RAMにアクセスするために使用される値は、RAMが32キロバイトしかないため、0x7fffに「クリップ」されることに注意してください。命令がプログラムカウンタを変更する場合を除き、プログラムカウンタはすべての命令の後にインクリメントされます。命令が2つの操作を実行する場合、区切り文字としてセミコロンが使用されます。
# 00 # 20 # 40 # 60 # 80 # a0 # c0 # e0
---+-----------------------+---------------------------+---------------------------+---------------------------+---------------------------+---------------------------+--------------------------+---------------------------------
00 # A = oper # A &= oper # A |= oper # A ^= oper # A += oper # A -= oper # RAM[oper] = oper # PC = (Y>>8)|oper
04 # A = oper # A &= oper # A |= oper # A ^= oper # A += oper # A -= oper # RAM[X] = oper # if (A < 0) PC = hi(PC)|oper
08 # A = oper # A &= oper # A |= oper # A ^= oper # A += oper # A -= oper # RAM[(Y>>8)|oper] = oper # if (A > 0) PC = hi(PC)|oper
0c # A = oper # A &= oper # A |= oper # A ^= oper # A += oper # A -= oper # RAM[(Y>>8)|X] = oper # if (A != 0) PC = hi(PC)|oper
10 # X = oper # X = A & oper # X = A | oper # X = A ^ oper # X = A + oper # X = A - oper # RAM[oper] = oper; X = A # if (A == 0) PC = hi(PC)|oper
14 # Y = oper # Y = A & oper # Y = A | oper # Y = A ^ oper # Y = A + oper # Y = A - oper # RAM[oper] = oper; Y = A # if (A <= 0) PC = hi(PC)|oper
18 # OUT = oper # OUT = A & oper # OUT = A | oper # OUT = A ^ oper # OUT = A + oper # OUT = A - oper # RAM[oper] = oper # if (A >= 0) PC = hi(PC)|oper
1c # OUT = oper; X++ # OUT = A & oper; X++ # OUT = A | oper; X++ # OUT = A ^ oper; X++ # OUT = A + oper; X++ # OUT = A - oper; X++ # RAM[(Y>>8)|X++] = oper # PC = hi(PC)|oper
01 # A = RAM[oper] # A &= RAM[oper] # A |= RAM[oper] # A ^= RAM[oper] # A += RAM[oper] # A -= RAM[oper] # RAM[oper] = undef # PC = (Y>>8)|RAM[oper]
05 # A = RAM[X] # A &= RAM[X] # A |= RAM[X] # A ^= RAM[X] # A += RAM[X] # A -= RAM[X] # RAM[X] = undef # if (A < 0) PC = hi(PC)|RAM[oper]
09 # A = RAM[(Y>>8)|oper] # A &= RAM[(Y>>8)|oper] # A |= RAM[(Y>>8)|oper] # A ^= RAM[(Y>>8)|oper] # A += RAM[(Y>>8)|oper] # A -= RAM[(Y>>8)|oper] # RAM[(Y>>8)|oper] = undef # if (A > 0) PC = hi(PC)|RAM[oper]
0d # A = RAM[(Y>>8)|X] # A &= RAM[(Y>>8)|X] # A |= RAM[(Y>>8)|X] # A ^= RAM[(Y>>8)|X] # A += RAM[(Y>>8)|X] # A -= RAM[(Y>>8)|X] # RAM[(Y>>8)|X] = undef # if (A != 0) PC = hi(PC)|RAM[oper]
11 # X = RAM[oper] # X = A & RAM[oper] # X = A | RAM[oper] # X = A ^ RAM[oper] # X = A + RAM[oper] # X = A - RAM[oper] # RAM[oper] = undef; X = A # if (A == 0) PC = hi(PC)|RAM[oper]
15 # Y = RAM[oper] # Y = A & RAM[oper] # Y = A | RAM[oper] # Y = A ^ RAM[oper] # Y = A + RAM[oper] # Y = A - RAM[oper] # RAM[oper] = undef; Y = A # if (A <= 0) PC = hi(PC)|RAM[oper]
19 # OUT = RAM[oper] # OUT = A & RAM[oper] # OUT = A | RAM[oper] # OUT = A ^ RAM[oper] # OUT = A + RAM[oper] # OUT = A - RAM[oper] # RAM[oper] = undef # if (A >= 0) PC = hi(PC)|RAM[oper]
1d # OUT = RAM[(Y>>8)|X++] # OUT = A & RAM[(Y>>8)|X++] # OUT = A | RAM[(Y>>8)|X++] # OUT = A ^ RAM[(Y>>8)|X++] # OUT = A + RAM[(Y>>8)|X++] # OUT = A - RAM[(Y>>8)|X++] # RAM[(Y>>8)|X++] = undef # PC = hi(PC)|RAM[oper]
02 # /*nop*/ # /*nop*/ # /*nop*/ # A = 0 # A *= 2 # A = 0 # RAM[oper] = A # PC = (Y>>8)|A
06 # /*nop*/ # /*nop*/ # /*nop*/ # A = 0 # A *= 2 # A = 0 # RAM[X] = A # if (A < 0) PC = hi(PC)|A
0a # /*nop*/ # /*nop*/ # /*nop*/ # A = 0 # A *= 2 # A = 0 # RAM[(Y>>8)|oper] = A # if (A > 0) PC = hi(PC)|A
0e # /*nop*/ # /*nop*/ # /*nop*/ # A = 0 # A *= 2 # A = 0 # RAM[(Y>>8)|X] = A # if (A != 0) PC = hi(PC)|A
12 # X = A # X = A # X = A # X = 0 # X = 2*A # X = 0 # RAM[oper] = A; X = A # if (A == 0) PC = hi(PC)|A
16 # Y = A # Y = A # Y = A # Y = 0 # Y = 2*A # Y = 0 # RAM[oper] = A; Y = A # if (A <= 0) PC = hi(PC)|A
1a # OUT = A # OUT = A # OUT = A # OUT = 0 # OUT = 2*A # OUT = 0 # RAM[oper] = A # if (A >= 0) PC = hi(PC)|A
1e # OUT = A; X++ # OUT = A; X++ # OUT = A; X++ # OUT = 0; X++ # OUT = 2*A; X++ # OUT = 0; X++ # RAM[(Y>>8)|X++] = A # PC = hi(PC)|A
03 # A = IN # A &= IN # A |= IN # A ^= IN # A += IN # A -= IN # RAM[oper] = IN # PC = (Y>>8)|IN
07 # A = IN # A &= IN # A |= IN # A ^= IN # A += IN # A -= IN # RAM[X] = IN # if (A < 0) PC = hi(PC)|IN
0b # A = IN # A &= IN # A |= IN # A ^= IN # A += IN # A -= IN # RAM[(Y>>8)|oper] = IN # if (A > 0) PC = hi(PC)|IN
0f # A = IN # A &= IN # A |= IN # A ^= IN # A += IN # A -= IN # RAM[(Y>>8)|X] = IN # if (A != 0) PC = hi(PC)|IN
13 # X = IN # X = A & IN # X = A | IN # X = A ^ IN # X = A + IN # X = A - IN # RAM[oper] = IN; X = A # if (A == 0) PC = hi(PC)|IN
17 # Y = IN # Y = A & IN # Y = A | IN # Y = A ^ IN # Y = A + IN # Y = A - IN # RAM[oper] = IN; Y = A # if (A <= 0) PC = hi(PC)|IN
1b # OUT = IN # OUT = A & IN # OUT = A | IN # OUT = A ^ IN # OUT = A + IN # OUT = A - IN # RAM[oper] = IN # if (A >= 0) PC = hi(PC)|IN
1f # OUT = IN; X++ # OUT = A & IN; X++ # OUT = A | IN; X++ # OUT = A ^ IN; X++ # OUT = A + IN; X++ # OUT = A - IN; X++ # RAM[(Y>>8)|X++] = IN # PC = hi(PC)|IN
次に、どの命令が実際に使用されているかを調べるプログラムを作成し、theloop.asmファイルを解析しました。その結果、以下の出力が得られました。最初の列は16進数の命令番号、2番目の列はその命令がファイル内で言及された回数、3番目の列は擬似C言語コード、最後の列はファイル内で使用されているニーモニックです。$00 はオペランドを表します。
inst # pseudo-C mnemonic
-------------------------------------------------------------
02 586: /*nop*/ // nop
00 54305: A = oper // ld $00
01 505: A = RAM[oper] // ld [$00]
03 1: A = IN // ld in
05 26: A = RAM[X] // ld [x]
09 2: A = RAM[(Y<<8)|oper] // ld [y,$00]
0d 32: A = RAM[(Y<<8)|X] // ld [y,x]
10 1: X = oper // ld $00,x
11 27: X = RAM[oper] // ld [$00],x
12 11: X = A // ld ac,x
14 326: Y = oper // ld $00,y
15 19: Y = RAM[oper] // ld [$00],y
16 2: Y = A // ld ac,y
18 14: OUT = oper // ld $00,out
19 6: OUT = RAM[oper] // ld [$00],out
20 33: A &= oper // anda $00
21 8: A &= RAM[oper] // anda [$00]
25 2: A &= RAM[X] // anda [x]
29 2: A &= RAM[(Y<<8)|oper] // anda [y,$00]
30 9: X = A & oper // anda $00,x
40 14: A |= oper // ora $00
41 17: A |= RAM[oper] // ora [$00]
45 3: A |= RAM[X] // ora [x]
50 6: X = A | oper // ora $00,x
5d 3: OUT = A | RAM[(Y<<8)|X++] // ora [y,x++],out
60 16: A ^= oper // xora $bf
61 11: A ^= RAM[oper] // xora [$00]
69 5: A ^= RAM[(Y<<8)|oper] // xora [y,$00]
80 402: A += oper // adda $00
81 28: A += RAM[oper] // adda [$00]
82 36: A *= 2 // adda ac
85 2: A += RAM[X] // adda [x]
89 6: A += RAM[(Y<<8)|oper] // adda [y,$00]
8d 4: A += RAM[(Y<<8)|X] // adda [y,x]
90 11: X = A + oper // adda $00,x
91 1: X = A + RAM[oper] // adda [$00],x
92 2: X = 2*A // adda ac,x
95 1