Mesin byte untuk benteng (dan tidak hanya) di Native American (bagian 2)

gambar

Mari kita lanjutkan eksperimen dengan bytecode. Ini adalah kelanjutan dari artikel tentang byte-machine di assembler, di sini adalah bagian pertama .

Secara umum, saya berencana membuat fort interpreter di bagian kedua, dan fort compiler untuk mesin byte ini di bagian ketiga. Tetapi volume yang diperoleh untuk artikel itu sangat besar. Untuk membuat penerjemah, Anda perlu memperluas kernel (seperangkat perintah byte), dan mengimplementasikan: variabel, string parsing, memasukkan string, kamus, kamus pencarian ... Ya, setidaknya output angka harus bekerja. Akibatnya, saya memutuskan untuk membagi artikel pada juru bahasa menjadi dua. Oleh karena itu, dalam artikel ini kami akan memperluas kernel, menentukan variabel, menggambar output angka. Berikut ini adalah contoh rencana: bagian ke-3 adalah juru bahasa, bagian ke-4 adalah kompiler. Dan, tentu saja, tes kinerja. Mereka akan berada di artikel 4 atau 5. Artikel-artikel ini akan menjadi setelah tahun baru.

Dan yang belum takut pada assembler dan bytecode yang mengerikan - selamat datang di cut! :)

Pertama, perbaiki kesalahan. Mari kita atur ekstensi file .s, seperti kebiasaan untuk GAS (terima kasih mistergrim ). Kemudian, ganti int 0x80 dengan syscall dan gunakan register 64-bit (terima kasih qw1 ). Pada awalnya, saya tidak hati-hati membaca deskripsi panggilan dan hanya mengoreksi register ... dan mendapatkan kesalahan Segmentasi. Ternyata semuanya telah berubah untuk syscall, termasuk nomor panggilan. sys_write untuk syscall adalah angka 1, dan sys_exit adalah 60. Sebagai hasilnya, perintah bad, type, dan bye mengambil bentuk sebagai berikut:

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 

Dan satu hal lagi. Cukup benar, dalam komentar di artikel terakhir, berez dan fpauk menulis bahwa jika alamat prosesor digunakan dalam bytecode, maka bytecode tergantung pada platform. Dan dalam contoh itu, alamat baris untuk "Halo, dunia!" Ditetapkan dalam bytecode oleh nilai (dengan perintah lit64). Tentu saja ini tidak perlu. Tapi itu cara termudah untuk memeriksa mesin byte. Saya tidak akan melakukan ini lagi, tetapi saya akan mendapatkan alamat variabel dengan cara lain: khususnya, dengan perintah var (lebih lanjut tentang itu nanti).

Lakukan pemanasan


Dan sekarang, sebagai pemanasan, kita akan melakukan semua operasi aritmatika integer dasar (+, -, *, /, mod, / mod, abs). Kami akan membutuhkannya.

Kode ini sangat sederhana sehingga saya membawanya dalam spoiler tanpa komentar.

Aritmatika
 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 

Secara tradisional, di benteng, operasi presisi ganda ditambahkan ke operasi aritmatika dan stack biasa. Kata-kata untuk operasi seperti itu biasanya dimulai dengan "2": 2DUP, 2SWAP, dll. Tapi kami memiliki aritmatika standar sudah 64 bit, dan kami pasti tidak akan melakukan 128 hari ini :)

Selanjutnya, kami menambahkan operasi stack dasar (drop, swap, root, -root, over, pick, roll).

Operasi tumpukan
 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 

Dan kami juga akan membuat perintah untuk membaca dan menulis ke memori (kata-kata Fort @ dan!). Serta rekan-rekan mereka ke kedalaman bit yang berbeda.

Membaca dan menulis ke memori
 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 

Kami mungkin masih membutuhkan tim pembanding, kami juga akan melakukannya.

Perintah perbandingan
 # 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 

Kami tidak akan menguji operasi. Yang utama adalah assembler tidak akan memberikan kesalahan saat kompilasi. Debugging akan sedang dalam proses menggunakannya.

Segera buat kata kedalaman (stack depth). Untuk melakukan ini, saat memulai, simpan nilai awal tumpukan data dan tumpukan kembali. Nilai-nilai ini mungkin masih berguna ketika memulai ulang sistem.

 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 

Output angka


Nah, pemanasan sudah selesai, dan Anda harus sedikit berkeringat. Ajari sistem kami untuk mengeluarkan angka. Untuk menampilkan angka di benteng, kata "." (titik). Mari kita lakukan cara itu dilakukan dalam implementasi Fort standar, menggunakan kata-kata <#, tahan, #, #s, #>, basis. Harus menyadari semua kata-kata ini. Untuk membentuk angka, penyangga dan penunjuk ke karakter yang akan digunakan digunakan, ini akan menjadi kata holdbuf dan holdpoint.

Jadi, kita perlu kata-kata ini:

  • holdbuf - buffer untuk menghasilkan representasi angka, formasi terjadi dari akhir
  • holdpoint - alamat ke karakter yang terakhir ditampilkan (dalam holdbuf)
  • <# - awal pembentukan angka; menetapkan holdpoint ke byte, setelah holdbuf byte terakhir
  • hold - mengurangi holdpoint sebanyak 1 dan menyimpan karakter dari stack ke buffer di alamat yang diterima
  • # - membagi kata di bagian atas tumpukan ke dasar sistem bilangan, menerjemahkan sisa divisi menjadi karakter dan menyimpannya ke buffer menggunakan penahan
  • #s - mengonversi seluruh kata; sebenarnya memanggil kata # dalam satu lingkaran sampai 0 tersisa di tumpukan
  • #> - penyelesaian konversi; mendorong awal string yang terbentuk dan panjangnya ke tumpukan

Kami akan melakukan semua kata dalam bytecode, tetapi pertama-tama, mari kita berurusan dengan variabel.

Variabel


Dan di sini akan ada sedikit sihir Fortian. Faktanya adalah bahwa dalam benteng variabel adalah sebuah kata. Ketika kata ini dieksekusi, alamat sel memori yang menyimpan nilai variabel ada di tumpukan. Anda dapat membaca atau menulis ke alamat ini. Misalnya, untuk menulis nilai 12345 ke variabel A, Anda perlu menjalankan perintah berikut: "12345 A!". Dalam contoh ini, 12345 didorong ke tumpukan, maka variabel A mendorong alamatnya, dan kata "!" menghapus dua nilai dari tumpukan dan menulis 12345 ke alamat variabel A. Dalam implementasi khas benteng (dengan kode langsung), variabelnya adalah perintah CALL mikroprosesor dengan alamat _next, setelah itu tempat dicadangkan untuk menyimpan nilai variabel. Ketika mengeksekusi kata seperti itu, mikroprosesor mentransfer kontrol ke _next dan mendorong alamat pengirim (via RSP) ke stack. Tetapi di benteng, tumpukan mikroprosesor adalah aritmatika, dan kami tidak akan kembali ke mana pun. Sebagai akibatnya, eksekusi berlanjut, dan pada stack adalah alamat variabel. Dan semua ini dengan satu tim prosesor! Di assembler, akan terlihat seperti ini:

  call _next #   _next,      ,   12345 .quad 12345 

Tapi kami punya bytecode, dan kami tidak bisa menggunakan mekanisme ini! Saya tidak segera mencari tahu bagaimana membuat mekanisme seperti itu pada bytecode. Tetapi, jika Anda berpikir logis, itu tidak mengganggu implementasi sesuatu yang sangat mirip. Hanya perlu diingat bahwa ini bukan perintah prosesor, tetapi bytecode, lebih tepatnya, "subrutin" pada bytecode. Inilah pernyataan masalahnya:

  • ini adalah kode byte, ketika mentransfer kontrol yang harus segera dikembalikan darinya
  • setelah kembali, alamat tempat nilai variabel disimpan harus tetap berada di tumpukan aritmatika

Kami memiliki perintah byte keluar. Mari kita membuat kata pada bytecode yang berisi perintah keluar tunggal. Maka perintah ini akan kembali darinya. Masih membuat perintah yang sama, yang juga mendorong alamat byte berikutnya pada stack (register R8). Kami akan melakukan ini sebagai titik masuk tambahan untuk keluar untuk menghemat transisi:

 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] 

Sekarang variabel dasar akan terlihat seperti ini:
 base: .byte b_var0 .quad 10 

By the way, mengapa tepatnya var0, dan bukan hanya var? Faktanya adalah bahwa akan ada perintah lain untuk mengidentifikasi kata-kata yang lebih maju yang berisi data. Saya akan menjelaskan secara lebih rinci dalam artikel berikut.

Sekarang kita siap untuk menggambar. Ayo mulai!

Kata dasar, holdbuf, holdpoint


Bagaimana variabel akan diatur sudah diputuskan. Oleh karena itu, kata-kata dasar, holdbuf, holdpoint diperoleh sebagai berikut:

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

Ukuran buffer holdbuf dipilih 70. Jumlah bit bit maksimum adalah 64 (ini jika Anda memilih sistem biner). Cadangan lain dari beberapa karakter telah dibuat untuk menempatkan, misalnya, tanda nomor dan spasi setelahnya. Kami akan memeriksa buffer overflow, tetapi untuk saat ini kami tidak akan memasukkan karakter tambahan dalam buffer. Maka akan memungkinkan untuk membuat diagnosis lain.

tahan


Sekarang Anda dapat membuat kata bertahan. Di benteng, kodenya terlihat seperti ini:

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

Bagi mereka yang melihat benteng untuk pertama kalinya, saya akan menganalisis kode secara detail. Untuk kata-kata selanjutnya saya tidak akan melakukan ini.

Pada awalnya ada kata untuk definisi kata baru dan nama kata baru: ": tahan". Setelah itu muncul kode yang diakhiri dengan kata ";". Mari kita menganalisis kode kata tersebut. Saya akan memberikan perintah dan status stack setelah menjalankan perintah. Sebelum kata itu dipanggil, ada kode karakter pada tumpukan, yang ditempatkan di buffer (ditunjukkan oleh <character>). Lebih lanjut ternyata seperti ini:

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

Setelah itu adalah perintah if, yang mengkompilasi menjadi lompatan bersyarat ke urutan perintah antara yang lain dan kemudian. Cabang bersyarat menghapus hasil perbandingan dari tumpukan dan melakukan transisi jika ada kebohongan di tumpukan. Jika tidak ada transisi, cabang antara jika dan yang lain dijalankan, di mana ada dua perintah drop yang menghapus karakter dan alamat. Jika tidak, eksekusi berlanjut. Kata "!" menyimpan nilai baru di holdpoint (alamat dan nilai dihapus dari tumpukan). Dan kata "c!" menulis karakter ke buffer, ini adalah perintah set8 byte (alamat dan nilai karakter dihapus dari stack).

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

Inilah berapa banyak tindakan yang dilakukan oleh urutan pendek perintah ini! Ya, benteng itu ringkas. Dan sekarang kita menghidupkan "kompiler" manual di kepala :) Dan kita mengkompilasi semua ini menjadi bytecode:
 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 # ; 

Di sini saya menggunakan label lokal (0 dan 1). Label-label ini dapat diakses dengan nama khusus. Misalnya, label 0 dapat diakses dengan nama 0f atau 0b. Ini berarti tautan ke label 0 terdekat (maju atau mundur). Cukup nyaman untuk label yang digunakan secara lokal, agar tidak muncul dengan nama yang berbeda.

Kata #


Mari kita buat kata #. Di benteng, kodenya akan terlihat seperti ini:

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

Kondisi di sini digunakan untuk memeriksa: apakah angka yang diterima kurang dari sepuluh? Jika kurang, angka 0–9 digunakan, jika tidak, karakter yang dimulai dengan ā€œAā€ digunakan. Ini akan memungkinkan bekerja dengan sistem angka heksadesimal. Urutan c ″ 0 mendorong kode karakter 0 ke tumpukan. Kami menghidupkan "kompiler":

 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 # ; 

Kata <#


Kata <# ini sangat sederhana:

 : <# holdbuf 70 + holdpoint ! ; 

Bytecode:

 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 

Word #>


Kata #> untuk menyelesaikan konversi terlihat seperti ini:

 : #> holdpoint @ holdbuf 70 + over - ; 

Bytecode:

 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 

Kata #s


Dan akhirnya, kata #s:

 : #s do # dup 0= until ; 

Bytecode:

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

Siapa pun yang berhati-hati akan melihat sedikit perbedaan antara bytecode dan kode benteng :)

Semuanya sudah siap


Sekarang, tidak ada yang akan menghentikan Anda dari membuat kata ".", Yang menampilkan angka:

 : . <# #s drop #> type ; 

Bytecode:

 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 

Mari kita buat bytecode pengujian yang memeriksa poin kita:

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

Tentu saja, itu tidak berhasil sekaligus. Tapi, setelah debugging, hasil berikut diperoleh:

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

Kusen terlihat segera. Setelah nomor itu, benteng harus menampilkan spasi. Tambahkan setelah conv_start (<#) panggil perintah 32 hold.

Kami juga menarik kesimpulan dari tanda itu. Di awal, tambahkan dup abs, dan di akhir, periksa tanda salinan kiri dan masukkan minus jika angkanya negatif (0 <jika c ″ tahan lalu). Akibatnya, kata "." mengambil formulir ini:

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

Bytecode:

 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 

Di urutan awal perintah byte, masukkan angka negatif dan periksa:

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

Ada kesimpulan angka!

Sumber lengkap
 .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 

Ringkasan


Sekarang kita memiliki inti perintah byte yang cukup baik: semua aritmatika dasar, operasi stack, operasi perbandingan, bekerja dengan memori, variabel. Juga, sudah ada output angka, sepenuhnya diimplementasikan dalam bytecode. Semuanya siap untuk dilakukan penerjemah, yang akan kita lakukan di artikel selanjutnya!

Selamat Tahun Baru untuk semua!

Kritik dipersilahkan! :)

Lanjutan: Mesin byte untuk benteng (dan tidak hanya) di Native American (bagian 3)

Source: https://habr.com/ru/post/id433836/


All Articles