From 149bf5b7e03d2f827754b826af84ada72bda9080 Mon Sep 17 00:00:00 2001 From: julianb0 Date: Mon, 17 Jun 2019 20:59:30 +0200 Subject: [PATCH] printf! :D --- as/k-as.py | 76 ++++--- ka/ABI | 38 +++- ka/crt/fmt/_doprnt.k | 236 ++++++++++++++++++++++ ka/crt/fmt/format.k | 2 + ka/crt/fmt/itoa.k | 34 ++-- ka/crt/{prn/print.k => fmt/printf.k} | 19 +- ka/crt/str/strchr.k | 16 +- ka/crt/str/strrev.k | 4 +- ka/dos.k | 1 - ka/main.k | 34 ++-- vm/in/INSTRS | 163 +++++---------- vm/in/mov.c | 16 ++ vm/in/string.c | 191 +++--------------- vm/pc/decd.c | 286 ++++++++------------------- vm/pc/dump.c | 7 +- vm/pc/exec.c | 121 ++++++++++++ 16 files changed, 668 insertions(+), 576 deletions(-) create mode 100644 ka/crt/fmt/_doprnt.k rename ka/crt/{prn/print.k => fmt/printf.k} (61%) create mode 100644 vm/pc/exec.c diff --git a/as/k-as.py b/as/k-as.py index 25bc874..a7aa99f 100755 --- a/as/k-as.py +++ b/as/k-as.py @@ -106,7 +106,7 @@ def do_includes(fi): global inc_depth for _, line in enumerate(fi): line = line.rstrip() - tok = line.split(' ', 1) + tok = line.split(None, 1) if len(tok) == 0: continue @@ -143,6 +143,8 @@ def do_includes(fi): else: source.write("{}\n".format(line)) + inc_depth -= 1 + #------------------------------------------------------------------------------- @@ -302,6 +304,8 @@ def parse_preproc(line): return print("Unrecognized directive: {}".format(line)) + leave() + sys.exit(1) #------------------------------------------------------------------------------- @@ -378,7 +382,7 @@ def parse_instr(line): if line == None or len(line) == 0: return 0 - tok = line.split(' ', 1) + tok = line.split(None, 1) instr = tok[0].strip() @@ -440,6 +444,11 @@ def parse_instr(line): if len(fts) != 0: fts += ' ' + # preprocessor + if word in pdefs: + word = pdefs[word] + # Fall through + # memory length prefixes if len(word) > 2 and '[' in word: if word[0] in 'bwlq': @@ -459,6 +468,11 @@ def parse_instr(line): if word[0] == '[': assert(word[-1] == ']') word = word[1:-1] + + # preprocessor, again + if word in pdefs: + word = pdefs[word] + # Fall through # # Make sure we got an access length prefix @@ -470,11 +484,16 @@ def parse_instr(line): instr_name += "_m" + # cheap way of getting [reg - imm] to work + word = word.replace('-', '+ -') + + # remove every spaces! + word = word.replace(' ', '') + # # Offsets # if '+' in word: - reg1 = "inv" reg2 = "inv" imm1 = '1' @@ -544,7 +563,7 @@ def parse_instr(line): # Update fts and instr_args # - instr_args += "{}:{} ".format(reg2.strip(), reg1.strip()) + instr_args += "{}:{} ".format(reg2, reg1) size += 2 if imm1 == '1': @@ -561,37 +580,6 @@ def parse_instr(line): instr_args += "%%imm16 {} %%imm16 {}".format(imm1, imm2) continue - - """ - # +2 for A_OFF, +2 for offset, +2 for regoff, +2 for register - size += 2 + 2 + 2 + 2 - instr_args += "off " - - assert(len(word) > 3) - - regoff = "inv" - - # [reg+off] or [reg+regoff] - if len(word.split('+')) == 2: - reg, off = word.split('+', 1) - - if not is_number(off): - regoff = off - off = '0' - - # [reg+regoff+off] - else: - assert(len(word.split('+')) == 3) - reg, regoff, off = word.split('+', 2) - - off = off.strip() - reg = reg.strip() - regoff = regoff.strip() - - instr_args += "{} {} {}".format(off, regoff, reg) - - continue - """ # # [imm64] or [reg] @@ -600,11 +588,15 @@ def parse_instr(line): fellthrough = True # FALLTHROUGH - # preprocessor + # preprocessor, yet again if word in pdefs: word = pdefs[word] # Fall through + # characters 'c' + if len(word) == 3 and word[0] == word[-1] == "'": + word = str(ord(word[1])) + # for now every immediate is 64-bit if is_number(word): # +8 for immediate @@ -656,8 +648,8 @@ def parse_instr(line): # Compute FT1 and FT2 # if ' ' in fts: - assert(len(fts.split(' ')) == 2) - ft1, ft2 = fts.split(' ') + assert(len(fts.split()) == 2) + ft1, ft2 = fts.split() w2 |= get_fts_mask(ft1, line) << 5 w2 |= get_fts_mask(ft2, line) @@ -690,7 +682,8 @@ def gentext(): data_start += (8 - data_start % 8) for _, line in enumerate(instrs): - tok = line.strip().split(' ') + tok = line.strip().split() + #print(tok) for word in tok: if len(word) == 0: @@ -702,6 +695,11 @@ def gentext(): continue if ':' in word: + if len(word.split(':')) < 2: + print("Stray ':' in line: {}".format(line)) + leave() + sys.exit(1) + reg2, reg1 = word.split(':', 1) idx1 = pregs.index(reg1) idx2 = pregs.index(reg2) diff --git a/ka/ABI b/ka/ABI index 598179d..12f8955 100644 --- a/ka/ABI +++ b/ka/ABI @@ -75,8 +75,9 @@ of the flags in the FLG register. Passing parameters is done using the following registers, in that order: ax0-ax7, lx0-lx7 -The stack is never used for argument passing. If you need to pass large -structures of data, pass their address in an appropriate register. +The stack is never used for argument passing, except for variadic functions, +cf the next section. If you need to pass large structures of data, pass +their address in an appropriate register. Return values are passed in 'rax'. If the return value does not fit and require more registers, use the following registers, in that order: @@ -84,14 +85,41 @@ and require more registers, use the following registers, in that order: The following registers are volatile; the calling function cannot assume that they will be left unmodified by the called function: - rax, rcx, rdx, rsi, rdi, lx0-lx7, ax0-ax7 + rax, rcx, rdx, rsx, lx0-lx7, ax0-ax7 The following registers are nonvolatile; the called function must preserve them: - rbp, rsp, rbx, rsx, rbi, nx0-nx7 + rbx, rsi, rdi, rbi, nx0-nx7, rbp, rsp #------------------------------------------------------------------------------# -3. SPECIAL REGISTERS +3. VARIADIC FUNCTIONS + +To call a variadic function, do this: + sub rsp, nargs * 8 + mov [rsp], arg0 + ... + ... + ... + mov [rsp+(N*8)], argN + call variadic_func + add rsp, nargs * 8 + +To the variadic function, argN can be accessed the following way: + mov reg, [rbp + N*8 + 16] + +For instance: + mov rax, [rbp + 16] ; arg1 + mov rdx, [rbp + 24] ; arg2 + +It is recommended to use the reg+reg*imm16+imm16 memory format: + mov rax, [rbp + rcx * 8 + 16] ; accesses arg#rcx + +The 'va_list' type can be regarded as a pointer to the +variadic function's rbp+16 + +#------------------------------------------------------------------------------# + +4. SPECIAL REGISTERS The 'inv' register cannot be referenced by machine code except when specified as an offset register in the [reg+reg(*/+...)] memory formats; in these case, diff --git a/ka/crt/fmt/_doprnt.k b/ka/crt/fmt/_doprnt.k new file mode 100644 index 0000000..f242de6 --- /dev/null +++ b/ka/crt/fmt/_doprnt.k @@ -0,0 +1,236 @@ +; The OS/K Team licenses this file to you under the MIT license. +; See the LICENSE file in the project root for more information. + +_doprnt_test: + mov ax0, .fmt + sub rsp, 128 + mov q[rsp+40], 0x7FE + mov q[rsp+32], -2 + mov q[rsp+24], 666 + mov q[rsp+16], 0x112233DDAA6677BB + mov q[rsp+8], 'K' + mov q[rsp], .str + call printf + add rsp, 128 + + ret + +.fmt = "%% Hello World %s - %c - %p - %d - %d - %b" +.str = "(cc)" + +; +; typedef int (*PUTC)(int ch) +; int _doprnt(PUTC putc, int n, const char *fmt, va_list ap) +; +_doprnt: + enter 8 + mov q[rbp-8], rbx + mov q[rbp-16], nx0 + mov q[rbp-24], nx1 + mov q[rbp-32], nx2 + mov q[rbp-40], rdi + mov q[rbp-48], rsi + mov q[rbp-56], nx3 + + mov rbx, ax2 ; fmt + mov nx3, ax0 ; putc + mov nx0, ax1 ; n + xor nx1, nx1 ; return value + xor nx2, nx2 ; index in va_list + mov rsi, ax3 ; ap + +.main_loop: + ; find '%' or null-terminator + mov rcx, STRLEN_MAX + mov rdi, rbx + scasb.rep.nz rdi, '%' + +.print_regular: + ; everything below rdi is a regular character; print it + cmp rbx, rdi + j.z .check_modf + + mov ax0, b[rbx] + call .doput + + inc rbx + jmp .print_regular + +.check_modf: + ; did we find a '%' ? + cmp b[rbx], '%' + j.nz .epilogue ; no, we found fmt's null-terminator; we're done + + ; we did find a modifier / '%' + mov rax, b[rbx+1] + add rbx, 2 + + cmp rax, 's' + j.z .modf_s + cmp rax, 'c' + j.z .modf_c + cmp rax, 'p' + j.z .modf_p + cmp rax, 'x' + j.z .modf_x + cmp rax, 'u' + j.z .modf_u + cmp rax, 'd' + j.z .modf_d + cmp rax, 'o' + j.z .modf_o + cmp rax, 'b' + j.z .modf_b + cmp rax, '%' + j.z .modf_percent + ; unrecognized + jmp .bad_modifier + +.modf_s: + ; get string address + mov rdi, q[rsi + nx2 * 8] + inc nx2 + + test rdi, rdi + j.z .nullstring + +.print_string: + mov ax0, b[rdi] + test ax0, ax0 + j.z .main_loop + + inc rdi + call .doput + jmp .print_string + +.modf_c: + mov ax0, q[rsi + nx2 * 8] + inc nx2 + call .doput + jmp .main_loop + +.modf_p: + mov ax0, '0' + call .doput + mov ax0, 'x' + call .doput + ; Fallthrough + +.modf_x: + mov ax2, 16 + mov ax3, 1 + jmp .print_number + +.modf_u: + mov ax2, 10 + mov ax3, 1 + jmp .print_number + +.modf_d: + mov ax2, 10 + xor ax3, ax3 + jmp .print_number + +.modf_o: + mov ax2, 8 + mov ax3, 1 + jmp .print_number + +.modf_b: + mov ax2, 2 + mov ax3, 1 + jmp .print_number + +.print_number: + ; allocate itoa convertion buffer + sub rsp, 80 + mov rdi, rsp + + ; assume modifier already set up ax2 and ax3 + mov ax0, rsp + mov ax1, q[rsi + nx2 * 8] + inc nx2 + call _itoa + +.print_itoa_buf: + mov ax0, b[rdi] + test ax0, ax0 + add.z rsp, 80 + j.z .main_loop + + inc rdi + call .doput + jmp .print_itoa_buf + +.modf_percent: + mov ax0, '%' + call .doput + jmp .main_loop + +.bad_modifier: + ; print "%?" to clearly indicate that something is wrong + mov ax0, '$' + call .doput + + mov ax0, '?' + call .doput + + jmp .main_loop + +.nullstring: + ; %s was passed a NULL + mov ax0, '(' + call .doput + + mov ax0, 'n' + call .doput + + mov ax0, 'u' + call .doput + + mov ax0, 'l' + call .doput + + mov ax0, 'l' + call .doput + + mov ax0, ')' + call .doput + + jmp .main_loop + +.epilogue: + mov rax, nx1 + mov rbx, q[rbp-8] + mov nx0, q[rbp-16] + mov nx1, q[rbp-24] + mov nx2, q[rbp-32] + mov rdi, q[rbp-40] + mov rsi, q[rbp-48] + mov nx3, q[rbp-56] + leave + ret +; +; prints ax0 +; +.doput: + ; update print count + inc nx1 + + ; if n==0, don't print + ; we follow the C convention that sprintf()-like functions + ; should return the number of characters that would have + ; been printed/written if 'n' were big enough + test nx0, nx0 + ret.z + + ; if n>0, decrement n and print + dec nx0 + call nx3 + + ; did putc fail? + cmp rax, 0 + xor.nz nx0, nx0 ; yes, so artificially set n=0 + + ret + diff --git a/ka/crt/fmt/format.k b/ka/crt/fmt/format.k index 709e832..9ee093e 100644 --- a/ka/crt/fmt/format.k +++ b/ka/crt/fmt/format.k @@ -2,4 +2,6 @@ ; See the LICENSE file in the project root for more information. include "crt/fmt/itoa.k" +include "crt/fmt/_doprnt.k" +include "crt/fmt/printf.k" diff --git a/ka/crt/fmt/itoa.k b/ka/crt/fmt/itoa.k index 9c664af..3445147 100644 --- a/ka/crt/fmt/itoa.k +++ b/ka/crt/fmt/itoa.k @@ -2,9 +2,21 @@ ; See the LICENSE file in the project root for more information. ; -; char *_itoa(char *buf, int num, int base, int unsi) +; wrappers ; -; Acts as itoa if unsi == 0, as utoa otherwise + +itoa: + mov ax3, 0 + jmp _itoa + +utoa: + mov ax3, 1 + jmp _itoa + +; +; void _itoa(char *buf, int num, int base, int unsi) +; +; Behaves as itoa if unsi == 0, as utoa otherwise ; _itoa: mov rax, ax0 @@ -46,11 +58,11 @@ _itoa: cmp lx1, 9 ; lx1 > 9 ? jmp.a .nondec - add lx1, 48 ; '0' + add lx1, '0' jmp .next .nondec: - add lx1, 87 ; 'a' - 10 + add lx1, 55 ; 'A' - 10 .next: mov b[ax0], lx1 @@ -62,7 +74,7 @@ _itoa: ; add minus flag, null-terminate and reverse .fini: cmp lx0, -1 - mov.z b[ax0], 45 ; '-' + mov.z b[ax0], '-' inc.z ax0 mov b[ax0], 0 @@ -86,15 +98,3 @@ _itoa: mov b[ax0+1], 0 ret -; -; wrappers -; - -itoa: - mov ax3, 0 - jmp _itoa - -utoa: - mov ax3, 1 - jmp _itoa - diff --git a/ka/crt/prn/print.k b/ka/crt/fmt/printf.k similarity index 61% rename from ka/crt/prn/print.k rename to ka/crt/fmt/printf.k index b55cef1..64d5545 100644 --- a/ka/crt/prn/print.k +++ b/ka/crt/fmt/printf.k @@ -2,15 +2,28 @@ ; See the LICENSE file in the project root for more information. ; -; Max amount of characters that print() will print +; int putc(int ch) ; -v_print_max := 0xFF +putc: + prn ax0 + xor rax, rax + ret + +; +; int printf(const char *fmt, ...) +; +printf: + mov ax2, ax0 + mov ax0, putc + mov ax1, STRLEN_MAX + lea ax3, b[rsp+8] + jmp _doprnt ; ; Print a string ; print: - mov rcx, v_print_max + mov rcx, STRLEN_MAX .1: test b[ax0], b[ax0] diff --git a/ka/crt/str/strchr.k b/ka/crt/str/strchr.k index 1181925..2bca5ae 100644 --- a/ka/crt/str/strchr.k +++ b/ka/crt/str/strchr.k @@ -5,14 +5,22 @@ ; char *strchrnul(const char *str, int ch) ; strchrnul: - + mov rcx, STRLEN_MAX + scasb.rep.nz ax0, ax1 + + mov rax, ax0 ret ; ; char *strchr(const char *str, int ch) ; strchr: - call strchrnul - test b[rax], b[rax] - mov.z rax, 0 + mov rcx, STRLEN_MAX + scasb.rep.nz ax0, ax1 + + cmp b[ax0], 0 + xor.z rax, rax + ret.z + + mov rax, ax0 ret diff --git a/ka/crt/str/strrev.k b/ka/crt/str/strrev.k index b2602ae..50d5e88 100644 --- a/ka/crt/str/strrev.k +++ b/ka/crt/str/strrev.k @@ -17,7 +17,7 @@ strrev: ; go to str's end, just before ; the null terminator mov rcx, STRLEN_MAX - scazsb.rep.nz ax1 + scasb.rep.nz ax1, 0 dec ax1 .2: @@ -48,7 +48,7 @@ strrev2: ; go to str's end, just before ; the null terminator mov rcx, STRLEN_MAX - scazsb.rep.nz ax1 + scasb.rep.nz ax1, 0 dec ax1 ; increase ax0 while decreasing ax1, performing exchanges diff --git a/ka/dos.k b/ka/dos.k index ae068b5..9b0b060 100644 --- a/ka/dos.k +++ b/ka/dos.k @@ -25,7 +25,6 @@ include "inc/regs.k" ; include "crt/err/errno.k" include "crt/fmt/format.k" -include "crt/prn/print.k" include "crt/str/string.k" ; diff --git a/ka/main.k b/ka/main.k index cb21e2f..c2f7b19 100644 --- a/ka/main.k +++ b/ka/main.k @@ -5,17 +5,28 @@ ; Main function ; main: - call showoff + call _doprnt_test ret showoff: call ramdev_test call bswap_test call itoa_test + prn 10 call str_test call movzx_test ret +strchr_test: + mov rax, 0 + mov ax0, .str + mov ax1, 33 + call strchr + prn b[rax] + ret + +.str = "Hello World!" + bswap_test: mov rdx, 0x1122334455667788 bswap rax, rdx @@ -35,25 +46,6 @@ ramdev_test: call MEM.GetMemSize ret -stosb_test: - mov rcx, 11 - mov rax, 33 - mov rdi, .buf - mov rsi, .buf - stosb.rep - - mov rbx, rdi - sub rbx, rsi - - mov ax0, .buf - mov ax1, 12 - call print_n - - ret - -.str1 = "Hello World!\n" -.buf = [32] - movzx_test: enter 1 @@ -139,7 +131,7 @@ str_test: call strcmp mov rsx, rax - mov ax0, .msg + mov ax0, .buf2 call strlen ret diff --git a/vm/in/INSTRS b/vm/in/INSTRS index 299f74e..526678f 100644 --- a/vm/in/INSTRS +++ b/vm/in/INSTRS @@ -316,6 +316,20 @@ xchg rm rim # cmpxchg rm rim +# +# Load argument #N (LDARG) +# +# IF ($2 < 8) THEN +# $1 = AX$2 +# ELSE +# $1 = LX($2-8) +# FI +# +# Throws: +# #ILL if $2 ≥ 16 +# +ldarg rm rim + #---------------------------------------------------------------------------# # Stack manipulation instructions # #---------------------------------------------------------------------------# @@ -359,7 +373,7 @@ leave # # Store value into string (STOSx) # -# [%str] = $val +# [%1] = $2 # IF (DF == 0) THEN # %str = %str + sizeof(x) # ELSE @@ -370,14 +384,15 @@ leave # When one parameter is given, %str = RDI and $val = $1 # When two parameters are given, %str = $1 and $val = $2 # -stosb -stosb rim stosb r rim +stosw r rim +stosl r rim +stosq r rim # # Load value from string (LODSx) # -# %dest = [%str] +# $1 = [%2] # IF (DF == 0) THEN # %str = %str + sizeof(x) # ELSE @@ -387,176 +402,92 @@ stosb r rim # Preserves CF, OF and SF # Sets ZF according to the loaded value # -# When no parameters are given, %dest = RAX and %str = RSI -# When one parameter is given, %dest = $1 and %str = RSI -# When two parameters are given, %dest = $1 and %str = $2 -# -lodsb -lodsb r lodsb r r -lodsw -lodsw r lodsw r r -lodsl -lodsl r lodsl r r -lodsq -lodsq r lodsq r r # # Scan string for a particular value (SCASx) # -# CMP([%str], $val) +# CMP([%1], $2) # -# IF (DF == 0) THEN -# %str = %str + sizeof(x) -# ELSE -# %str = %str - sizeof(x) -# FI -# -# Sets CF, OF, ZF and SF according to the result of the comparison -# -# When no parameters are given, %str = RDI and $val = RAX -# When one parameter is given, %str = RDI and $val = $1 -# When two parameters are given, %str = $1 and $val = $2 -# -# Note that SCASx moves in the string no matter whether the value -# was found or not; therefore when the value *is* found, it will -# be sizeof(x) bytes below the current value of %str -# -scasb -scasb rim -scasb r rim -scasw -scasw rim -scasw r rim -scasl -scasl rim -scasl r rim -scasq -scasq rim -scasq r rim - -# -# Scan string for null terminator (SCAZSx) -# -# CMP([%str], 0) -# -# IF (ZF == 0) +# IF ([%1] == 0) THEN +# ZF = 1 +# ELIF (ZF == 0) THEN # IF (DF == 0) THEN -# %str = %str + sizeof(x) +# %1 = %1 + sizeof(x) # ELSE -# %str = %str - sizeof(x) +# %1 = %1 - sizeof(x) # FI # FI # -# Sets CF, OF, ZF and SF according to the result of the comparison +# Sets CF, OF and SF according to the result of the comparison +# Sets ZF according to whether [%1] and $2 are equal, OR if [%1] is null # -# When no parameters are given, %str = RDI and $val = RAX -# When one parameter is given, %str = RDI and $val = $1 -# When two parameters are given, %str = $1 and $val = $2 +# Notes: +# - Does not move past the value when found +# - 'SCASB.REP.NZ reg ch' is a short 'strchnul()' # -# Unlike SCASx, this instruction does NOT move forward after -# finding what it was looking for. The instruction: -# REP.NZ SCAZSx %str -# serves as a much faster shorthand for -# REP.NZ SCASx %str -# DEC %str -# -scazsb -scazsb r -scazsw -scazsw r -scazsl -scazsl r -scazsq -scazsq r +scasb r rim +scasw r rim +scasl r rim +scasq r rim # # Compare bytes in strings (CMPSx) # -# CMP([%str1], [%str2]) +# CMP([%1], [%2]) # # IF (DF == 0) THEN -# %str1 = %str1 + sizeof(x) -# %str2 = %str2 + sizeof(x) +# %1 = %1 + sizeof(x) +# %2 = %2 + sizeof(x) # ELSE -# %str1 = %str1 - sizeof(x) -# %str2 = %str2 - sizeof(x) +# %1 = %1 - sizeof(x) +# %2 = %2 - sizeof(x) # FI # # Sets CF, OF, ZF and SF according to the result of the comparison # -# When no parameters are given, %str1 = RDI and %str2 = RSI -# When one parameter is given, %str1 = RDI and %str2 = $1 -# When two parameters are given, %str1 = $1 and %str2 = $2 +# Moves past the compared values in any case! # -cmpsb -cmpsb r cmpsb r r -cmpsw -cmpsw r cmpsw r r -cmpsl -cmpsl r cmpsl r r -cmpsq -cmpsq r cmpsq r r # # Safe compare bytes in strings (CMPZSx) # # Behaves precisely like CMPSx, except in the following case: -# - If both [%str1] and [%str2] are zero, clears ZF (indicating NOT EQUAL) +# - If both [%1] and [%2] are zero, clears ZF (indicating NOT EQUAL) # -# This prevents 'REP.Z CMPZSx' from looping infinitely when both strings +# This prevents 'CMPZSx.REP.Z' from looping infinitely when both strings # have the exact same content; this allows for short strcmp's # -cmpzsb -cmpzsb r cmpzsb r r -cmpzsw -cmpzsw r cmpzsw r r -cmpzsl -cmpzsl r cmpzsl r r -cmpzsq -cmpzsq r cmpzsq r r # # Move value from string to string (MOVSx) # -# [%str1] = [%str2] +# [%1] = [%1] # IF (DF == 0) THEN -# %str1 = %str1 + sizeof(x) -# %str2 = %str2 + sizeof(x) +# %1 = %1 + sizeof(x) +# %2 = %2 + sizeof(x) # ELSE -# %str1 = %str1 - sizeof(x) -# %str2 = %str2 - sizeof(x) +# %1 = %1 - sizeof(x) +# %2 = %2 - sizeof(x) # FI # # Preserves CF, OF and SF # Sets ZF according to the moved value # -# When no parameters are given, %str1 = RDI and %str2 = RSI -# When one parameter is given, %str1 = RDI and %str2 = $1 -# When two parameters are given, %str1 = $1 and %str2 = $2 -# -movsb -movsb r movsb r r -movsw -movsw r movsw r r -movsl -movsl r movsl r r -movsq -movsq r movsq r r #---------------------------------------------------------------------------# diff --git a/vm/in/mov.c b/vm/in/mov.c index 1df73e6..6cae33a 100644 --- a/vm/in/mov.c +++ b/vm/in/mov.c @@ -51,6 +51,21 @@ IMPL_OUT; //----------------------------------------------------------------------------// +IMPL_START_2(ldarg) +{ + if (v2 < 8) + v1 = R(AX0 + v2); + + else if (v2 < 16) + v1 = R(LX0 + v2 - 8); + + else + _except(ctx, E_ILL, "ldarg: value out of range: %lu", v1); +} +IMPL_OUT; + +//----------------------------------------------------------------------------// + IMPL_START_1(gco) { v1 = cr1; @@ -63,4 +78,5 @@ IMPL_START_1(gcd) } IMPL_OUT; +//----------------------------------------------------------------------------// diff --git a/vm/in/string.c b/vm/in/string.c index 06ab1e7..e974de2 100644 --- a/vm/in/string.c +++ b/vm/in/string.c @@ -15,28 +15,11 @@ static void stos_impl(ctx_t *ctx, acc_t *p1, acc_t *p2, uint len) { - ulong reg, val; + DECV(v2, p2); - if (p2) { - DECV(v2, p2); - reg = p1->reg; - val = v2; - } + writemem(ctx, v2, R(p1->reg), len); - else if (p1) { - DECV(v1, p1); - reg = RDI; - val = v1; - } - - else { - reg = RDI; - val = rax; - } - - writemem(ctx, val, R(reg), len); - - STR_MOVE(reg, len); + STR_MOVE(p1->reg, len); } IMPL_START_0(stosb) @@ -67,28 +50,11 @@ IMPL_END; static void lods_impl(ctx_t *ctx, acc_t *p1, acc_t *p2, uint len) { - ulong reg1, reg2; - - if (p2) { - reg1 = p1->reg; - reg2 = p2->reg; - } - - else if (p1) { - reg1 = p1->reg; - reg2 = RSI; - } + R(p1->reg) = readmem(ctx, R(p2->reg), len); - else { - reg1 = RAX; - reg2 = RSI; - } + flg = (R(p1->reg) == 0 ? flg|ZF : flg&~ZF); - R(reg1) = readmem(ctx, R(reg2), len); - - flg = (R(reg1) == 0 ? flg|ZF : flg&~ZF); - - STR_MOVE(reg2, len); + STR_MOVE(p2->reg, len); } IMPL_START_0(lodsb) @@ -119,29 +85,18 @@ IMPL_END; static void scas_impl(ctx_t *ctx, acc_t *p1, acc_t *p2, uint len) { - ulong reg, val; + DECV(v2, p2); - if (p2) { - DECV(v2, p2); - reg = p1->reg; - val = v2; + ulong x = readmem(ctx, R(p1->reg), len); + COMPARE(x, v2); + + if (x == 0) { + flg |= ZF; } - else if (p1) { - DECV(v1, p1); - reg = RDI; - val = v1; + else if (!(flg&ZF)) { + STR_MOVE(p1->reg, len); } - - else { - reg = RDI; - val = rax; - } - - ulong x = readmem(ctx, R(reg), len); - COMPARE(x, val); - - STR_MOVE(reg, len); } IMPL_START_0(scasb) @@ -170,75 +125,15 @@ IMPL_END; //----------------------------------------------------------------------------// -static void scazs_impl(ctx_t *ctx, acc_t *p1, uint len) -{ - ulong reg; - - if (p1) - reg = p1->reg; - else - reg = RDI; - - ulong x = readmem(ctx, R(reg), len); - COMPARE(x, 0); - - if (!(flg&ZF)) { - STR_MOVE(reg, len); - } -} - -IMPL_START_0(scazsb) -{ - scazs_impl(ctx, p1, 1); -} -IMPL_END; - -IMPL_START_0(scazsw) -{ - scazs_impl(ctx, p1, 2); -} -IMPL_END; - -IMPL_START_0(scazsl) -{ - scazs_impl(ctx, p1, 4); -} -IMPL_END; - -IMPL_START_0(scazsq) -{ - scazs_impl(ctx, p1, 8); -} -IMPL_END; - -//----------------------------------------------------------------------------// - static void cmps_impl(ctx_t *ctx, acc_t *p1, acc_t *p2, uint len) { - ulong reg1, reg2; - - if (p2) { - reg1 = p1->reg; - reg2 = p2->reg; - } - - else if (p1) { - reg1 = RDI; - reg2 = p1->reg; - } - - else { - reg1 = RDI; - reg2 = RSI; - } - - ulong x1 = readmem(ctx, R(reg1), len); - ulong x2 = readmem(ctx, R(reg2), len); + ulong x1 = readmem(ctx, R(p1->reg), len); + ulong x2 = readmem(ctx, R(p2->reg), len); COMPARE(x1, x2); - STR_MOVE(reg1, len); - STR_MOVE(reg2, len); + STR_MOVE(p1->reg, len); + STR_MOVE(p2->reg, len); } IMPL_START_0(cmpsb) @@ -269,33 +164,16 @@ IMPL_END; static void cmpzs_impl(ctx_t *ctx, acc_t *p1, acc_t *p2, uint len) { - ulong reg1, reg2; - - if (p2) { - reg1 = p1->reg; - reg2 = p2->reg; - } - - else if (p1) { - reg1 = RDI; - reg2 = p1->reg; - } - - else { - reg1 = RDI; - reg2 = RSI; - } - - ulong x1 = readmem(ctx, R(reg1), len); - ulong x2 = readmem(ctx, R(reg2), len); + ulong x1 = readmem(ctx, R(p1->reg), len); + ulong x2 = readmem(ctx, R(p2->reg), len); COMPARE(x1, x2); if (!x1 && !x2) flg &= ~ZF; - STR_MOVE(reg1, len); - STR_MOVE(reg2, len); + STR_MOVE(p1->reg, len); + STR_MOVE(p2->reg, len); } IMPL_START_0(cmpzsb) @@ -326,30 +204,13 @@ IMPL_END; static void movs_impl(ctx_t *ctx, acc_t *p1, acc_t *p2, uint len) { - ulong reg1, reg2; - - if (p2) { - reg1 = p1->reg; - reg2 = p2->reg; - } - - else if (p1) { - reg1 = RDI; - reg2 = p1->reg; - } - - else { - reg1 = RDI; - reg2 = RSI; - } - - ulong x = readmem(ctx, R(reg2), len); - writemem(ctx, x, R(reg1), len); + ulong x = readmem(ctx, R(p2->reg), len); + writemem(ctx, x, R(p1->reg), len); flg = (x == 0 ? flg|ZF : flg&~ZF); - STR_MOVE(reg1, len); - STR_MOVE(reg2, len); + STR_MOVE(p1->reg, len); + STR_MOVE(p2->reg, len); } IMPL_START_0(movsb) diff --git a/vm/pc/decd.c b/vm/pc/decd.c index af9c297..e6af903 100644 --- a/vm/pc/decd.c +++ b/vm/pc/decd.c @@ -4,7 +4,7 @@ #include // -// Imperatively read the "DECD" file before reading this code +// Read the "DECD" file before reading this code // static void check_param_type(ctx_t *ctx, instr_t *in, uint prm, uchar fmt) @@ -26,113 +26,6 @@ static void check_param_type(ctx_t *ctx, instr_t *in, uint prm, uchar fmt) "fmt=0x%x prm=0x%x", in->full, fmt, prm); } -void decode(ctx_t *ctx) -{ - char *illmsg; - - instr_t *in; - - acc_t p1 = { 0 }; - acc_t p2 = { 0 }; - - bool rep = 0; - uint cond = 0; - bool lock, nomore; - - ushort w1, w2; - uchar f1 = 0, f2 = 0; - - ulong pc = rip; - - // Instruction counter - R(RX0)++; - - // - // Process the first word of the instruction - // - w1 = ctx->get(ctx); - - // Extract first word flags - - lock = !!(w1 & PREF_LOCK); - nomore = !!(w1 & PREF_NOMORE); - - w1 &= ~(PREF_LOCK|PREF_NOMORE); - - // Find instruction - - if (w1 >= NINSTRS) - { - illmsg = "No such INSTR"; - goto ill; - } - - in = &ctx->i[w1]; - - if (nomore) - goto skip_w2; - - // - // Process second word - // - - w2 = ctx->get(ctx); - - // REP and COND - rep = !!(w2 & PREF_REP); - cond = (w2 & BITS_COND) >> COND_SHIFT; - - // F1 and F2 - f1 = (w2 >> F1_SHIFT) & Fx_MASK; - f2 = w2 & Fx_MASK; - -skip_w2: - - // - // Deal with operand 1 - // - - if (in->prm1 == NOPRM) - { - if (f1 || f2) - { - illmsg = "FT1 and/or FT2 filled for 0-param INSTR"; - goto ill; - } - - exec_instr(ctx, in, NULL, NULL, lock, rep, cond, pc); - return; - } - - check_param_type(ctx, in, in->prm1, f1); - extract_param(ctx, &p1, f1); - - // - // Deal with operand 2 - // - - if (in->prm2 == NOPRM) - { - if (f2) - { - illmsg = "FT2 filled for 1-param INSTR"; - goto ill; - } - - exec_instr(ctx, in, &p1, NULL, lock, rep, cond, pc); - return; - } - - check_param_type(ctx, in, in->prm2, f2); - extract_param(ctx, &p2, f2); - - exec_instr(ctx, in, &p1, &p2, lock, rep, cond, pc); - return; - -ill: - _except(ctx, E_ILL, illmsg); -} - // // Verify that access to a certain register is legal // @@ -251,119 +144,110 @@ void extract_param(ctx_t *ctx, acc_t *p, uchar fmt) } } -static bool eval_cond(ctx_t *ctx, uint cond) +void decode(ctx_t *ctx) { - bool neg = cond & (1 << 4); - bool ok; + char *illmsg; + + instr_t *in; + + acc_t p1 = { 0 }; + acc_t p2 = { 0 }; + + bool rep = 0; + uint cond = 0; + bool lock, nomore; + + ushort w1, w2; + uchar f1 = 0, f2 = 0; + + ulong pc = rip; - cond &= ~(1 << 4); - - switch (cond) + // Instruction counter + R(RX0)++; + + // + // Process the first word of the instruction + // + w1 = ctx->get(ctx); + + // Extract first word flags + + lock = !!(w1 & PREF_LOCK); + nomore = !!(w1 & PREF_NOMORE); + + w1 &= ~(PREF_LOCK|PREF_NOMORE); + + // Find instruction + + if (w1 >= NINSTRS) { - case CD_NONE: ok = 1; break; - - case CD_C: ok = flg&CF; break; - case CD_O: ok = flg&OF; break; - case CD_Z: ok = flg&ZF; break; - case CD_S: ok = flg&SF; break; - case CD_P: ok = flg&PF; break; - - case CD_A: ok = !(flg&CF || flg&ZF); break; - case CD_AE: ok = !(flg&CF); break; - - case CD_B: ok = flg&CF; break; - case CD_BE: ok = flg&CF || flg&ZF; break; - - case CD_G: ok = !(flg&ZF) && (!(flg&SF) == !(flg&OF)); break; - case CD_GE: ok = !(flg&SF) == !(flg&OF); break; - - case CD_L: ok = !(flg&SF) != !(flg&OF); break; - case CD_LE: ok = flg&ZF || (!(flg&SF) != !(flg&OF)); break; - - case CD_CXZ: ok = !rcx; break; - - default: - _except(ctx, E_ILL, "Invalid COND value: 0x%x", (neg?cond|(1<<4):cond)); + illmsg = "No such INSTR"; + goto ill; } - - return neg ? !ok : !!ok; -} -// -// Executes an instruction -// -void exec_instr(ctx_t *ctx, - instr_t *in, - acc_t *p1, - acc_t *p2, - bool lock, - bool rep, - uint cond, - ulong pc) -{ - bool out; - ulong r1 = 0, r2 = 0; + in = &ctx->i[w1]; - // Debugging - dump_instr(ctx, in, p1, p2, lock, rep, cond, pc); + if (nomore) + goto skip_w2; // - // For REPs we evaluate the condition AFTER running the instruction, - // in a do ... while(cond) fashion + // Process second word // - if (!rep && !eval_cond(ctx, cond)) + + w2 = ctx->get(ctx); + + // REP and COND + rep = !!(w2 & PREF_REP); + cond = (w2 & BITS_COND) >> COND_SHIFT; + + // F1 and F2 + f1 = (w2 >> F1_SHIFT) & Fx_MASK; + f2 = w2 & Fx_MASK; + +skip_w2: + + // + // Deal with operand 1 + // + + if (in->prm1 == NOPRM) + { + if (f1 || f2) + { + illmsg = "FT1 and/or FT2 filled for 0-param INSTR"; + goto ill; + } + + exec_instr(ctx, in, NULL, NULL, lock, rep, cond, pc); return; + } -do_rep: + check_param_type(ctx, in, in->prm1, f1); + extract_param(ctx, &p1, f1); - out = in->func(ctx, p1, p2, &r1, &r2); + // + // Deal with operand 2 + // - if (out) + if (in->prm2 == NOPRM) { - if (p1->type == A_REG) - R(p1->reg) = r1; - - else if (p1->type == A_IMM64) - _except(ctx, E_ACC, "Trying to output to an IMM64"); - - else + if (f2) { - assert(ACC_IS_MEM(p1)); - writemem(ctx, r1, p1->addr, p1->mlen); + illmsg = "FT2 filled for 1-param INSTR"; + goto ill; } - } - - if (out == 2) - { - if (p2->type == A_REG) - R(p2->reg) = r2; - else if (p2->type == A_IMM64) - _except(ctx, E_ACC, "Trying to output to an IMM64"); - - else - { - assert(ACC_IS_MEM(p2)); - writemem(ctx, r2, p2->addr, p2->mlen); - } + exec_instr(ctx, in, &p1, NULL, lock, rep, cond, pc); + return; } - if (rep) - { - // RCX remains untouched when condition fails - if (!eval_cond(ctx, cond)) - return; + check_param_type(ctx, in, in->prm2, f2); + extract_param(ctx, &p2, f2); - if (rcx > 0) - rcx--; + exec_instr(ctx, in, &p1, &p2, lock, rep, cond, pc); + return; - if (rcx == 0) - return; - - // Show that we're REP'ing - dump_instr(ctx, in, p1, p2, lock, rep, cond, pc); - - goto do_rep; - } +ill: + _except(ctx, E_ILL, illmsg); } diff --git a/vm/pc/dump.c b/vm/pc/dump.c index 6b316bc..1b2cec0 100644 --- a/vm/pc/dump.c +++ b/vm/pc/dump.c @@ -78,8 +78,11 @@ void dump_instr(ctx_t *ctx, log("%s", cond_suffixes[cond]); } - if (!rep && cond != CD_CXZ) - log("\t\t"); + if (!rep +#if _ATT_STYLE == 1 + && cond != CD_CXZ +#endif + ) log("\t\t"); else log("\t"); diff --git a/vm/pc/exec.c b/vm/pc/exec.c new file mode 100644 index 0000000..7986f26 --- /dev/null +++ b/vm/pc/exec.c @@ -0,0 +1,121 @@ +// The OS/K Team licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include + +static bool eval_cond(ctx_t *ctx, uint cond) +{ + bool neg = cond & (1 << 4); + bool ok; + + cond &= ~(1 << 4); + + switch (cond) + { + case CD_NONE: ok = 1; break; + + case CD_C: ok = flg&CF; break; + case CD_O: ok = flg&OF; break; + case CD_Z: ok = flg&ZF; break; + case CD_S: ok = flg&SF; break; + case CD_P: ok = flg&PF; break; + + case CD_A: ok = !(flg&CF || flg&ZF); break; + case CD_AE: ok = !(flg&CF); break; + + case CD_B: ok = flg&CF; break; + case CD_BE: ok = flg&CF || flg&ZF; break; + + case CD_G: ok = !(flg&ZF) && (!(flg&SF) == !(flg&OF)); break; + case CD_GE: ok = !(flg&SF) == !(flg&OF); break; + + case CD_L: ok = !(flg&SF) != !(flg&OF); break; + case CD_LE: ok = flg&ZF || (!(flg&SF) != !(flg&OF)); break; + + case CD_CXZ: ok = !rcx; break; + + default: + _except(ctx, E_ILL, "Invalid COND value: 0x%x", (neg?cond|(1<<4):cond)); + } + + return neg ? !ok : !!ok; +} + +// +// Executes an instruction +// +void exec_instr(ctx_t *ctx, + instr_t *in, + acc_t *p1, + acc_t *p2, + bool lock, + bool rep, + uint cond, + ulong pc) +{ + bool out; + ulong r1 = 0, r2 = 0; + + // Debugging + dump_instr(ctx, in, p1, p2, lock, rep, cond, pc); + + // + // For REPs we evaluate the condition AFTER running the instruction, + // in a do ... while(cond) fashion + // + if (!rep && !eval_cond(ctx, cond)) + return; + +do_rep: + + out = in->func(ctx, p1, p2, &r1, &r2); + + if (out) + { + if (p1->type == A_REG) + R(p1->reg) = r1; + + else if (p1->type == A_IMM64) + _except(ctx, E_ACC, "Trying to output to an IMM64"); + + else + { + assert(ACC_IS_MEM(p1)); + writemem(ctx, r1, p1->addr, p1->mlen); + } + } + + if (out == 2) + { + if (p2->type == A_REG) + R(p2->reg) = r2; + + else if (p2->type == A_IMM64) + _except(ctx, E_ACC, "Trying to output to an IMM64"); + + else + { + assert(ACC_IS_MEM(p2)); + writemem(ctx, r2, p2->addr, p2->mlen); + } + } + + if (rep) + { + // RCX remains untouched when condition fails + if (!eval_cond(ctx, cond)) + return; + + if (rcx > 0) + rcx--; + + if (rcx == 0) + return; + + // Show that we're REP'ing + dump_instr(ctx, in, p1, p2, lock, rep, cond, pc); + + goto do_rep; + } +} +