読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

NASM / Linux x86-64でjonesforthを移植してみる 2

昨日の続き。

https://github.com/phaendal/jf64/

1文字入力、1文字出力、トークンの読み取り、文字列の出力、などを実装して、以下のようにdefwordで命令列を定義して実行できるようになった。

    defword "test", 4, 0, test
    ; @を出力する
    dq      lit
    dq      60
    dq      lit
    dq      4
    dq      add
    dq      emit
    
    ;; 1文字読み込んで、次の文字にする
    ; dq      key
    ; dq      lit
    ; dq      1
    ; dq      add
    ; dq      emit
    
    ; 1トークン読み込んで、表示する
    dq      read_token
    dq      pr
    
    ; helloworld
    dq      helloworld
    
    ; 終了
    dq      bye

昨日の時点ではgasの.setディレクティブのようにプリプロセッサが使う変数を定義する方法がわからなくて前のワードへのリンクを手動で貼ってたけど、%defineで定義すれば再定義できることがわかったので書き換えた。(redefineできないと思い込んでた)

%macro setheader 4 ; name, namelen, flags, label
section .rodata
align 8
global header_%4
    header_%4:
    dq prev_link
%define prev_link header_%4
    dq %3
    db %2
    db %1
align 8
%endmacro

dq prev_linkで前のワードへのポインタを置き、そのワードのヘッダをprev_linkに設定しなおしている。

ここまでは良かったんだけど、その後read-tokenでの文字の比較が上手くいかなくて挫折しかけた。

cmp rax, ' 'みたいなraxレジスタと文字の比較に失敗してることはわかったんだけど、レジスタの内容を見る手段が無いので、cmpで比較できないのか、raxに予想外のものが入ってるのかわからない。gdbを使ってみたけどnasmでブレークポイントを設定する方法も失敗した。

結局レジスタの内容を適当に出力するサブルーチンを使って調べたところ、どうもraxに4文字入ってそうだ、とわかった。16bitで4文字で64bit。

推測だけど、keyワードのサブルーチンで、システムコールreadを使って文字列abcdを読み込んだとき、メモリには若い方から16bitずつabcdと書き込まれる。aから64bit幅でレジスタにmovすると、レジスタ上位からdcbaと読み込まれる。よって、64bit幅のraxじゃなくて、mov al, [addr]みたいに16bit幅で読み込んでやることで上手くいった。

なんとか上手く動いたけど、自信が全然無い。おそらくぼんやりとしかわかってないエンディアンの話とかなんだろうけど、どうやって実行時のレジスタ・メモリ・ビット幅を調べていいかわからないので、Assembly Programming Linuxなどを参考にして検証用のルーチンを組んでいくことにする。

WORDワード(read-token)までできているので、一気にForth作りあげてしまっても良さそうだけど。そっちのほうが楽しいかな。

あとemacsだとassembler-modeとかnasm-modeの使い勝手があんまり好きじゃないので、そこらへんもどうにかしたい。

広告を非表示にする