a23_useSelfMade #8
2023.04.14 Fri #1 [casm] [easy-C]
- HL-9のcasm出力が少しマシになりました。
DosPrompt>hl9
HL9>casm 1
HL9>pr 0:<10
LdImm32(R01, 0);
CmpJmpGrtEquImm32(R01, 10, 2);
Label(3);
ApiPrIntReg32(R01);
Label(4);
AddShtImm32(R01, 1);
CmpJmpLesThnImm32(R01, 10, 3);
Label(2);
End();
HL9>
- これをt0004.cにコピペして実行すると・・・
DosPrompt>gcc -m32 -O3 -DAArch_X86 -DAGraph_Win -Iacl1/ -include casm32.c -o t0004.exe t0004.c -lgdi32 -lwinmm
DosPrompt>t0004
0123456789
DosPrompt>
- こんなふうにばっちり実行できました。
2023.04.17 Mon #1 [casm]
- casmはOSECPU-VMと同じく、整数レジスタはデータレジスタとポインタレジスタに分かれています。これは68000と同じ設計方針で、x86やARMやRISC-Vの汎用レジスタ方式とは違う設計です。汎用レジスタ方式では、データもポインタも同じレジスタを使います。今調べたら、PowerPCやMIPSも汎用レジスタ方式のようです。
- ということで、レジスタをデータとポインタに分けるのはもはや主流ではなさそうです(8bitのころは主流だった気がするのですが・・・)。それでも私は、分けたほうがいいと思っています。
- データに必要なbit幅と、アドレスを表すのに必要なbit幅は必ず一致するとは限りません。汎用レジスタ方式はこれらを強引に一致させています。8bit-CPUでは、アドレスレジスタが16bitでデータレジスタは8bitになっていて、この不一致に対応していました。
- OSECPU-VMが汎用レジスタ方式を採用しなかったのは、まず第一にこれは仮想的な命令セットで、実際は何らかのCPUの機械語に翻訳されて動くことを想定していたので、どのCPUでもそこそこうまく適合できる必要があったためです。データ&アドレス方式の実CPUが汎用レジスタの「ふり」をするのにはかなりのオーバーヘッドを伴いまず。それぞれのレジスタの用途を判別して実レジスタにマッピングしなければいけないからです。しかし逆の、汎用レジスタ方式の実CPUがデータ&アドレス方式を真似るのは結構簡単です。EAXは常にデータレジスタとして使う、みたいに決め打ちしてしまえばいいだけだからです。
- また私がもしFPGAなどでCPUを作る機会があったら、たぶんデータ&アドレス方式のCPUを作ると思います。理由は、例えばアドレスレジスタが8本でデータレジスタも8本の時、これは合わせて16本になるわけですが、しかしアドレッシングでベースレジスタを指定するときはアドレスレジスタ番号しか必要ないので、3bitで済みます。追加のディスプレースメントを指定するためのレジスタはデータレジスタしかありえないのでやはり3bitで済みます。こうして、レジスタは16本あるのに、命令の中のレジスタ指定のビット数は節約できるのです。
- 一方で、問題があります。C言語で書かれたプログラムのうちのいくつかは、ポインタとintが同じbit幅であることを期待しています。今ではその書き方は問題になったので、intptr_tという型を使うようになっていますが、それでも「ポインタと何らかの整数型は相互に変換可能である」という期待はなくなっていません。しかもアドレスはバイト単位で表現されていると期待されています。たとえば4バイトのintがあったら、そのアドレスは当然4の倍数だと思われているのです。
- しかし私の考えでは、ポインタはその指しているメモリに読み書きができればいいのであって、その内部表現は本来はCPUの裁量でどうとでもなるはずです。私は触ったことはまだないのですが、charが32bitの処理系も存在していて、そういうマシンではintのポインタに++しても、1しか増えないかもしれません。
- こういうことに配慮すると、「ポインタの下位2bitは0に固定されるはずだから、ここにフラグを入れて利用しよう」みたいなアイデアはCPUに強く依存していることがわかります。ポインタの値をintにキャストしてANDして・・・みたいなのは移植性を下げてしまうのです(移植性を気にしなくていいのなら、メモリを節約するいい方法だとも思いますが)。
- ということで、私はポインタレジスタの中身はすべてのポインタとして扱うことにして、その中の数ビットをフラグにするのはやめるべきだと思います、移植性が必要なら。そしてこういうことを一切しないのなら、データレジスタとアドレスレジスタが分かれているのは、それほど不便ではないのです。
2023.04.18 Tue #1 [casm] [easy-C]
- これができるようになりました。
HL9>casm 1
HL9>AWin *w = aOpenWin(256, 256, "gradation"); c=0; [y=0:<256] { [x=0:<256] { aSetPix(w, x, y, c); c += 256; }}
LdPtrImm(P02, "gradation");
LdImm32(R00, 256)
LdImm32(R01, 256)
ApiOpenWinReg32(P03, R01, R00, P02);
CpyPtr(P04, P03);
LdImm32(R02, 0);
LdImm32(R03, 0);
CmpJmpGrtEquImm32(R03, 256, 2);
Label(3);
LdImm32(R04, 0);
CmpJmpGrtEquImm32(R04, 256, 4);
Label(5);
ApiSetPix32(P04, R04, R03, R02);
AddLngImm32(R02, R02, 256);
Label(6);
AddShtImm32(R04, 1);
CmpJmpLesThnImm32(R04, 256, 5);
Label(4);
Label(7);
AddShtImm32(R03, 1);
CmpJmpLesThnImm32(R03, 256, 3);
Label(2);
End();
2023.04.18 Tue #2 [casm]
- このcasmとOSECPU-VMの技術を組み合わせたら、また超小さいバイナリが作れるのかな?なんて思いついてしまいました。
- ちょっとOSECPU-VM rev2 のフロントエンドコードのメモを引っ張り出します。
| 0 | nop | |
| 1 | LB | 連番用 |
| 4-1 | LB i | 連番以外も可 |
| 2 | LIMM/CP src,reg | {0,1,2,3,4,rep0,-1} |
| 3 | JMP lb | |
| 4-3 | PLIMM lb,preg | |
| 4-4 | CND reg | 条件実行プリフィクス |
| 5 | API | |
| 4-5 | API | |
| 6 | loop v1,reg | |
| 4-6 | loop v0,v1,reg | |
| 7 | endloop | |
| 8 | LMEM ? | |
| 9 | SMEM ? | |
| 10 | OR2 | |
| 11 | XOR2 | |
| 12 | AND2 | |
| 13 | SBX2 | |
| 14 | ADD2 | |
| 15 | SUB2 | |
| 16 | MUL2 | |
| 17 | SBR2 | |
| 18 | SHL2 | |
| 19 | SAR2 | |
| 1a | DIV2 | |
| 1b | MOD2 | |
| 1e | PCP | |
| 20 | CMP | |
| 21 | CMP | |
| 22 | CMP | |
| 23 | CMP | |
| 24 | CMP | |
| 25 | CMP | |
| 26 | DIV3 | |
| 27 | MOD3 | |
| 28 | OR3 | |
| 29 | XOR3 | |
| 2a | AND3 | |
| 2b | SBX3 | |
| 2c | SHL2 | |
| 2d | SAR2 | |
| 2e | | |
{ R1, 16, 1, 2, 8, 4, R0 }, // OR
{ -1, R1, 1, 2, 3, 4, R0 }, // XOR
{ R1, 15, 1, 7, 3, 5, R0 }, // AND
{ 64, 16, 1, 2, 8, 4, 32 }, // SBX
{ -1, R1, 1, 2, 3, 4, R0 }, // ADD
{ R1, R0, 1, 2, 3, 4, 5 }, // SUB
{ 10, R0, R1, 7, 3, 6, 5 }, // MUL
{ -1, 0, 1, 2, 3, 4, R0 }, // SBR
{ R1, R0, 1, 2, 3, 4, 5 }, // SHL
{ R1, R0, 1, 2, 3, 4, 5 }, // SAR
{ 10, R0, R1, 7, 3, 6, 5 }, // DIV
{ 10, R0, R1, 7, 3, 6, 5 }, // MOD
こめんと欄