2022-02-10 13:24:02 -05:00
|
|
|
function sign_extend_32_to_64
|
|
|
|
argument n
|
|
|
|
local c
|
|
|
|
c = n > 31
|
|
|
|
n |= c * 0xffffffff00000000
|
|
|
|
return n
|
|
|
|
|
2022-02-10 18:09:32 -05:00
|
|
|
; round up to nearest multiple of 8
|
|
|
|
function round_up_to_8
|
|
|
|
argument n
|
|
|
|
n += 7
|
|
|
|
return n & 0xfffffffffffffff8
|
2022-02-10 13:24:02 -05:00
|
|
|
|
2022-01-11 18:03:09 -05:00
|
|
|
; multiply two 64-bit signed numbers to a 128-bit number
|
|
|
|
function full_multiply_signed
|
|
|
|
argument a
|
|
|
|
argument b
|
|
|
|
argument p_lower
|
|
|
|
argument p_upper
|
|
|
|
local lower
|
|
|
|
local upper
|
|
|
|
|
|
|
|
lower = a * b
|
|
|
|
; mov rax, rdx
|
|
|
|
byte 0x48
|
|
|
|
byte 0x89
|
|
|
|
byte 0xd0
|
|
|
|
; mov [rbp-48] (upper), rax
|
|
|
|
byte 0x48
|
|
|
|
byte 0x89
|
|
|
|
byte 0x85
|
|
|
|
byte 0xd0
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
|
|
|
|
*8p_lower = lower
|
|
|
|
*8p_upper = upper
|
|
|
|
return
|
2022-01-12 09:45:20 -05:00
|
|
|
|
2022-01-21 23:24:18 -05:00
|
|
|
|
|
|
|
function divmod_unsigned
|
|
|
|
argument a
|
|
|
|
argument b
|
|
|
|
argument p_quotient
|
|
|
|
argument p_remainder
|
|
|
|
local q
|
|
|
|
local r
|
|
|
|
|
|
|
|
; mov rax, [rbp-16]
|
|
|
|
byte 0x48
|
|
|
|
byte 0x8b
|
|
|
|
byte 0x85
|
|
|
|
byte 0xf0
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
|
|
|
|
; mov rbx, rax
|
|
|
|
byte 0x48
|
|
|
|
byte 0x89
|
|
|
|
byte 0xc3
|
|
|
|
|
|
|
|
; mov rax, [rbp-8]
|
|
|
|
byte 0x48
|
|
|
|
byte 0x8b
|
|
|
|
byte 0x85
|
|
|
|
byte 0xf8
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
|
|
|
|
; xor edx, edx
|
|
|
|
byte 0x31
|
|
|
|
byte 0xd2
|
|
|
|
|
|
|
|
; div rbx
|
|
|
|
byte 0x48
|
|
|
|
byte 0xf7
|
|
|
|
byte 0xf3
|
|
|
|
|
|
|
|
; mov [rbp-40], rax
|
|
|
|
byte 0x48
|
|
|
|
byte 0x89
|
|
|
|
byte 0x85
|
|
|
|
byte 0xd8
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
|
|
|
|
; mov rax, rdx
|
|
|
|
byte 0x48
|
|
|
|
byte 0x89
|
|
|
|
byte 0xd0
|
|
|
|
|
|
|
|
; mov [rbp-48], rax
|
|
|
|
byte 0x48
|
|
|
|
byte 0x89
|
|
|
|
byte 0x85
|
|
|
|
byte 0xd0
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
|
|
|
|
*8p_quotient = q
|
|
|
|
*8p_remainder = r
|
|
|
|
|
|
|
|
return
|
|
|
|
|
2022-01-12 09:45:20 -05:00
|
|
|
; allows for negative shifts
|
|
|
|
function right_shift
|
|
|
|
argument x
|
|
|
|
argument n
|
|
|
|
if n < 0 goto right_shift_negative
|
|
|
|
return x > n
|
|
|
|
:right_shift_negative
|
|
|
|
n = 0 - n
|
|
|
|
return x < n
|
|
|
|
|
|
|
|
; allows for negative shifts
|
|
|
|
function left_shift
|
|
|
|
argument x
|
|
|
|
argument n
|
|
|
|
if n < 0 goto right_shift_negative
|
|
|
|
return x < n
|
|
|
|
:left_shift_negative
|
|
|
|
n = 0 - n
|
|
|
|
return x > n
|
2022-01-13 16:12:28 -05:00
|
|
|
|
|
|
|
function max_signed
|
|
|
|
argument a
|
|
|
|
argument b
|
|
|
|
if a > b goto maxs_return_a
|
|
|
|
return b
|
|
|
|
:maxs_return_a
|
|
|
|
return a
|
2022-01-11 18:03:09 -05:00
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
function file_error
|
|
|
|
argument name
|
2022-02-10 21:09:52 -05:00
|
|
|
puts(.str_file_error)
|
|
|
|
puts(name)
|
|
|
|
putc(10)
|
2022-01-07 23:32:27 -05:00
|
|
|
exit(1)
|
2022-01-26 16:40:11 -05:00
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
:str_file_error
|
|
|
|
string Error opening file:
|
|
|
|
byte 32
|
|
|
|
byte 0
|
|
|
|
|
2022-01-26 16:40:11 -05:00
|
|
|
function die
|
|
|
|
argument message
|
2022-02-10 21:09:52 -05:00
|
|
|
puts(message)
|
|
|
|
putc(10)
|
2022-01-26 16:40:11 -05:00
|
|
|
exit(1)
|
|
|
|
|
2022-01-27 18:52:39 -05:00
|
|
|
function ftruncate
|
|
|
|
argument fd
|
|
|
|
argument length
|
|
|
|
local x
|
|
|
|
x = syscall(77, fd, length)
|
|
|
|
if x != 0 goto ftruncate_failed
|
|
|
|
return
|
|
|
|
|
|
|
|
:ftruncate_failed
|
2022-02-10 21:09:52 -05:00
|
|
|
puts(.str_ftruncate_failed)
|
2022-01-27 18:52:39 -05:00
|
|
|
exit(1)
|
|
|
|
:str_ftruncate_failed
|
|
|
|
string ftruncated failed.
|
|
|
|
byte 10
|
|
|
|
byte 0
|
|
|
|
|
|
|
|
function mmap
|
|
|
|
argument addr
|
|
|
|
argument length
|
|
|
|
argument prot
|
|
|
|
argument flags
|
|
|
|
argument fd
|
|
|
|
argument offset
|
|
|
|
return syscall(9, addr, length, prot, flags, fd, offset)
|
|
|
|
|
|
|
|
function munmap
|
|
|
|
argument addr
|
|
|
|
argument length
|
|
|
|
return syscall(11, addr, length)
|
|
|
|
|
|
|
|
#define PROT_READ 1
|
|
|
|
#define PROT_WRITE 2
|
|
|
|
#define PROT_READ_WRITE 3
|
|
|
|
#define MAP_SHARED 0x01
|
|
|
|
#define MAP_PRIVATE_ANONYMOUS 0x22
|
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
function malloc
|
|
|
|
argument size
|
|
|
|
local total_size
|
|
|
|
local memory
|
|
|
|
total_size = size + 8
|
2022-01-27 18:52:39 -05:00
|
|
|
memory = mmap(0, total_size, PROT_READ_WRITE, MAP_PRIVATE_ANONYMOUS, -1, 0)
|
2022-01-07 23:32:27 -05:00
|
|
|
if memory ] 0xffffffffffff0000 goto malloc_failed
|
|
|
|
*8memory = total_size
|
|
|
|
return memory + 8
|
|
|
|
|
|
|
|
:malloc_failed
|
2022-02-10 21:09:52 -05:00
|
|
|
puts(.str_out_of_memory)
|
2022-01-07 23:32:27 -05:00
|
|
|
exit(1)
|
|
|
|
|
|
|
|
:str_out_of_memory
|
|
|
|
string Out of memory.
|
|
|
|
byte 10
|
|
|
|
byte 0
|
|
|
|
|
|
|
|
function free
|
|
|
|
argument memory
|
|
|
|
local psize
|
|
|
|
local size
|
|
|
|
psize = memory - 8
|
|
|
|
size = *8psize
|
2022-01-27 18:52:39 -05:00
|
|
|
munmap(psize, size)
|
2022-01-07 23:32:27 -05:00
|
|
|
return
|
|
|
|
|
2022-01-21 23:24:18 -05:00
|
|
|
; returns a pointer to a null-terminated string containing the
|
|
|
|
; (unsigned) number given
|
2022-01-07 23:32:27 -05:00
|
|
|
function itos
|
|
|
|
global 32 itos_string
|
|
|
|
argument x
|
|
|
|
local c
|
|
|
|
local p
|
|
|
|
p = &itos_string
|
|
|
|
p += 30
|
|
|
|
:itos_loop
|
2022-01-21 23:24:18 -05:00
|
|
|
divmod_unsigned(x, 10, &x, &c)
|
2022-01-07 23:32:27 -05:00
|
|
|
c += '0
|
|
|
|
*1p = c
|
|
|
|
if x == 0 goto itos_loop_end
|
|
|
|
p -= 1
|
|
|
|
goto itos_loop
|
|
|
|
:itos_loop_end
|
|
|
|
return p
|
|
|
|
|
2022-01-11 14:03:13 -05:00
|
|
|
; returns the number in the given base at the start of the string, advancing the string past it.
|
|
|
|
function strtoi
|
|
|
|
argument p_s
|
|
|
|
argument base
|
|
|
|
local s
|
2022-01-07 23:32:27 -05:00
|
|
|
local c
|
2022-01-11 14:03:13 -05:00
|
|
|
local n
|
2022-01-07 23:32:27 -05:00
|
|
|
n = 0
|
2022-01-11 14:03:13 -05:00
|
|
|
s = *8p_s
|
|
|
|
:strtoi_loop
|
|
|
|
c = *1s
|
|
|
|
if c < '0 goto strtoi_loop_end
|
|
|
|
if c <= '9 goto strtoi_decimal_digit
|
|
|
|
if c < 'A goto strtoi_loop_end
|
|
|
|
if c <= 'F goto strtoi_upper_hexdigit
|
|
|
|
if c < 'a goto strtoi_loop_end
|
|
|
|
if c <= 'f goto strtoi_lower_hexdigit
|
|
|
|
goto strtoi_loop_end
|
|
|
|
|
|
|
|
:strtoi_decimal_digit
|
|
|
|
c -= '0
|
|
|
|
goto strtoi_digit
|
|
|
|
:strtoi_upper_hexdigit
|
|
|
|
c += 10 - 'A
|
|
|
|
goto strtoi_digit
|
|
|
|
:strtoi_lower_hexdigit
|
|
|
|
c += 10 - 'a
|
|
|
|
goto strtoi_digit
|
|
|
|
:strtoi_digit
|
|
|
|
if c >= base goto strtoi_loop_end
|
|
|
|
n *= base
|
|
|
|
n += c
|
|
|
|
s += 1
|
|
|
|
goto strtoi_loop
|
|
|
|
|
|
|
|
:strtoi_loop_end
|
|
|
|
*8p_s = s
|
2022-01-07 23:32:27 -05:00
|
|
|
return n
|
2022-01-11 14:03:13 -05:00
|
|
|
|
|
|
|
; returns the decimal number at the start of the given string
|
|
|
|
function stoi
|
|
|
|
argument s
|
|
|
|
return strtoi(&s, 10)
|
2022-01-07 23:32:27 -05:00
|
|
|
|
|
|
|
function memchr
|
|
|
|
argument mem
|
|
|
|
argument c
|
|
|
|
local p
|
|
|
|
p = mem
|
|
|
|
:memchr_loop
|
2022-01-08 12:15:17 -05:00
|
|
|
if *1p == c goto memchr_loop_end
|
2022-01-07 23:32:27 -05:00
|
|
|
p += 1
|
|
|
|
goto memchr_loop
|
|
|
|
:memchr_loop_end
|
|
|
|
return p
|
|
|
|
|
2022-01-11 14:03:13 -05:00
|
|
|
function strchr
|
|
|
|
argument str
|
|
|
|
argument c
|
|
|
|
local p
|
|
|
|
p = str
|
|
|
|
:strchr_loop
|
|
|
|
if *1p == 0 goto return_0
|
|
|
|
if *1p == c goto strchr_loop_end
|
|
|
|
p += 1
|
|
|
|
goto strchr_loop
|
|
|
|
:strchr_loop_end
|
|
|
|
return p
|
|
|
|
|
2022-01-08 14:37:39 -05:00
|
|
|
; copy from *p_src to *p_dest until terminator is reached, setting both to point to their respective terminators
|
|
|
|
function memccpy_advance
|
|
|
|
argument p_dest
|
|
|
|
argument p_src
|
|
|
|
argument terminator
|
|
|
|
local src
|
|
|
|
local dest
|
|
|
|
local c
|
|
|
|
src = *8p_src
|
|
|
|
dest = *8p_dest
|
|
|
|
:memccpy_advance_loop
|
|
|
|
c = *1src
|
|
|
|
*1dest = c
|
|
|
|
if c == terminator goto memccpy_advance_loop_end
|
|
|
|
src += 1
|
|
|
|
dest += 1
|
|
|
|
goto memccpy_advance_loop
|
|
|
|
:memccpy_advance_loop_end
|
|
|
|
*8p_src = src
|
|
|
|
*8p_dest = dest
|
|
|
|
return
|
|
|
|
|
2022-01-09 17:26:16 -05:00
|
|
|
; copy from src to dest until terminator is reached, returning pointer to terminator in dest.
|
|
|
|
function memccpy
|
|
|
|
argument dest
|
|
|
|
argument src
|
|
|
|
argument terminator
|
|
|
|
memccpy_advance(&dest, &src, terminator)
|
|
|
|
return dest
|
|
|
|
|
2022-02-08 22:17:28 -05:00
|
|
|
; like C, but doesn't return anything
|
2022-02-02 23:02:31 -05:00
|
|
|
; also, you can copy overlapping regions as long as dest < src.
|
2022-01-09 15:56:31 -05:00
|
|
|
function memcpy
|
|
|
|
argument dest
|
|
|
|
argument src
|
|
|
|
argument n
|
|
|
|
local p
|
|
|
|
local q
|
|
|
|
p = dest
|
|
|
|
q = src
|
|
|
|
:memcpy_loop
|
|
|
|
if n == 0 goto return_0
|
|
|
|
*1p = *1q
|
|
|
|
p += 1
|
|
|
|
q += 1
|
|
|
|
n -= 1
|
|
|
|
goto memcpy_loop
|
|
|
|
|
2022-02-08 22:17:28 -05:00
|
|
|
; like C, but doesn't return anything
|
|
|
|
function memset
|
|
|
|
argument dest
|
|
|
|
argument c
|
|
|
|
argument n
|
|
|
|
:memset_loop
|
|
|
|
if n == 0 goto return_0
|
|
|
|
*1dest = c
|
|
|
|
dest += 1
|
|
|
|
n -= 1
|
|
|
|
goto memset_loop
|
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
function strlen
|
|
|
|
argument s
|
|
|
|
local p
|
|
|
|
p = s
|
|
|
|
:strlen_loop
|
2022-01-08 12:15:17 -05:00
|
|
|
if *1p == 0 goto strlen_loop_end
|
2022-01-07 23:32:27 -05:00
|
|
|
p += 1
|
|
|
|
goto strlen_loop
|
|
|
|
:strlen_loop_end
|
|
|
|
return p - s
|
|
|
|
|
2022-01-08 14:37:39 -05:00
|
|
|
; like C strcpy, but returns a pointer to the terminating null character in dest
|
2022-01-07 23:32:27 -05:00
|
|
|
function strcpy
|
|
|
|
argument dest
|
|
|
|
argument src
|
|
|
|
local p
|
|
|
|
local q
|
|
|
|
local c
|
|
|
|
p = dest
|
|
|
|
q = src
|
|
|
|
:strcpy_loop
|
|
|
|
c = *1q
|
|
|
|
*1p = c
|
|
|
|
if c == 0 goto strcpy_loop_end
|
|
|
|
p += 1
|
|
|
|
q += 1
|
|
|
|
goto strcpy_loop
|
|
|
|
:strcpy_loop_end
|
|
|
|
return p
|
2022-01-08 14:37:39 -05:00
|
|
|
|
|
|
|
function str_equals
|
|
|
|
argument a
|
|
|
|
argument b
|
|
|
|
local c
|
|
|
|
local d
|
|
|
|
:str_equals_loop
|
|
|
|
c = *1a
|
|
|
|
d = *1b
|
|
|
|
if c != d goto return_0
|
|
|
|
if c == 0 goto return_1
|
|
|
|
a += 1
|
|
|
|
b += 1
|
|
|
|
goto str_equals_loop
|
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
function str_startswith
|
|
|
|
argument s
|
|
|
|
argument prefix
|
|
|
|
local p
|
|
|
|
local q
|
|
|
|
local c1
|
|
|
|
local c2
|
|
|
|
p = s
|
|
|
|
q = prefix
|
|
|
|
:str_startswith_loop
|
|
|
|
c1 = *1p
|
|
|
|
c2 = *1q
|
|
|
|
if c2 == 0 goto return_1
|
|
|
|
if c1 != c2 goto return_0
|
|
|
|
p += 1
|
|
|
|
q += 1
|
|
|
|
goto str_startswith_loop
|
|
|
|
|
|
|
|
function fputs
|
|
|
|
argument fd
|
|
|
|
argument s
|
|
|
|
local length
|
|
|
|
length = strlen(s)
|
|
|
|
syscall(1, fd, s, length)
|
|
|
|
return
|
|
|
|
|
|
|
|
function puts
|
|
|
|
argument s
|
|
|
|
fputs(1, s)
|
|
|
|
return
|
|
|
|
|
2022-01-24 14:06:52 -05:00
|
|
|
function putsln
|
|
|
|
argument s
|
|
|
|
fputs(1, s)
|
|
|
|
fputc(1, 10)
|
|
|
|
return
|
|
|
|
|
|
|
|
|
2022-01-08 14:37:39 -05:00
|
|
|
function print_separator
|
|
|
|
fputs(1, .str_separator)
|
|
|
|
return
|
|
|
|
|
|
|
|
:str_separator
|
|
|
|
byte 10
|
|
|
|
string ------------------------------------------------
|
|
|
|
byte 10
|
|
|
|
byte 0
|
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
function fputn
|
|
|
|
argument fd
|
|
|
|
argument n
|
|
|
|
local s
|
|
|
|
s = itos(n)
|
|
|
|
fputs(fd, s)
|
|
|
|
return
|
|
|
|
|
2022-01-11 17:36:33 -05:00
|
|
|
function fputn_signed
|
|
|
|
argument fd
|
|
|
|
argument n
|
|
|
|
if n < 0 goto fputn_negative
|
2022-02-10 13:24:02 -05:00
|
|
|
fputn(fd, n)
|
|
|
|
return
|
2022-01-11 17:36:33 -05:00
|
|
|
:fputn_negative
|
|
|
|
fputc(fd, '-)
|
|
|
|
n = 0 - n
|
|
|
|
fputn(fd, n)
|
|
|
|
return
|
2022-02-10 13:24:02 -05:00
|
|
|
|
|
|
|
; like fputn_signed, but include the sign even if it's zero or positive
|
|
|
|
function fputn_with_sign
|
|
|
|
argument fd
|
|
|
|
argument n
|
|
|
|
if n < 0 goto fputn_with_sign_negative
|
|
|
|
fputc(fd, '+)
|
|
|
|
fputn(fd, n)
|
|
|
|
return
|
|
|
|
:fputn_with_sign_negative
|
|
|
|
fputc(fd, '-)
|
|
|
|
n = 0 - n
|
|
|
|
fputn(fd, n)
|
|
|
|
return
|
2022-01-11 17:36:33 -05:00
|
|
|
|
2022-01-13 16:12:28 -05:00
|
|
|
:hex_digits
|
|
|
|
string 0123456789abcdef
|
|
|
|
|
|
|
|
function fputx64
|
2022-01-11 22:43:52 -05:00
|
|
|
argument fd
|
|
|
|
argument n
|
|
|
|
local m
|
|
|
|
local x
|
|
|
|
m = 60
|
2022-01-13 16:12:28 -05:00
|
|
|
:fputx64_loop
|
2022-01-11 22:43:52 -05:00
|
|
|
x = n > m
|
|
|
|
x &= 0xf
|
|
|
|
x += .hex_digits
|
|
|
|
fputc(fd, *1x)
|
|
|
|
m -= 4
|
2022-01-13 16:12:28 -05:00
|
|
|
if m >= 0 goto fputx64_loop
|
2022-01-11 22:43:52 -05:00
|
|
|
return
|
2022-01-13 16:12:28 -05:00
|
|
|
function putx64
|
|
|
|
argument n
|
|
|
|
fputx64(1, n)
|
|
|
|
return
|
2022-01-26 18:00:47 -05:00
|
|
|
function putx64ln
|
|
|
|
argument n
|
|
|
|
fputx64(1, n)
|
|
|
|
fputc(1, 10)
|
|
|
|
return
|
2022-01-13 16:12:28 -05:00
|
|
|
|
|
|
|
function fputx32
|
|
|
|
argument fd
|
|
|
|
argument n
|
|
|
|
local m
|
|
|
|
local x
|
|
|
|
m = 28
|
|
|
|
:fputx32_loop
|
|
|
|
x = n > m
|
|
|
|
x &= 0xf
|
|
|
|
x += .hex_digits
|
|
|
|
fputc(fd, *1x)
|
|
|
|
m -= 4
|
|
|
|
if m >= 0 goto fputx32_loop
|
|
|
|
return
|
|
|
|
function putx32
|
2022-01-11 22:43:52 -05:00
|
|
|
argument n
|
2022-01-13 16:12:28 -05:00
|
|
|
fputx32(1, n)
|
2022-01-11 22:43:52 -05:00
|
|
|
return
|
2022-01-26 18:00:47 -05:00
|
|
|
function putx32ln
|
|
|
|
argument n
|
|
|
|
fputx32(1, n)
|
|
|
|
fputc(1, 10)
|
|
|
|
return
|
2022-01-11 22:43:52 -05:00
|
|
|
|
2022-01-08 18:13:48 -05:00
|
|
|
function putn
|
|
|
|
argument n
|
|
|
|
fputn(1, n)
|
|
|
|
return
|
2022-01-11 17:36:33 -05:00
|
|
|
|
|
|
|
function putn_signed
|
|
|
|
argument n
|
|
|
|
fputn_signed(1, n)
|
|
|
|
return
|
|
|
|
|
2022-02-10 13:24:02 -05:00
|
|
|
function putn_with_sign
|
|
|
|
argument n
|
|
|
|
fputn_with_sign(1, n)
|
|
|
|
return
|
|
|
|
|
2022-01-24 14:06:52 -05:00
|
|
|
function putnln
|
|
|
|
argument n
|
|
|
|
fputn(1, n)
|
|
|
|
fputc(1, 10)
|
|
|
|
return
|
|
|
|
|
|
|
|
function putnln_signed
|
|
|
|
argument n
|
|
|
|
fputn_signed(1, n)
|
|
|
|
fputc(1, 10)
|
|
|
|
return
|
|
|
|
|
2022-02-10 13:24:02 -05:00
|
|
|
function putnln_with_sign
|
|
|
|
argument n
|
|
|
|
fputn_with_sign(1, n)
|
|
|
|
fputc(1, 10)
|
|
|
|
return
|
2022-01-11 17:36:33 -05:00
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
function fputc
|
|
|
|
argument fd
|
|
|
|
argument c
|
2022-01-08 12:15:17 -05:00
|
|
|
syscall(1, fd, &c, 1)
|
2022-01-07 23:32:27 -05:00
|
|
|
return
|
|
|
|
|
|
|
|
function putc
|
|
|
|
argument c
|
|
|
|
fputc(1, c)
|
|
|
|
return
|
2022-01-24 14:06:52 -05:00
|
|
|
function putcln
|
|
|
|
argument c
|
|
|
|
fputc(1, c)
|
|
|
|
fputc(1, 10)
|
|
|
|
return
|
2022-01-07 23:32:27 -05:00
|
|
|
|
|
|
|
; returns 0 at end of file
|
|
|
|
function fgetc
|
|
|
|
argument fd
|
|
|
|
local c
|
|
|
|
c = 0
|
2022-01-08 12:15:17 -05:00
|
|
|
syscall(0, fd, &c, 1)
|
2022-01-07 23:32:27 -05:00
|
|
|
return c
|
|
|
|
|
|
|
|
; read a line from fd as a null-terminated string
|
|
|
|
; returns 0 at end of file, 1 otherwise
|
|
|
|
function fgets
|
|
|
|
argument fd
|
|
|
|
argument buf
|
|
|
|
argument size
|
|
|
|
local p
|
|
|
|
local end
|
|
|
|
local c
|
|
|
|
p = buf
|
|
|
|
end = buf + size
|
|
|
|
|
|
|
|
:fgets_loop
|
|
|
|
c = fgetc(fd)
|
|
|
|
if c == 0 goto fgets_eof
|
|
|
|
if c == 10 goto fgets_eol
|
|
|
|
*1p = c
|
|
|
|
p += 1
|
|
|
|
if p == end goto fgets_eob
|
|
|
|
goto fgets_loop
|
|
|
|
|
|
|
|
:fgets_eol ; end of line
|
|
|
|
*1p = 0
|
|
|
|
return 1
|
|
|
|
:fgets_eof ; end of file
|
|
|
|
*1p = 0
|
|
|
|
return 0
|
|
|
|
:fgets_eob ; end of buffer
|
|
|
|
p -= 1
|
|
|
|
*1p = 0
|
|
|
|
return 1
|
|
|
|
|
|
|
|
; open the given file for reading
|
|
|
|
function open_r
|
|
|
|
argument filename
|
|
|
|
local fd
|
|
|
|
fd = syscall(2, filename, 0)
|
|
|
|
if fd < 0 goto open_r_error
|
|
|
|
return fd
|
|
|
|
:open_r_error
|
|
|
|
file_error(filename)
|
|
|
|
return -1
|
|
|
|
|
|
|
|
; open the given file for writing with the given mode
|
|
|
|
function open_w
|
|
|
|
argument filename
|
|
|
|
argument mode
|
|
|
|
local fd
|
|
|
|
fd = syscall(2, filename, 0x241, mode)
|
|
|
|
if fd < 0 goto open_w_error
|
|
|
|
return fd
|
|
|
|
:open_w_error
|
|
|
|
file_error(filename)
|
|
|
|
return -1
|
2022-01-27 18:52:39 -05:00
|
|
|
|
|
|
|
; open the given file for reading and writing with the given mode
|
|
|
|
function open_rw
|
|
|
|
argument filename
|
|
|
|
argument mode
|
|
|
|
local fd
|
|
|
|
fd = syscall(2, filename, 0x242, mode)
|
|
|
|
if fd < 0 goto open_rw_error
|
|
|
|
return fd
|
|
|
|
:open_rw_error
|
|
|
|
file_error(filename)
|
|
|
|
return -1
|
2022-01-07 23:32:27 -05:00
|
|
|
|
|
|
|
function close
|
|
|
|
argument fd
|
|
|
|
syscall(3, fd)
|
|
|
|
return
|
|
|
|
|
2022-01-11 15:55:37 -05:00
|
|
|
#define SEEK_SET 0
|
|
|
|
#define SEEK_CUR 1
|
|
|
|
#define SEEK_END 2
|
|
|
|
|
|
|
|
function lseek
|
|
|
|
argument fd
|
|
|
|
argument offset
|
|
|
|
argument whence
|
|
|
|
return syscall(8, fd, offset, whence)
|
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
function isupper
|
|
|
|
argument c
|
|
|
|
if c < 'A goto return_0
|
|
|
|
if c <= 'Z goto return_1
|
|
|
|
goto return_0
|
|
|
|
|
2022-01-08 12:15:17 -05:00
|
|
|
function islower
|
|
|
|
argument c
|
|
|
|
if c < 'a goto return_0
|
|
|
|
if c <= 'z goto return_1
|
|
|
|
goto return_0
|
|
|
|
|
|
|
|
function isdigit
|
|
|
|
argument c
|
|
|
|
if c < '0 goto return_0
|
|
|
|
if c <= '9 goto return_1
|
|
|
|
goto return_0
|
|
|
|
|
2022-01-11 15:09:49 -05:00
|
|
|
function isoctdigit
|
|
|
|
argument c
|
|
|
|
if c < '0 goto return_0
|
|
|
|
if c <= '7 goto return_1
|
|
|
|
goto return_0
|
|
|
|
|
2022-01-08 12:15:17 -05:00
|
|
|
function isalpha
|
|
|
|
argument c
|
|
|
|
if c < 'A goto return_0
|
|
|
|
if c <= 'Z goto return_1
|
|
|
|
if c < 'a goto return_0
|
|
|
|
if c <= 'z goto return_1
|
|
|
|
goto return_0
|
|
|
|
|
|
|
|
; characters which can start identifiers in C
|
|
|
|
function isalpha_or_underscore
|
|
|
|
argument c
|
|
|
|
if c < 'A goto return_0
|
|
|
|
if c <= 'Z goto return_1
|
|
|
|
if c == '_ goto return_1
|
|
|
|
if c < 'a goto return_0
|
|
|
|
if c <= 'z goto return_1
|
|
|
|
goto return_0
|
|
|
|
|
|
|
|
; characters which can appear in identifiers in C
|
|
|
|
function isalnum_or_underscore
|
|
|
|
argument c
|
|
|
|
if c < '0 goto return_0
|
|
|
|
if c <= '9 goto return_1
|
|
|
|
if c < 'A goto return_0
|
|
|
|
if c <= 'Z goto return_1
|
|
|
|
if c == '_ goto return_1
|
|
|
|
if c < 'a goto return_0
|
|
|
|
if c <= 'z goto return_1
|
|
|
|
goto return_0
|
|
|
|
|
2022-01-11 14:03:13 -05:00
|
|
|
; is the given character one of:
|
|
|
|
; .0123456789
|
|
|
|
; (these are the characters which can appear at the start of a number in C)
|
|
|
|
function isdigit_or_dot
|
|
|
|
argument c
|
|
|
|
if c < '. goto return_0
|
|
|
|
if c == '. goto return_1
|
|
|
|
if c < '0 goto return_0
|
|
|
|
if c <= '9 goto return_1
|
|
|
|
goto return_0
|
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
function exit
|
|
|
|
argument status_code
|
|
|
|
syscall(0x3c, status_code)
|
|
|
|
|
2022-01-11 22:29:00 -05:00
|
|
|
; return index of leftmost bit
|
|
|
|
; error on 0
|
|
|
|
function leftmost_1bit
|
|
|
|
argument x
|
|
|
|
local i
|
|
|
|
local b
|
|
|
|
if x == 0 goto leftmost1bit_0
|
|
|
|
|
|
|
|
i = 63
|
|
|
|
:leftmost1bit_loop
|
|
|
|
b = 1 < i
|
|
|
|
b &= x
|
|
|
|
if b != 0 goto leftmost1bit_found
|
|
|
|
i -= 1
|
|
|
|
goto leftmost1bit_loop
|
|
|
|
:leftmost1bit_found
|
|
|
|
return i
|
|
|
|
:leftmost1bit_0
|
2022-02-10 21:09:52 -05:00
|
|
|
puts(.str_leftmost1bit_0)
|
2022-01-11 22:29:00 -05:00
|
|
|
exit(1)
|
|
|
|
:str_leftmost1bit_0
|
|
|
|
string 0 passed to leftmost_1bit.
|
|
|
|
byte 0
|
2022-01-26 18:00:47 -05:00
|
|
|
|
|
|
|
:empty_string
|
|
|
|
byte 0
|
|
|
|
|
2022-01-07 23:32:27 -05:00
|
|
|
:return_0
|
|
|
|
return 0
|
|
|
|
:return_1
|
|
|
|
return 1
|
2022-01-15 10:35:36 -05:00
|
|
|
:return_2
|
|
|
|
return 2
|
|
|
|
:return_3
|
|
|
|
return 3
|
|
|
|
:return_4
|
|
|
|
return 4
|
|
|
|
:return_5
|
|
|
|
return 5
|
|
|
|
:return_6
|
|
|
|
return 6
|
|
|
|
:return_7
|
|
|
|
return 7
|
|
|
|
:return_8
|
|
|
|
return 8
|
|
|
|
:return_9
|
|
|
|
return 9
|
2022-01-11 00:09:11 -05:00
|
|
|
:return_minus1
|
|
|
|
return -1
|
2022-01-13 18:13:29 -05:00
|
|
|
:return_0x10
|
|
|
|
return 0x10
|
|
|
|
:return_0x20
|
|
|
|
return 0x20
|
|
|
|
:return_0x30
|
|
|
|
return 0x30
|
|
|
|
:return_0x40
|
|
|
|
return 0x40
|
|
|
|
:return_0x50
|
|
|
|
return 0x50
|
|
|
|
:return_0x60
|
|
|
|
return 0x60
|
|
|
|
:return_0x70
|
|
|
|
return 0x70
|
|
|
|
:return_0x80
|
|
|
|
return 0x80
|
|
|
|
:return_0x90
|
|
|
|
return 0x90
|
|
|
|
:return_0xa0
|
|
|
|
return 0xa0
|
|
|
|
:return_0xb0
|
|
|
|
return 0xb0
|
|
|
|
:return_0xc0
|
|
|
|
return 0xc0
|
|
|
|
:return_0xd0
|
|
|
|
return 0xd0
|
2022-01-27 18:00:31 -05:00
|
|
|
:return_0xd8
|
|
|
|
return 0xd8
|
2022-01-13 18:13:29 -05:00
|
|
|
:return_0xe0
|
|
|
|
return 0xe0
|
|
|
|
:return_0xf0
|
|
|
|
return 0xf0
|
2022-01-27 10:57:18 -05:00
|
|
|
:return_0xffff
|
|
|
|
return 0xffff
|
2022-01-07 23:32:27 -05:00
|
|
|
|
|
|
|
function syscall
|
|
|
|
; I've done some testing, and this should be okay even if
|
|
|
|
; rbp-56 goes beyond the end of the stack.
|
|
|
|
; mov rax, [rbp-16]
|
|
|
|
byte 0x48
|
|
|
|
byte 0x8b
|
|
|
|
byte 0x85
|
|
|
|
byte 0xf0
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
; mov rdi, rax
|
|
|
|
byte 0x48
|
|
|
|
byte 0x89
|
|
|
|
byte 0xc7
|
|
|
|
|
|
|
|
; mov rax, [rbp-24]
|
|
|
|
byte 0x48
|
|
|
|
byte 0x8b
|
|
|
|
byte 0x85
|
|
|
|
byte 0xe8
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
; mov rsi, rax
|
|
|
|
byte 0x48
|
|
|
|
byte 0x89
|
|
|
|
byte 0xc6
|
|
|
|
|
|
|
|
; mov rax, [rbp-32]
|
|
|
|
byte 0x48
|
|
|
|
byte 0x8b
|
|
|
|
byte 0x85
|
|
|
|
byte 0xe0
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
; mov rdx, rax
|
|
|
|
byte 0x48
|
|
|
|
byte 0x89
|
|
|
|
byte 0xc2
|
|
|
|
|
|
|
|
; mov rax, [rbp-40]
|
|
|
|
byte 0x48
|
|
|
|
byte 0x8b
|
|
|
|
byte 0x85
|
|
|
|
byte 0xd8
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
; mov r10, rax
|
|
|
|
byte 0x49
|
|
|
|
byte 0x89
|
|
|
|
byte 0xc2
|
|
|
|
|
|
|
|
; mov rax, [rbp-48]
|
|
|
|
byte 0x48
|
|
|
|
byte 0x8b
|
|
|
|
byte 0x85
|
|
|
|
byte 0xd0
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
; mov r8, rax
|
|
|
|
byte 0x49
|
|
|
|
byte 0x89
|
|
|
|
byte 0xc0
|
|
|
|
|
|
|
|
; mov rax, [rbp-56]
|
|
|
|
byte 0x48
|
|
|
|
byte 0x8b
|
|
|
|
byte 0x85
|
|
|
|
byte 0xc8
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
; mov r9, rax
|
|
|
|
byte 0x49
|
|
|
|
byte 0x89
|
|
|
|
byte 0xc1
|
|
|
|
|
|
|
|
; mov rax, [rbp-8]
|
|
|
|
byte 0x48
|
|
|
|
byte 0x8b
|
|
|
|
byte 0x85
|
|
|
|
byte 0xf8
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
byte 0xff
|
|
|
|
|
|
|
|
; syscall
|
|
|
|
byte 0x0f
|
|
|
|
byte 0x05
|
|
|
|
|
|
|
|
return
|