583 lines
9.3 KiB
Text
583 lines
9.3 KiB
Text
; add 24 + 16 = 40 to the stack pointer to put argc, argv in the right place
|
|
byte 0x48
|
|
byte 0x81
|
|
byte 0xc4
|
|
byte 40
|
|
byte 0
|
|
byte 0
|
|
byte 0
|
|
|
|
global output_fd
|
|
|
|
goto main
|
|
|
|
global defines
|
|
global defines_end
|
|
|
|
function main
|
|
argument argv2
|
|
argument argv1
|
|
argument argv0
|
|
argument argc
|
|
local input_filename
|
|
local output_filename
|
|
|
|
defines = malloc(4000000)
|
|
defines_end = defines
|
|
|
|
if argc < 3 goto default_filenames
|
|
input_filename = argv1
|
|
output_filename = argv2
|
|
goto got_filenames
|
|
:default_filenames
|
|
input_filename = .str_default_input_filename
|
|
output_filename = .str_default_output_filename
|
|
:got_filenames
|
|
|
|
output_fd = syscall(2, output_filename, 0x241, 420) ; 420 = octal 644
|
|
if output_fd >= 0 goto output_file_good
|
|
file_error(output_filename)
|
|
:output_file_good
|
|
preprocess(input_filename, output_fd)
|
|
close(output_fd)
|
|
free(defines)
|
|
exit(0)
|
|
|
|
:str_default_input_filename
|
|
string in04a
|
|
byte 0
|
|
|
|
:str_default_output_filename
|
|
string out04a
|
|
byte 0
|
|
|
|
function preprocess
|
|
argument input_filename
|
|
argument output_fd
|
|
local input_fd
|
|
global 2048 line_buf
|
|
local line
|
|
local b
|
|
local p
|
|
local c
|
|
local line_number
|
|
|
|
line_number = 0
|
|
line = &line_buf
|
|
|
|
; first, open the input file
|
|
input_fd = syscall(2, input_filename, 0)
|
|
if input_fd >= 0 goto input_file_good
|
|
file_error(input_filename)
|
|
:input_file_good
|
|
|
|
; output a line directive
|
|
fputs(output_fd, .str_line1)
|
|
fputs(output_fd, input_filename)
|
|
fputc(output_fd, 10)
|
|
|
|
:preprocess_loop
|
|
line_number += 1
|
|
b = fgets(input_fd, line, 2000)
|
|
if b == 0 goto preprocess_eof
|
|
b = str_startswith(line, .str_define)
|
|
if b != 0 goto handle_define
|
|
b = str_startswith(line, .str_include)
|
|
if b != 0 goto handle_include
|
|
|
|
; normal line (not #define or #include)
|
|
p = line
|
|
:normal_line_loop
|
|
c = *1p
|
|
if c == 0 goto normal_line_loop_end
|
|
; optimization: don't look this up if it doesn't start with an uppercase letter
|
|
b = isupper(c)
|
|
if b == 0 goto no_replacement
|
|
b = look_up_define(p)
|
|
if b == 0 goto no_replacement
|
|
; wow! a replacement!
|
|
fputs(output_fd, b)
|
|
; advance p past this identifier
|
|
:advance_loop
|
|
c = *1p
|
|
b = is_ident(c)
|
|
if b == 0 goto normal_line_loop
|
|
p += 1
|
|
goto advance_loop
|
|
:no_replacement
|
|
fputc(output_fd, c)
|
|
p += 1
|
|
goto normal_line_loop
|
|
:normal_line_loop_end
|
|
fputc(output_fd, 10)
|
|
goto preprocess_loop
|
|
|
|
:handle_define
|
|
local def
|
|
def = line + 8 ; 8 = length of "#define "
|
|
; make sure define name only consists of identifier characters
|
|
p = def
|
|
c = *1p
|
|
b = isupper(c)
|
|
if b == 0 goto bad_define
|
|
:define_check_loop
|
|
c = *1p
|
|
if c == 32 goto define_check_loop_end
|
|
b = is_ident(c)
|
|
if b == 0 goto bad_define
|
|
p += 1
|
|
goto define_check_loop
|
|
:define_check_loop_end
|
|
b = look_up_define(def)
|
|
if b != 0 goto redefinition
|
|
defines_end = strcpy(defines_end, def)
|
|
defines_end += 1
|
|
fputc(output_fd, 10) ; don't screw up line numbers
|
|
goto preprocess_loop
|
|
:bad_define
|
|
fputs(2, .str_bad_define)
|
|
fputs(2, line)
|
|
fputc(2, 10)
|
|
exit(1)
|
|
:redefinition
|
|
fputs(2, .str_redefinition)
|
|
fputs(2, line)
|
|
fputc(2, 10)
|
|
exit(1)
|
|
:handle_include
|
|
local included_filename
|
|
local n
|
|
included_filename = line + 9 ; 9 = length of "#include "
|
|
preprocess(included_filename, output_fd)
|
|
; reset filename and line number
|
|
fputs(output_fd, .str_line)
|
|
n = line_number + 1
|
|
fputn(output_fd, n)
|
|
fputc(output_fd, 32)
|
|
fputs(output_fd, input_filename)
|
|
fputc(output_fd, 10)
|
|
goto preprocess_loop
|
|
:preprocess_eof
|
|
close(input_fd)
|
|
return
|
|
|
|
:str_redefinition
|
|
string Preprocessor redefinition:
|
|
byte 32
|
|
byte 0
|
|
|
|
:str_bad_define
|
|
string Bad preprocessor definition:
|
|
byte 32
|
|
byte 0
|
|
|
|
:str_define
|
|
string #define
|
|
byte 32
|
|
byte 0
|
|
|
|
:str_include
|
|
string #include
|
|
byte 32
|
|
byte 0
|
|
|
|
:str_line
|
|
string #line
|
|
byte 32
|
|
byte 0
|
|
|
|
:str_line1
|
|
string #line
|
|
byte 32
|
|
string 1
|
|
byte 32
|
|
byte 0
|
|
|
|
; returns a pointer to the thing str should be replaced with,
|
|
; or 0 if there is no definition for str.
|
|
function look_up_define
|
|
argument str
|
|
local lookup
|
|
local p
|
|
local c
|
|
lookup = defines
|
|
:lookup_loop
|
|
c = *1lookup
|
|
if c == 0 goto lookup_not_found
|
|
c = ident_eq(str, lookup)
|
|
if c == 1 goto lookup_found
|
|
lookup = memchr(lookup, 0)
|
|
lookup += 1
|
|
goto lookup_loop
|
|
:lookup_not_found
|
|
return 0
|
|
:lookup_found
|
|
p = memchr(lookup, 32)
|
|
return p + 1 ; the character after the space following the name is the replacement
|
|
|
|
; returns 1 if the identifiers s1 and s2 are equal; 0 otherwise
|
|
function ident_eq
|
|
argument s1
|
|
argument s2
|
|
local p1
|
|
local p2
|
|
local c1
|
|
local c2
|
|
local b1
|
|
local b2
|
|
p1 = s1
|
|
p2 = s2
|
|
:ident_eq_loop
|
|
c1 = *1p1
|
|
c2 = *1p2
|
|
b1 = is_ident(c1)
|
|
b2 = is_ident(c2)
|
|
if b1 != b2 goto return_0
|
|
if b1 == 0 goto return_1
|
|
if c1 != c2 goto return_0
|
|
p1 += 1
|
|
p2 += 1
|
|
goto ident_eq_loop
|
|
|
|
function is_ident
|
|
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
|
|
goto return_0
|
|
|
|
function file_error
|
|
argument name
|
|
fputs(2, .str_file_error)
|
|
fputs(2, name)
|
|
fputc(2, 10)
|
|
exit(1)
|
|
|
|
:str_file_error
|
|
string Error opening file:
|
|
byte 32
|
|
byte 0
|
|
|
|
function malloc
|
|
argument size
|
|
local total_size
|
|
local memory
|
|
total_size = size + 8
|
|
memory = syscall(9, 0, total_size, 3, 0x22, -1, 0)
|
|
if memory ] 0xffffffffffff0000 goto malloc_failed
|
|
*8memory = total_size
|
|
return memory + 8
|
|
|
|
:malloc_failed
|
|
fputs(2, .str_out_of_memory)
|
|
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
|
|
syscall(11, psize, size)
|
|
return
|
|
|
|
; returns a pointer to a null-terminated string containing the number given
|
|
function itos
|
|
global 32 itos_string
|
|
argument x
|
|
local c
|
|
local p
|
|
p = &itos_string
|
|
p += 30
|
|
:itos_loop
|
|
c = x % 10
|
|
c += '0
|
|
*1p = c
|
|
x /= 10
|
|
if x == 0 goto itos_loop_end
|
|
p -= 1
|
|
goto itos_loop
|
|
:itos_loop_end
|
|
return p
|
|
|
|
|
|
; returns the number at the start of the given string
|
|
function stoi
|
|
argument s
|
|
local p
|
|
local n
|
|
local c
|
|
n = 0
|
|
p = s
|
|
:stoi_loop
|
|
c = *1p
|
|
if c < '0 goto stoi_loop_end
|
|
if c > '9 goto stoi_loop_end
|
|
n *= 10
|
|
n += c - '0
|
|
p += 1
|
|
goto stoi_loop
|
|
:stoi_loop_end
|
|
return n
|
|
|
|
function memchr
|
|
argument mem
|
|
argument c
|
|
local p
|
|
local a
|
|
p = mem
|
|
:memchr_loop
|
|
a = *1p
|
|
if a == c goto memchr_loop_end
|
|
p += 1
|
|
goto memchr_loop
|
|
:memchr_loop_end
|
|
return p
|
|
|
|
function strlen
|
|
argument s
|
|
local c
|
|
local p
|
|
p = s
|
|
:strlen_loop
|
|
c = *1p
|
|
if c == 0 goto strlen_loop_end
|
|
p += 1
|
|
goto strlen_loop
|
|
:strlen_loop_end
|
|
return p - s
|
|
|
|
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
|
|
|
|
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
|
|
|
|
function fputn
|
|
argument fd
|
|
argument n
|
|
local s
|
|
s = itos(n)
|
|
fputs(fd, s)
|
|
return
|
|
|
|
function fputc
|
|
argument fd
|
|
argument c
|
|
local p
|
|
p = &c
|
|
syscall(1, fd, p, 1)
|
|
return
|
|
|
|
function putc
|
|
argument c
|
|
fputc(1, c)
|
|
return
|
|
|
|
; returns 0 at end of file
|
|
function fgetc
|
|
argument fd
|
|
local c
|
|
local p
|
|
c = 0
|
|
p = &c
|
|
syscall(0, fd, p, 1)
|
|
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
|
|
|
|
function close
|
|
argument fd
|
|
syscall(3, fd)
|
|
return
|
|
|
|
function isupper
|
|
argument c
|
|
if c < 'A goto return_0
|
|
if c <= 'Z goto return_1
|
|
goto return_0
|
|
|
|
function exit
|
|
argument status_code
|
|
syscall(0x3c, status_code)
|
|
|
|
:return_0
|
|
return 0
|
|
:return_1
|
|
return 1
|
|
|
|
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
|