mirror of
https://gitlab.os-k.eu/os-k-team/kvisc.git
synced 2023-08-25 14:05:46 +02:00
printf! :D
This commit is contained in:
parent
dbafbdb6e8
commit
149bf5b7e0
76
as/k-as.py
76
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':
|
||||
@ -460,6 +469,11 @@ def parse_instr(line):
|
||||
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':
|
||||
@ -562,37 +581,6 @@ def parse_instr(line):
|
||||
|
||||
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)
|
||||
|
38
ka/ABI
38
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,
|
||||
|
236
ka/crt/fmt/_doprnt.k
Normal file
236
ka/crt/fmt/_doprnt.k
Normal file
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]
|
@ -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
|
||||
|
@ -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
|
||||
|
1
ka/dos.k
1
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"
|
||||
|
||||
;
|
||||
|
34
ka/main.k
34
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
|
||||
|
163
vm/in/INSTRS
163
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
|
||||
|
||||
#---------------------------------------------------------------------------#
|
||||
|
16
vm/in/mov.c
16
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;
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
|
191
vm/in/string.c
191
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;
|
||||
R(p1->reg) = readmem(ctx, R(p2->reg), len);
|
||||
|
||||
if (p2) {
|
||||
reg1 = p1->reg;
|
||||
reg2 = p2->reg;
|
||||
}
|
||||
flg = (R(p1->reg) == 0 ? flg|ZF : flg&~ZF);
|
||||
|
||||
else if (p1) {
|
||||
reg1 = p1->reg;
|
||||
reg2 = RSI;
|
||||
}
|
||||
|
||||
else {
|
||||
reg1 = RAX;
|
||||
reg2 = RSI;
|
||||
}
|
||||
|
||||
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)
|
||||
|
286
vm/pc/decd.c
286
vm/pc/decd.c
@ -4,7 +4,7 @@
|
||||
#include <pc/arch.h>
|
||||
|
||||
//
|
||||
// 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;
|
||||
|
||||
cond &= ~(1 << 4);
|
||||
instr_t *in;
|
||||
|
||||
switch (cond)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
in = &ctx->i[w1];
|
||||
|
||||
//
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
|
||||
exec_instr(ctx, in, &p1, NULL, lock, rep, cond, pc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (out == 2)
|
||||
{
|
||||
if (p2->type == A_REG)
|
||||
R(p2->reg) = r2;
|
||||
check_param_type(ctx, in, in->prm2, f2);
|
||||
extract_param(ctx, &p2, f2);
|
||||
|
||||
else if (p2->type == A_IMM64)
|
||||
_except(ctx, E_ACC, "Trying to output to an IMM64");
|
||||
exec_instr(ctx, in, &p1, &p2, lock, rep, cond, pc);
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
ill:
|
||||
_except(ctx, E_ILL, illmsg);
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
121
vm/pc/exec.c
Normal file
121
vm/pc/exec.c
Normal file
@ -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 <pc/arch.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user