1
0
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:
julianb0 2019-06-17 20:59:30 +02:00
parent dbafbdb6e8
commit 149bf5b7e0
No known key found for this signature in database
GPG Key ID: DDF8325C95299A62
16 changed files with 668 additions and 576 deletions

View File

@ -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
View File

@ -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
View 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

View File

@ -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"

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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"
;

View File

@ -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

View File

@ -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 ([%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()'
#
# 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 (DF == 0) THEN
# %str = %str + sizeof(x)
# ELSE
# %str = %str - sizeof(x)
# FI
# 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
#
# 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
#
# 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
#---------------------------------------------------------------------------#

View File

@ -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;
//----------------------------------------------------------------------------//

View File

@ -15,28 +15,11 @@
static void stos_impl(ctx_t *ctx, acc_t *p1, acc_t *p2, uint len)
{
ulong reg, val;
if (p2) {
DECV(v2, p2);
reg = p1->reg;
val = v2;
}
else if (p1) {
DECV(v1, p1);
reg = RDI;
val = v1;
}
writemem(ctx, v2, R(p1->reg), len);
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;
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)

View File

@ -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];
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);
//
// Executes an instruction
// Deal with operand 2
//
void exec_instr(ctx_t *ctx,
instr_t *in,
acc_t *p1,
acc_t *p2,
bool lock,
bool rep,
uint cond,
ulong pc)
if (in->prm2 == NOPRM)
{
bool out;
ulong r1 = 0, r2 = 0;
if (f2)
{
illmsg = "FT2 filled for 1-param INSTR";
goto ill;
}
// Debugging
dump_instr(ctx, in, p1, p2, lock, rep, cond, pc);
exec_instr(ctx, in, &p1, NULL, lock, rep, cond, pc);
return;
}
//
// For REPs we evaluate the condition AFTER running the instruction,
// in a do ... while(cond) fashion
//
if (!rep && !eval_cond(ctx, cond))
check_param_type(ctx, in, in->prm2, f2);
extract_param(ctx, &p2, f2);
exec_instr(ctx, in, &p1, &p2, lock, rep, cond, pc);
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;
}
ill:
_except(ctx, E_ILL, illmsg);
}

View File

@ -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
View 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;
}
}