Zen言語で配列ポインタに詰まった話

オンライン授業で課題が多くてバタバタしている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などビルドイン関数を利用する時はその意味をよく吟味して使いましょう…

投稿者: PG_MANA

支離滅裂な自称プログラマー。 C,C++,Rust,JavaScript,PHP,HTML,CSS,OS自作,openSUSE,Arch,旅行 なんか色々してる人 #seccamp 17 19 20 23 #OtakuAssembly