ネイティブアメリカンの砦のバイトマシン(だけでなく)(パート2)

画像



バイトコードを使用して実験を続けましょう。 これは、アセンブラのバイトマシンに関する記事の続きです。 最初の部分はこちらです



一般に、第2部ではフォートインタプリタを作成し、第3部ではこのバイトマシン用のフォートコンパイラを作成することを計画しました。 しかし、記事のために得られたボリュームは非常に大きかった。 インタプリタを作成するには、カーネル(バイトコマンドのセット)を展開し、実装する必要があります:変数、文字列の解析、文字列の入力、辞書、辞書の検索...まあ、少なくとも数値の出力は機能するはずです。 その結果、通訳に関する記事を2つに分割することにしました。 したがって、この記事では、カーネルを拡張し、変数を決定し、数値の出力を描画します。 以下は計画の例です。3番目の部分はインタープリター、4番目の部分はコンパイラーです。 そして、もちろん、パフォーマンステスト。 それらは4番目または5番目の記事に記載されます。 これらの記事は新年以降になります。



そして、ひどいアセンブラーとバイトコードをまだ恐れていない人-カットへようこそ! :)



まず、エラーを修正します。 GASの慣例に従って、ファイル拡張子.sを設定しましょう( mistergrimに感謝します )。 次に、int 0x80をsyscallに置き換え、64ビットレジスタを使用します( qw1に感謝)。 最初は、コールの説明を注意深く読んでいないので、レジスタのみを修正しました...セグメンテーション違反が発生しました。 コール番号を含む、syscallのすべてが変更されたことがわかります。 syscallのsys_writeは番号1、sys_exitは60です。その結果、bad、type、およびbyeコマンドは次の形式を取りました。



b_bad = 0x00 bcmd_bad: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 - stdout mov rsi, offset msg_bad_byte #     mov rdx, msg_bad_byte_len #   syscall #   mov rax, 60 #   № 1 - sys_exit mov rbx, 1 #    1 syscall #   b_bye = 0x01 bcmd_bye: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 - stdout mov rsi, offset msg_bye #     mov rdx, msg_bye_len #   syscall #   mov rax, 60 #   № 60 - sys_exit mov rdi, 0 #    0 syscall #   b_type = 0x80 bcmd_type: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 - stdout pop rdx pop rsi push r8 syscall #   pop r8 jmp _next
      
      





そしてもう一つ。 最後の記事へのコメントで、 berezfpaukは 、バイトアドレスでプロセッサアドレスが使用されている場合、バイトコードはプラットフォームに依存することを書いています。 そして、その例では、「Hello、world!」の文字列のアドレスが(lit64コマンドを使用して)値によってバイトコードに設定されました。 もちろん、これは必要ありません。 しかし、それがバイトマシンをチェックする最も簡単な方法でした。 これはもうしませんが、他の方法で変数のアドレスを取得します。特に、varコマンドを使用します(これについては後で説明します)。



ウォームアップ



そして今、ウォームアップとして、すべての基本的な整数算術演算(+、-、*、/、mod、/ mod、abs)を行います。 それらが必要になります。



コードは非常に単純なので、コメントなしでスポイラーに持ち込みます。



算術
 b_add = 0x21 bcmd_add: pop rax add [rsp], rax jmp _next b_sub = 0x22 bcmd_sub: pop rax sub [rsp], rax jmp _next b_mul = 0x23 bcmd_mul: pop rax pop rbx imul rbx push rax jmp _next b_div = 0x24 bcmd_div: pop rbx pop rax cqo idiv rbx push rax jmp _next b_mod = 0x25 bcmd_mod: pop rbx pop rax cqo idiv rbx push rdx jmp _next b_divmod = 0x26 bcmd_divmod: pop rbx pop rax cqo idiv rbx push rdx push rax jmp _next b_abs = 0x27 bcmd_abs: mov rax, [rsp] or rax, rax jge _next neg rax mov [rsp], rax jmp _next
      
      





従来、フォートでは、通常の算術演算とスタック演算に倍精度演算が追加されています。 このような操作の言葉は、通常、文字「2」で始まります:2DUP、2SWAPなど。 ただし、標準の算術演算はすでに64ビットであり、今日は絶対に128にはなりません:)



次に、基本的なスタック操作(ドロップ、スワップ、ルート、-ルート、オーバー、ピック、ロール)を追加します。



スタック操作
 b_drop = 0x31 bcmd_drop: add rsp, 8 jmp _next b_swap = 0x32 bcmd_swap: pop rax pop rbx push rax push rbx jmp _next b_rot = 0x33 bcmd_rot: pop rax pop rbx pop rcx push rbx push rax push rcx jmp _next b_mrot = 0x34 bcmd_mrot: pop rcx pop rbx pop rax push rcx push rax push rbx jmp _next b_over = 0x35 bcmd_over: push [rsp + 8] jmp _next b_pick = 0x36 bcmd_pick: pop rcx push [rsp + 8*rcx] jmp _next b_roll = 0x37 bcmd_roll: pop rcx mov rbx, [rsp + 8*rcx] roll1: mov rax, [rsp + 8*rcx - 8] mov [rsp + 8*rcx], rax dec rcx jnz roll1 push rbx jmp _next
      
      





また、メモリへの読み書きコマンドも作成します(Fortの単語@および!)。 また、異なるビット深度に対応するもの。



メモリの読み取りと書き込み
 b_get = 0x40 bcmd_get: pop rcx push [rcx] jmp _next b_set = 0x41 bcmd_set: pop rcx pop rax mov [rcx], rax jmp _next b_get8 = 0x42 bcmd_get8: pop rcx movsx rax, byte ptr [rcx] push rax jmp _next b_set8 = 0x43 bcmd_set8: pop rcx pop rax mov [rcx], al jmp _next b_get16 = 0x44 bcmd_get16: pop rcx movsx rax, word ptr [rcx] push rax jmp _next b_set16 = 0x45 bcmd_set16: pop rcx pop rax mov [rcx], ax jmp _next b_get32 = 0x46 bcmd_get32: pop rcx movsx rax, dword ptr [rcx] push rax jmp _next b_set32 = 0x47 bcmd_set32: pop rcx pop rax mov [rcx], eax jmp _next
      
      





まだ比較チームが必要な場合がありますが、それらも行います。



比較コマンド
 # 0= b_zeq = 0x50 bcmd_zeq: pop rax or rax, rax jnz rfalse rtrue: push -1 jmp _next rfalse: push 0 jmp _next # 0< b_zlt = 0x51 bcmd_zlt: pop rax or rax, rax jl rtrue push 0 jmp _next # 0> b_zgt = 0x52 bcmd_zgt: pop rax or rax, rax jg rtrue push 0 jmp _next # = b_eq = 0x53 bcmd_eq: pop rbx pop rax cmp rax, rbx jz rtrue push 0 jmp _next # < b_lt = 0x54 bcmd_lt: pop rbx pop rax cmp rax, rbx jl rtrue push 0 jmp _next # > b_gt = 0x55 bcmd_gt: pop rbx pop rax cmp rax, rbx jg rtrue push 0 jmp _next # <= b_lteq = 0x56 bcmd_lteq: pop rbx pop rax cmp rax, rbx jle rtrue push 0 jmp _next # >= b_gteq = 0x57 bcmd_gteq: pop rbx pop rax cmp rax, rbx jge rtrue push 0 jmp _next
      
      





操作をテストしません。 主なことは、アセンブラがコンパイル時にエラーを出さないことです。 デバッグは、それらの使用中です。



すぐに単語の深さ(スタックの深さ)を作ります。 これを行うには、起動時に、データスタックとリターンスタックの初期値を保存します。 これらの値は、システムを再起動するときに引き続き役立つ場合があります。



 init_stack: .quad 0 init_rstack: .quad 0 _start: mov rbp, rsp sub rbp, stack_size lea r8, start mov init_stack, rsp mov init_rstack, rbp jmp _next b_depth = 0x38 bcmd_depth: mov rax, init_stack sub rax, rsp shr rax, 3 push rax jmp _next
      
      





数値出力



さて、ウォームアップは終わりました。少し汗をかかなければなりません。 システムに数字を出力するように教えます。 単語「。」は、砦に数字を表示するために使用されます。 (期間)。 <#、hold、#、#s、#>、baseという単語を使用して、標準のFort実装で行われている方法でそれをやってみましょう。 これらすべての言葉を実現する必要があります。 数値を形成するには、バッファーと形成されたシンボルへのポインターが使用されます。これらは、単語holdbufとholdpointになります。



したがって、次の単語が必要です。





バイトコードですべての単語を実行しますが、最初に変数を処理します。



変数



そして、ここには小さなフォーシャンの魔法があります。 実際、砦では変数は単語です。 このワードが実行されると、変数の値を格納しているメモリセルのアドレスがスタック上にあります。 このアドレスを読み書きできます。 たとえば、値12345を変数Aに書き込むには、「12345 A!」というコマンドを実行する必要があります。 この例では、12345がスタックにプッシュされ、変数Aがそのアドレスとワード「!」をプッシュします。 スタックから2つの値を削除し、変数Aのアドレスに12345を書き込みます。(直接コードを使用した)フォートの典型的な実装では、変数はアドレス_nextを持つCALLマイクロプロセッサコマンドであり、その後変数の値を格納する場所が予約されます。 そのようなワードを実行すると、マイクロプロセッサは制御を_nextに転送し、リターンアドレスを(RSP経由で)スタックにプッシュします。 しかし、砦では、マイクロプロセッサスタックは算術演算であり、どこにも戻りません。 この結果、実行が継続され、スタック上に変数のアドレスがあります。 そして、これらすべてを1つのプロセッサチームで! アセンブラーでは、次のようになります。



  call _next #   _next,      ,   12345 .quad 12345
      
      





しかし、バイトコードがあり、このメカニズムを使用することはできません! バイトコードでこのようなメカニズムを作成する方法はすぐにはわかりませんでした。 しかし、論理的に考えると、非常に類似したものを実装することを妨げるものは何もありません。 これはプロセッサコマンドではなく、バイトコード、より正確にはバイトコードの「サブルーチン」であることに注意してください。 問題のステートメントは次のとおりです。





exit byteコマンドがあります。 単一の終了コマンドを含むバイトコードに単語を作りましょう。 その後、このコマンドはそこから戻ります。 スタック上の次のバイトのアドレス(レジスタR8)をさらにプッシュする同じコマンドを作成することは残ります。 移行を保存するために終了する追加のエントリポイントとしてこれを行います。



 b_var0 = 0x28 bcmd_var0: push r8 b_exit = 0x17 bcmd_exit: mov r8, [rbp] add rbp, 8 _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8]
      
      





これで、ベース変数は次のようになります。

 base: .byte b_var0 .quad 10
      
      





ところで、なぜvarだけでなくvar0なのか? 問題の事実は、データを含むより高度な単語を識別する他のコマンドがあることです。 次の記事で詳しく説明します。



これで、数字を描画する準備が整いました。 さあ始めましょう!



単語ベース、holdbuf、holdpoint



変数の配置方法はすでに決定されています。 したがって、単語base、holdbuf、holdpointは次のように取得されます。



 base: .byte b_var0 .quad 10 holdbuf_len = 70 holdbuf: .byte b_var0 .space holdbuf_len holdpoint: .byte b_var0 .quad 0
      
      





holdbufバッファのサイズは70です。数値の最大ビット数は64です(これはバイナリシステムを選択した場合です)。 たとえば、数字の記号とその後のスペースなど、いくつかの文字の予約も行われています。 バッファオーバーフローをチェックしますが、今のところはバッファに余分な文字を入れません。 その後、別の診断を行うことが可能になります。



つかむ



これで単語を保持できます。 砦では、コードは次のようになります。



 : hold holdpoint @ 1- dup holdbuf > if drop drop else dup holdpoint ! c! then ;
      
      





初めて砦を見た人のために、コードを詳細に分析します。 次の言葉のために、私はこれをしません。



最初に、新しい単語の定義と新しい単語の名前を表す単語「:hold」があります。 その後、「;」という単語で終わるコードが続きます。 単語コードを解析しましょう。 コマンドを実行した後、コマンドとスタックの状態を提供します。 単語が呼び出される前に、スタック上に文字コードがあり、これはバッファに配置されます(<character>で示されます)。 さらに、次のようになります。



 holdpoint <> <  holdpoint> @ <> <  holdpoint> 1- <> <  holdpoint  1> dup <> <  holdpoint  1> <  holdpoint  1> holdbuf <> <  holdpoint  1> <  holdpoint  1> <  holdbuf> > <> <  holdpoint  1> <,    holdpoint  1    holdbuf>
      
      





その後にifコマンドがあり、elseとthenの間の一連のコマンドへの条件付きジャンプにコンパイルされます。 条件分岐は、スタックから比較結果を削除し、スタックに嘘があった場合に遷移を実行します。 遷移がなかった場合、ifとelseの間の分岐が実行されます。この分岐には、文字とアドレスを削除する2つのドロップコマンドがあります。 それ以外の場合、実行は継続されます。 「!」という言葉 新しい値をホールドポイントに保存します(アドレスと値はスタックから削除されます)。 「c!」という単語は、文字をバッファに書き込みます。これはset8バイトのコマンドです(文字のアドレスと値はスタックから削除されます)。



 dup <> <  holdpoint  1> <  holdpoint  1> holdpoint <> <  holdpoint  1> <  holdpoint  1> <  holdpoint> ! <> <  holdpoint  1> c! ,  ,   ! :)
      
      





この短いコマンドシーケンスが実行するアクションの数を次に示します。 はい、要塞は簡潔です。 そして今、私たちは頭のマニュアル「コンパイラ」をオンにします:)そしてそれをすべてバイトコードにコンパイルします:

 hold: .byte b_call8 .byte holdpoint - . - 1 # holdpoint .byte b_get # @ .byte b_wm # 1- .byte b_dup # dup .byte b_call8 .byte holdbuf - . - 1 # holdbuf .byte b_gt # > .byte b_qbranch8 # if .byte 0f - . .byte b_drop # drop .byte b_drop # drop .byte b_branch8 #     ( then) .byte 1f - . 0: .byte b_dup # dup .byte b_call8 .byte holdpoint - . - 1 # holdpoint .byte b_set # ! .byte b_set8 # c! 1: .byte b_exit # ;
      
      





ここでは、ローカルラベル(0および1)を使用しました。 これらのラベルには特別な名前でアクセスできます。 たとえば、ラベル0は、0fまたは0bという名前でアクセスできます。 これは、最も近いラベル0(前方または後方)へのリンクを意味します。 異なる名前を思い付かないように、ローカルで使用されるラベルには非常に便利です。



ワード#



#という言葉を作りましょう。 砦では、そのコードは次のようになります。



 : # base /mod swap dup 10 < if c″ 0 + else 10 - c″ A + then hold ;
      
      





ここでの条件は、チェックに使用されます。受信した数は10少ないですか? 小さい場合は0〜9の数字が使用され、そうでない場合は「A」で始まる文字が使用されます。 これにより、16進数システムでの作業が可能になります。 シーケンスc″ 0は、文字コード0をスタックにプッシュします。「コンパイラー」をオンにします。



 conv: .byte b_call16 .word base - . - 2 # base .byte b_get # @ .byte b_divmod # /mod .byte b_swap # swap .byte b_dup # dup .byte b_lit8 .byte 10 # 10 .byte b_lt # < .byte b_qnbranch8 # if .byte 0f - . .byte b_lit8 .byte '0' # c″ 0 .byte b_add # + .byte b_branch8 # else .byte 1f - . 0: .byte b_lit8 .byte 'A' # c″ A .byte b_add # + 1: .byte b_call16 .word hold - . - 2 # hold .byte b_exit # ;
      
      





単語<#



<#という単語は非常に簡単です。



 : <# holdbuf 70 + holdpoint ! ;
      
      





バイトコード:



 conv_start: .byte b_call16 .word holdbuf - . - 2 .byte b_lit8 .byte holdbuf_len .byte b_add .byte b_call16 .word holdpoint - . - 2 .byte b_set .byte b_exit
      
      





ワード#>



変換を完了する#>という単語は次のようになります。



 : #> holdpoint @ holdbuf 70 + over - ;
      
      





バイトコード:



 conv_end: .byte b_call16 .word holdpoint - . - 2 .byte b_get .byte b_call16 .word holdbuf - . - 2 .byte b_lit8 .byte holdbuf_len .byte b_add .byte b_over .byte b_sub .byte b_exit
      
      





単語#s



最後に、#s:



 : #s do # dup 0= until ;
      
      





バイトコード:



 conv_s: .byte b_call8 .byte conv - . - 1 .byte b_dup .byte b_qbranch8 .byte conv_s - . .byte b_exit
      
      





注意を払う人はだれでも、バイトコードとフォートコードの間にわずかな矛盾があることに気付くでしょう:)



すべて準備完了



これで、数字を表示する「。」という単語の作成を止めることはできません。



 : . <# #s drop #> type ;
      
      





バイトコード:



 dot: .byte b_call8 .byte conv_start - . - 1 .byte b_call8 .byte conv_s - . - 1 .byte b_drop .byte b_call8 .byte conv_end - . - 1 .byte b_type .byte b_exit
      
      





ポイントをチェックするテストバイトコードを作成しましょう。



 start: .byte b_lit16 .word 1234 .byte b_call16 .word dot - . - 2 .byte b_bye
      
      





もちろん、一度にすべては機能しませんでした。 しかし、デバッグ後、次の結果が得られました。



 $ as forth.asm -o forth.o -g -ahlsm>list.txt $ ld forth.o -o forth $ ./forth 1234bye!
      
      





枠はすぐに表示されます。 数字の後に、砦にスペースが表示されます。 conv_start(<#)が32 holdコマンドを呼び出した後に追加します。



また、サインの結論を引き出します。 最初にdup absを追加し、最後に左のコピーの符号を確認し、数値が負の場合はマイナスを入力します(0 <if c ''-ホールド)。 その結果、単語「。」 この形式を取ります:



 : . dup abs <# 32 hold #s drop #> 0< if c″ - hold then type ;
      
      





バイトコード:



 dot: .byte b_dup .byte b_abs .byte b_call8 .byte conv_start - . - 1 .byte b_lit8 .byte ' ' .byte b_call16 .word hold - . - 2 .byte b_call8 .byte conv_s - . - 1 .byte b_drop .byte b_zlt .byte b_qnbranch8 .byte 1f - . .byte b_lit8 .byte '-' .byte b_call16 .word hold - . - 2 1: .byte b_call8 .byte conv_end - . - 1 .byte b_type .byte b_exit
      
      





バイトコマンドの開始シーケンスで、負の数を入力して確認します。



 $ as forth.asm -o forth.o -g -ahlsm>list.txt $ ld forth.o -o forth $ ./forth -1234 bye!
      
      





数字の結論があります!



完全なソース
 .intel_syntax noprefix stack_size = 1024 .section .data init_stack: .quad 0 init_rstack: .quad 0 msg_bad_byte: .ascii "Bad byte code!\n" msg_bad_byte_len = . - msg_bad_byte #  len    msg_bye: .ascii "bye!\n" msg_bye_len = . - msg_bye bcmd: .quad bcmd_bad, bcmd_bye, bcmd_num0, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x00 .quad bcmd_lit8, bcmd_lit16, bcmd_lit32, bcmd_lit64, bcmd_call8, bcmd_call16, bcmd_call32, bcmd_bad .quad bcmd_branch8, bcmd_branch16, bcmd_qbranch8, bcmd_qbranch16, bcmd_qnbranch8, bcmd_qnbranch16,bcmd_bad, bcmd_exit # 0x10 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_wm, bcmd_add, bcmd_sub, bcmd_mul, bcmd_div, bcmd_mod, bcmd_divmod, bcmd_abs # 0x20 .quad bcmd_var0, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_dup, bcmd_drop, bcmd_swap, bcmd_rot, bcmd_mrot, bcmd_over, bcmd_pick, bcmd_roll # 0x30 .quad bcmd_depth, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_get, bcmd_set, bcmd_get8, bcmd_set8, bcmd_get16, bcmd_set16, bcmd_get32, bcmd_set32 # 0x40 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_zeq, bcmd_zlt, bcmd_zgt, bcmd_eq, bcmd_lt, bcmd_gt, bcmd_lteq, bcmd_gteq #0x50 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x60 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_type, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad # 0x80 .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad .quad bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad, bcmd_bad start: .byte b_lit16 .word -1234 .byte b_call16 .word dot - . - 2 .byte b_bye base: .byte b_var0 .quad 10 holdbuf_len = 70 holdbuf: .byte b_var0 .space holdbuf_len holdpoint: .byte b_var0 .quad 0 # : hold holdpoint @ 1- dup holdbuf > if drop drop else dup holdpoint ! c! then ; hold: .byte b_call8 .byte holdpoint - . - 1 # holdpoint .byte b_get # @ .byte b_wm # 1- .byte b_dup # dup .byte b_call8 .byte holdbuf - . - 1 # holdbuf .byte b_gt # > .byte b_qbranch8 # if .byte 0f - . .byte b_drop # drop .byte b_drop # drop .byte b_branch8 #     ( then) .byte 1f - . 0: .byte b_dup # dup .byte b_call8 .byte holdpoint - . - 1 # holdpoint .byte b_set # ! .byte b_set8 # c! 1: .byte b_exit # ; # : # base /mod swap dup 10 < if c" 0 + else 10 - c" A + then hold ; conv: .byte b_call16 .word base - . - 2 # base .byte b_get # @ .byte b_divmod # /mod .byte b_swap # swap .byte b_dup # dup .byte b_lit8 .byte 10 # 10 .byte b_lt # < .byte b_qnbranch8 # if .byte 0f - . .byte b_lit8 .byte '0' # c" 0 .byte b_add # + .byte b_branch8 # else .byte 1f - . 0: .byte b_lit8 .byte '?' # c" A .byte b_add # + 1: .byte b_call16 .word hold - . - 2 # hold .byte b_exit # ; # : <# holdbuf 70 + holdpoint ! ; conv_start: .byte b_call16 .word holdbuf - . - 2 .byte b_lit8 .byte holdbuf_len .byte b_add .byte b_call16 .word holdpoint - . - 2 .byte b_set .byte b_exit # : #s do # dup 0=until ; conv_s: .byte b_call8 .byte conv - . - 1 .byte b_dup .byte b_qbranch8 .byte conv_s - . .byte b_exit # : #> holdpoint @ holdbuf 70 + over - ; conv_end: .byte b_call16 .word holdpoint - . - 2 .byte b_get .byte b_call16 .word holdbuf - . - 2 .byte b_lit8 .byte holdbuf_len .byte b_add .byte b_over .byte b_sub .byte b_exit dot: .byte b_dup .byte b_abs .byte b_call8 .byte conv_start - . - 1 .byte b_lit8 .byte ' ' .byte b_call16 .word hold - . - 2 .byte b_call8 .byte conv_s - . - 1 .byte b_drop .byte b_zlt .byte b_qnbranch8 .byte 1f - . .byte b_lit8 .byte '-' .byte b_call16 .word hold - . - 2 1: .byte b_call8 .byte conv_end - . - 1 .byte b_type .byte b_exit .section .text .global _start #     _start: mov rbp, rsp sub rbp, stack_size lea r8, start mov init_stack, rsp mov init_rstack, rbp jmp _next b_var0 = 0x28 bcmd_var0: push r8 b_exit = 0x17 bcmd_exit: mov r8, [rbp] add rbp, 8 _next: movzx rcx, byte ptr [r8] inc r8 jmp [bcmd + rcx*8] b_num0 = 0x02 bcmd_num0: push 0 jmp _next b_lit8 = 0x08 bcmd_lit8: movsx rax, byte ptr [r8] inc r8 push rax jmp _next b_lit16 = 0x09 bcmd_lit16: movsx rax, word ptr [r8] add r8, 2 push rax jmp _next b_call8 = 0x0C bcmd_call8: movsx rax, byte ptr [r8] sub rbp, 8 inc r8 mov [rbp], r8 add r8, rax jmp _next b_call16 = 0x0D bcmd_call16: movsx rax, word ptr [r8] sub rbp, 8 add r8, 2 mov [rbp], r8 add r8, rax jmp _next b_call32 = 0x0E bcmd_call32: movsx rax, dword ptr [r8] sub rbp, 8 add r8, 4 mov [rbp], r8 add r8, rax jmp _next b_lit32 = 0x0A bcmd_lit32: movsx rax, dword ptr [r8] add r8, 4 push rax jmp _next b_lit64 = 0x0B bcmd_lit64: mov rax, [r8] add r8, 8 push rax jmp _next b_dup = 0x30 bcmd_dup: push [rsp] jmp _next b_wm = 0x20 bcmd_wm: decq [rsp] jmp _next b_add = 0x21 bcmd_add: pop rax add [rsp], rax jmp _next b_sub = 0x22 bcmd_sub: pop rax sub [rsp], rax jmp _next b_mul = 0x23 bcmd_mul: pop rax pop rbx imul rbx push rax jmp _next b_div = 0x24 bcmd_div: pop rbx pop rax cqo idiv rbx push rax jmp _next b_mod = 0x25 bcmd_mod: pop rbx pop rax cqo idiv rbx push rdx jmp _next b_divmod = 0x26 bcmd_divmod: pop rbx pop rax cqo idiv rbx push rdx push rax jmp _next b_abs = 0x27 bcmd_abs: mov rax, [rsp] or rax, rax jge _next neg rax mov [rsp], rax jmp _next b_drop = 0x31 bcmd_drop: add rsp, 8 jmp _next b_swap = 0x32 bcmd_swap: pop rax pop rbx push rax push rbx jmp _next b_rot = 0x33 bcmd_rot: pop rax pop rbx pop rcx push rbx push rax push rcx jmp _next b_mrot = 0x34 bcmd_mrot: pop rcx pop rbx pop rax push rcx push rax push rbx jmp _next b_over = 0x35 bcmd_over: push [rsp + 8] jmp _next b_pick = 0x36 bcmd_pick: pop rcx push [rsp + 8*rcx] jmp _next b_roll = 0x37 bcmd_roll: pop rcx mov rbx, [rsp + 8*rcx] roll1: mov rax, [rsp + 8*rcx - 8] mov [rsp + 8*rcx], rax dec rcx jnz roll1 push rbx jmp _next b_depth = 0x38 bcmd_depth: mov rax, init_stack sub rax, rsp shr rax, 3 push rax jmp _next b_get = 0x40 bcmd_get: pop rcx push [rcx] jmp _next b_set = 0x41 bcmd_set: pop rcx pop rax mov [rcx], rax jmp _next b_get8 = 0x42 bcmd_get8: pop rcx movsx rax, byte ptr [rcx] push rax jmp _next b_set8 = 0x43 bcmd_set8: pop rcx pop rax mov [rcx], al jmp _next b_get16 = 0x44 bcmd_get16: pop rcx movsx rax, word ptr [rcx] push rax jmp _next b_set16 = 0x45 bcmd_set16: pop rcx pop rax mov [rcx], ax jmp _next b_get32 = 0x46 bcmd_get32: pop rcx movsx rax, dword ptr [rcx] push rax jmp _next b_set32 = 0x47 bcmd_set32: pop rcx pop rax mov [rcx], eax jmp _next # 0= b_zeq = 0x50 bcmd_zeq: pop rax or rax, rax jnz rfalse rtrue: push -1 jmp _next rfalse: push 0 jmp _next # 0< b_zlt = 0x51 bcmd_zlt: pop rax or rax, rax jl rtrue push 0 jmp _next # 0> b_zgt = 0x52 bcmd_zgt: pop rax or rax, rax jg rtrue push 0 jmp _next # = b_eq = 0x53 bcmd_eq: pop rbx pop rax cmp rax, rbx jz rtrue push 0 jmp _next # < b_lt = 0x54 bcmd_lt: pop rbx pop rax cmp rax, rbx jl rtrue push 0 jmp _next # > b_gt = 0x55 bcmd_gt: pop rbx pop rax cmp rax, rbx jg rtrue push 0 jmp _next # <= b_lteq = 0x56 bcmd_lteq: pop rbx pop rax cmp rax, rbx jle rtrue push 0 jmp _next # >= b_gteq = 0x57 bcmd_gteq: pop rbx pop rax cmp rax, rbx jge rtrue push 0 jmp _next b_branch8 = 0x10 bcmd_branch8: movsx rax, byte ptr [r8] add r8, rax jmp _next b_branch16 = 0x11 bcmd_branch16: movsx rax, word ptr [r8] add r8, rax jmp _next b_qbranch8 = 0x12 bcmd_qbranch8: pop rax or rax, rax jnz bcmd_branch8 inc r8 jmp _next b_qbranch16 = 0x13 bcmd_qbranch16: pop rax or rax, rax jnz bcmd_branch16 add r8, 2 jmp _next b_qnbranch8 = 0x14 bcmd_qnbranch8: pop rax or rax, rax jz bcmd_branch8 inc r8 jmp _next b_qnbranch16 = 0x15 bcmd_qnbranch16:pop rax or rax, rax jz bcmd_branch16 add r8, 2 jmp _next b_bad = 0x00 bcmd_bad: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 — stdout mov rsi, offset msg_bad_byte #     mov rdx, msg_bad_byte_len #   syscall #   mov rax, 60 #   № 1 - sys_exit mov rbx, 1 #    1 syscall #   b_bye = 0x01 bcmd_bye: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 — stdout mov rsi, offset msg_bye #     mov rdx, msg_bye_len #   syscall #   mov rax, 60 #   № 60 - sys_exit mov rdi, 0 #    0 syscall #   b_type = 0x80 bcmd_type: mov rax, 1 #   № 1 - sys_write mov rdi, 1 #  № 1 - stdout pop rdx pop rsi push r8 syscall #   pop r8 jmp _next
      
      





まとめ



これで、バイトコマンドのかなりまともなコアができました。すべての基本的な算術演算、スタック操作、比較操作、メモリの操作、変数です。 また、バイトコードで完全に実装された数値の出力がすでにあります。 インタプリタが実行する準備はすべて整っています。これは次の記事で行います!



明けましておめでとうございます!



批判は大歓迎です! :)



続き: ネイティブアメリカンの砦のバイトマシン(だけでなく)(パート3)



All Articles