オンライン授業で課題が多くてバタバタしているPG_MANAです。
今回、Zen言語(https://zen-lang.org/ja-JP/)に新しいバージョンが登場し言語仕様が一部変わったようで、build.zenの書き換えが必要になりました。
それとは別にZen言語でVRAMに書き込もうとしたときに躓いたので備忘録して残しておきます。
環境
- Solus Linux(Linux Kernel 5.6.4-152.current) x86_64
- Zen(v0.8.20200706LTS)
ソースコードと解析
今回書いていたコードはi386な環境でfreestandingで起動して画面を白で塗りつぶすというものです。(hariboteOSのブートローダを使用)(一部コードを削除)
export fn HariMain() linksection(".text.hari_main") noreturn {
var p: usize = 0;
if (binfo.vmode == 16) {
const vram = @intToPtr(*volatile [*]mut u16,0xfd000000).*; // 0xfd000000はVRAMのアドレス
const max: usize = 1024 * 768;
while (p < max) {
vram[p] = 0xffff;
p += 1;
}
}
while (true) {
asm volatile ("hlt");
}
}
これがうまく動かないのでobjdumpを覗いたところ
...
48: b8 00 00 00 fd mov eax,0xfd000000
4d: 8b 00 mov eax,DWORD PTR [eax]
4f: 89 45 f8 mov DWORD PTR [ebp-0x8],eax
52: 81 7d fc 00 00 0c 00 cmp DWORD PTR [ebp-0x4],0xc0000
59: 73 1c jae 77 <HariMain+0x47>
5b: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
5e: 8b 4d f8 mov ecx,DWORD PTR [ebp-0x8]
61: 66 c7 04 41 ff ff mov WORD PTR [ecx+eax*2],0xffff
67: 8b 45 fc mov eax,DWORD PTR [ebp-0x4]
6a: 83 c0 01 add eax,0x1
...
となっています。
これをみるとVRAMにアクセスする際の手続きが*(*(0xfd000000
) + 2 * p)となっています。これではうまく動きません。
一瞬コンパイラのバグを疑ったのですが、よくよく考えると配列型の実態は配列の最初の要素のポインターに過ぎないので@intToPtr(*volatile [*]mut u16,0xfd000000).*
と書くとそうなるのは、まあそれはそうです。@intToPtr()
につられてついつい一番前に*をつけてしまいました。良くない良くない。
さて原因がわかったので単なる配列型にキャストすべく、const vram = @intToPtr(volatile [*]mut u16,0xfd000000);
としてもコンパイルエラーになるので、どうしたものか悩みながら試行錯誤していたところ、順序を入れ替えてconst vram = @intToPtr([*]volatile mut u16,0xfd000000);
としたらできました。
結論
export fn HariMain() linksection(".text.hari_main") noreturn {
var p: usize = 0;
if (binfo.vmode == 16) {
const vram = @intToPtr([*]volatile mut u16,0xfd000000); // 0xfd000000はVRAMのアドレス
const max: usize = 1024 * 768;
while (p < max) {
vram[p] = 0xffff;
p += 1;
}
}
while (true) {
asm volatile ("hlt");
}
}
@intToPtrなどビルドイン関数を利用する時はその意味をよく吟味して使いましょう…