lang-bootstrap/04b/in03

1767 lines
21 KiB
Text
Raw Normal View History

2021-11-19 09:52:27 -05:00
; initialize global_variables_end
C=:global_variables_end
D=:global_variables
8C=D
; initialize static_memory_end
C=:static_memory_end
2022-01-06 11:57:55 -05:00
; 0x40000 = 256KB for code
D=x440000
2021-11-20 11:38:34 -05:00
8C=D
; initialize labels_end
C=:labels_end
D=:labels
8C=D
2021-11-19 09:52:27 -05:00
; open input file
2022-01-05 23:44:04 -05:00
J=:input_filename
2021-11-19 09:52:27 -05:00
I=d0
syscall x2
J=A
?J<0:input_file_error
; open output file
2022-01-05 23:44:04 -05:00
J=:output_filename
2021-11-19 09:52:27 -05:00
I=x241
D=x1ed
syscall x2
J=A
?J<0:output_file_error
:second_pass_starting_point
2021-11-19 23:27:08 -05:00
; write ELF header
J=d4
I=:ELF_header
D=x78
syscall x1
2021-11-19 09:52:27 -05:00
:read_line
; increment line number
D=:line_number
C=8D
C+=d1
8D=C
; use rbp to store line pointer
R=:line
:read_line_loop
; read 1 byte into rbp
J=d3
I=R
D=d1
syscall x0
D=A
?D=0:eof
; check if the character was a newline:
C=1R
D=xa
?C=D:read_line_loop_end
; check if the character was a tab:
D=x9
; if so, don't increment rbp
2021-11-19 23:27:08 -05:00
?C=D:read_line_loop
2021-11-19 09:52:27 -05:00
; check if the character was a semicolon:
D=';
; if so, it's a comment
?C=D:handle_comment
R+=d1
!:read_line_loop
:handle_comment
; read out rest of line from file
J=d3
I=R
D=d1
syscall x0
D=A
?D=0:eof
C=1R
D=xa
; if we didn't reach the end of the line, keep going
?C!D:handle_comment
!:read_line_loop_end
:read_line_loop_end
; remove whitespace (specifically, ' ' characters) at end of line
I=R
:remove_terminal_whitespace_loop
I-=d1
C=1I
D=x20
?C!D:remove_terminal_whitespace_loop_end
; replace ' ' with a newline
D=xa
1I=D
!:remove_terminal_whitespace_loop
:remove_terminal_whitespace_loop_end
; check if this is a blank line
C=:line
D=1C
C=xa
?C=D:read_line
2021-11-20 11:38:34 -05:00
C=':
?C=D:handle_label_definition
2021-11-19 09:52:27 -05:00
I=:line
J=:"global"
C=x20
call :string=
D=A
?D!0:handle_global
2021-11-19 23:27:08 -05:00
I=:line
J=:"local"
C=x20
call :string=
D=A
?D!0:handle_local
2022-01-05 23:44:04 -05:00
; arguments are treated the same as local variables
2021-11-19 23:27:08 -05:00
I=:line
J=:"argument"
C=x20
call :string=
D=A
?D!0:handle_local
I=:line
J=:"return"
C=x20
call :string=
D=A
?D!0:handle_return
2022-01-06 13:53:52 -05:00
I=:line
J=:"byte"
C=x20
call :string=
D=A
?D!0:handle_byte
2022-01-06 14:02:30 -05:00
I=:line
J=:"string"
C=x20
call :string=
D=A
?D!0:handle_string
2022-01-05 23:44:04 -05:00
; set delimiter to newline
2021-11-19 23:27:08 -05:00
C=xa
2022-01-05 23:44:04 -05:00
2021-11-19 23:27:08 -05:00
I=:line
J=:"function"
call :string=
D=A
?D!0:handle_function
2022-01-06 13:53:52 -05:00
I=:line
J=:"return\n"
call :string=
D=A
?D!0:handle_return
2022-01-06 13:13:12 -05:00
; check if this is a function call (where we discard the return value)
I=:line
; (check for an opening bracket not preceded by a space)
:call_check_loop
C=1I
D=x20
?C=D:call_check_loop_end
D=xa
?C=D:call_check_loop_end
D='(
?C=D:handle_call
I+=d1
!:call_check_loop
:call_check_loop_end
2021-11-19 09:52:27 -05:00
!:read_line
:eof
C=:second_pass
D=1C
?D!0:exit_success
; set 2nd pass to 1
1C=d1
2022-01-06 11:57:55 -05:00
; make sure output file is large enough for static memory
; we'll use the ftruncate syscall to set the size of the file
J=d4
I=:static_memory_end
I=8I
syscall x4d
; seek both files back to start
J=d3
I=d0
D=d0
syscall x8
J=d4
I=d0
D=d0
syscall x8
2022-01-06 13:13:12 -05:00
; set line number to 0
C=:line_number
8C=0
!:second_pass_starting_point
:exit_success
2021-11-19 09:52:27 -05:00
J=d0
syscall x3c
2021-11-20 12:47:26 -05:00
align
2022-01-05 23:44:04 -05:00
:local_variable_name
2021-11-20 12:47:26 -05:00
reserve d8
2022-01-06 13:13:12 -05:00
2022-01-06 13:53:52 -05:00
:handle_byte
I=:line
; 5 = length of "byte "
I+=d5
call :read_number
; make sure byte is 0-255
C=A
D=xff
?CaD:bad_byte
; write byte
I=:byte
1I=C
J=d4
D=d1
syscall x1
!:read_line
:byte
reserve d1
2022-01-06 14:02:30 -05:00
:handle_string
I=:line
; 7 = length of "string "
I+=d7
J=I
; find end of string
:string_loop
C=1J
D=xa
?C=D:string_loop_end
J+=d1
!:string_loop
:string_loop_end
; get length of string
D=J
D-=I
; output fd
J=d4
syscall x1
!:read_line
2022-01-06 13:13:12 -05:00
:handle_call
J=I
; just use the rvalue function call code
C=:rvalue
D=:line
8C=D
I=:line
call :rvalue_function_call
!:read_line
2021-11-20 12:47:26 -05:00
2021-11-19 23:27:08 -05:00
:handle_local
R=I
; emit sub rsp, 8
J=d4
I=:sub_rsp_8
D=d7
syscall x1
I=R
; skip ' '
I+=d1
2021-11-20 12:47:26 -05:00
2022-01-05 23:44:04 -05:00
; store away pointer to variable name
C=:local_variable_name
2021-11-20 12:47:26 -05:00
8C=I
2022-01-05 23:44:04 -05:00
; check if already defined
2021-11-20 12:47:26 -05:00
J=:local_variables
call :ident_lookup
C=A
?C!0:local_redeclaration
2022-01-06 11:57:55 -05:00
2022-01-05 23:44:04 -05:00
C=:local_variable_name
2021-11-20 12:47:26 -05:00
I=8C
2021-11-19 23:27:08 -05:00
J=:local_variables_end
J=8J
call :ident_copy
2022-01-06 11:57:55 -05:00
2021-11-28 10:28:25 -05:00
; increase stack_end, store it in J
C=:stack_end
D=4C
D+=d8
4C=D
4J=D
J+=d4
2021-11-19 23:27:08 -05:00
; store null terminator
1J=0
2022-01-06 11:57:55 -05:00
2021-11-19 23:27:08 -05:00
; update :local_variables_end
I=:local_variables_end
8I=J
; read the next line
!:read_line
:sub_rsp_8
x48
x81
xec
x08
x00
x00
x00
2021-11-20 12:47:26 -05:00
align
:global_start
reserve d8
2022-01-05 23:44:04 -05:00
:global_variable_name
reserve d8
2021-11-19 09:52:27 -05:00
:handle_global
; ignore if this is the second pass
C=:second_pass
C=1C
?C!0:read_line
2021-11-19 23:27:08 -05:00
; skip ' '
I+=d1
2021-11-20 12:47:26 -05:00
2022-01-05 23:44:04 -05:00
; store away pointer to variable name
C=:global_variable_name
2021-11-20 12:47:26 -05:00
8C=I
2022-01-05 18:19:28 -05:00
2022-01-05 23:44:04 -05:00
; check if already defined
J=:global_variables
2021-11-20 12:47:26 -05:00
call :ident_lookup
C=A
?C!0:global_redeclaration
2022-01-05 23:44:04 -05:00
C=:global_variable_name
I=8C
2021-11-20 12:47:26 -05:00
2021-11-19 09:52:27 -05:00
J=:global_variables_end
J=8J
call :ident_copy
; store address
D=:static_memory_end
2022-01-05 23:44:04 -05:00
C=4D
4J=C
J+=d4
2022-01-05 23:44:04 -05:00
; increase static_memory_end
2021-11-19 09:52:27 -05:00
C+=d8
2022-01-05 23:44:04 -05:00
4D=C
; store null terminator
1J=0
2021-11-19 09:52:27 -05:00
; update :global_variables_end
I=:global_variables_end
8I=J
; go read the next line
!:read_line
2021-11-19 23:27:08 -05:00
:handle_function
; emit prologue
2021-11-19 23:27:08 -05:00
J=d4
I=:function_prologue
D=d14
2021-11-19 23:27:08 -05:00
syscall x1
; reset local variable table
D=:local_variables
1D=0
C=:local_variables_end
8C=D
2021-11-28 10:28:25 -05:00
; reset stack_end
D=:stack_end
2022-01-05 23:44:04 -05:00
4D=0
2021-11-28 10:28:25 -05:00
2021-11-19 23:27:08 -05:00
; go read the next line
!:read_line
:function_prologue
; sub rsp, 8
x48
x81
xec
x08
x00
x00
x00
; mov [rsp], rbp
x48
x89
x2c
x24
; mov rbp, rsp
2021-11-19 23:27:08 -05:00
R=S
; total length: 7 + 4 + 3 = 14 bytes
:function_epilogue
; mov rsp, rbp
S=R
; mov rbp, [rsp]
x48
x8b
x2c
x24
; add rsp, 8
x48
x81
xc4
x08
x00
x00
x00
; ret
return
; total length = 15 bytes
2021-11-19 23:27:08 -05:00
2021-11-20 11:38:34 -05:00
:handle_label_definition
; ignore if this is the second pass
C=:second_pass
C=1C
?C!0:read_line
2021-11-20 11:38:34 -05:00
; make sure label only has identifier characters
I=:line
I+=d1
:label_checking_loop
C=1I
D=xa
?C=D:label_checking_loop_end
I+=d1
B=C
call :isident
D=A
?D!0:label_checking_loop
!:bad_label
:label_checking_loop_end
2021-11-20 12:47:26 -05:00
I=:line
I+=d1
2022-01-06 11:57:55 -05:00
J=:labels
2021-11-20 12:47:26 -05:00
call :ident_lookup
C=A
?C!0:label_redefinition
2021-11-20 11:38:34 -05:00
J=:labels_end
J=8J
I=:line
I+=d1
call :ident_copy
R=J
2022-01-05 23:44:04 -05:00
; figure out where in the file we are
2021-11-20 11:38:34 -05:00
J=d4
I=d0
D=d1
syscall x8
C=A
C+=x400000
J=R
2022-01-05 23:44:04 -05:00
; store address
2021-11-20 11:38:34 -05:00
4J=C
J+=d4
; update labels_end
C=:labels_end
8C=J
; read the next line
!:read_line
2021-11-19 23:27:08 -05:00
:handle_return
2021-11-28 10:28:25 -05:00
I=:line
2022-01-06 13:53:52 -05:00
; skip "return"
I+=d6
C=1I
D=xa
?C=D:no_return_value
; skip ' ' after return
I+=d1
2021-11-28 10:28:25 -05:00
2022-01-06 11:57:55 -05:00
call :set_rax_to_rvalue
2021-11-19 23:27:08 -05:00
2022-01-06 13:53:52 -05:00
:no_return_value
2021-11-19 23:27:08 -05:00
J=d4
I=:function_epilogue
D=d15
2021-11-19 23:27:08 -05:00
syscall x1
2021-11-28 10:28:25 -05:00
; go read the next line
2021-11-19 23:27:08 -05:00
!:read_line
:mov_rsp_rbp
S=R
:ret
return
2021-11-19 09:52:27 -05:00
; copy the newline-terminated identifier from rsi to rdi
:ident_copy
C=1I
B=C
call :isident
D=A
?D=0:bad_identifier
:ident_loop
C=1I
2021-11-20 11:38:34 -05:00
1J=C
I+=d1
J+=d1
2021-11-19 09:52:27 -05:00
D=xa
?C=D:ident_loop_end
B=C
call :isident
D=A
?D=0:bad_identifier
!:ident_loop
:ident_loop_end
return
2021-11-20 12:47:26 -05:00
align
:ident_lookup_i
reserve d8
2022-01-05 23:44:04 -05:00
; look up identifier rsi in list rdi
2021-11-20 12:47:26 -05:00
; returns address of whatever's right after the identifier in the list, or 0 if not found
:ident_lookup
C=:ident_lookup_i
8C=I
:ident_lookup_loop
2022-01-05 18:19:28 -05:00
; check if reached the end of the table
C=1J
?C=0:return_0
2021-11-20 12:47:26 -05:00
I=:ident_lookup_i
I=8I
call :ident=
C=A
2021-11-28 10:28:25 -05:00
; move past terminator of identifier in table
:ident_finish_loop
D=1J
J+=d1
A=xa
?D!A:ident_finish_loop
; check if this was it
2021-11-20 12:47:26 -05:00
?C!0:return_J
2021-11-28 10:28:25 -05:00
; nope. keep going
2022-01-05 23:44:04 -05:00
; skip over address:
J+=d4
2021-11-20 12:47:26 -05:00
!:ident_lookup_loop
2021-11-19 09:52:27 -05:00
; can the character in rbx appear in an identifier?
:isident
A='0
?B<A:return_0
; note: 58 = '9' + 1
A=d58
?B<A:return_1
A='A
?B<A:return_0
; note: 91 = 'z' + 1
A=d91
?B<A:return_1
A='z
?B>A:return_0
; 96 = 'a' - 1
A=d96
?B>A:return_1
A='_
?B=A:return_1
!:return_0
2022-01-06 11:57:55 -05:00
; set <rax> to the term in rsi
:set_rax_to_term
R=I
C=1I
D=''
2022-01-06 13:53:52 -05:00
?C=D:term_number
2022-01-06 11:57:55 -05:00
D='.
?C=D:term_label
D=d58
?C<D:term_number
2022-01-06 11:57:55 -05:00
; (fallthrough)
; set <rax> to the variable in rsi
:set_rax_to_variable
; variable
2022-01-06 11:57:55 -05:00
call :set_rax_to_address_of_variable
call :set_rbx_to_rax
call :set_rax_to_[rbx]
return
:term_label
C=:second_pass
C=1C
; skip looking up label on first pass; just use whatever's in rsi
?C=0:set_rax_to_immediate
; move past .
I+=d1
J=:labels
call :ident_lookup
C=A
?C=0:bad_label
; set rax to label value
I=4C
!:set_rax_to_immediate
align
:rvalue
reserve d8
; set <rax> to the rvalue in rsi
:set_rax_to_rvalue
; store pointer to rvalue
C=:rvalue
8C=I
C=1I
D='&
?C=D:rvalue_addressof
D='~
?C=D:rvalue_bitwise_not
D='*
?C=D:rvalue_dereference
J=I
:rvalue_loop
C=1J
D='(
2022-01-06 13:13:12 -05:00
?C=D:rvalue_function_call
2022-01-06 11:57:55 -05:00
D=x20
?C=D:rvalue_binary_op
D=xa
; no space or opening bracket; this must be a term
?C=D:set_rax_to_term
J+=d1
!:rvalue_loop
2022-01-06 13:13:12 -05:00
align
:rvalue_function_arg
reserve d8
:rvalue_function_arg_offset
reserve d4
:rvalue_function_call
I=J
I+=d1
C=1I
D=')
?C=D:function_call_no_arguments
C=:rvalue_function_arg_offset
; set arg offset to -16 (to skip over stack space for return address and rbp)
D=xfffffffffffffff0
4C=D
:rvalue_function_loop
C=:rvalue_function_arg
8C=I
; set <rax> to argument
call :set_rax_to_term
; set <[rsp-arg_offset]> to rax
; first, output prefix
J=d4
I=:mov_[rsp_offset]_rax_prefix
D=d4
syscall x1
; now decrement offset, and output it
I=:rvalue_function_arg_offset
C=4I
C-=d8
4I=C
J=d4
D=d4
syscall x1
C=:rvalue_function_arg
I=8C
; skip over argument
:rvalue_function_arg_loop
C=1I
D=',
?C=D:rvalue_function_next_arg
D=')
?C=D:rvalue_function_loop_end
D=xa
; no closing bracket
?C=D:bad_call
I+=d1
!:rvalue_function_arg_loop
:rvalue_function_next_arg
; skip comma
I+=d1
C=1I
D=x20
; make sure there's a space after the comma
?C!D:bad_call
; skip space
I+=d1
; handle the next argument
!:rvalue_function_loop
:rvalue_function_loop_end
:function_call_no_arguments
I+=d1
C=1I
D=xa
; make sure there's nothing after the closing bracket
?C!D:bad_term
C=:second_pass
C=1C
?C=0:ignore_function_address
; look up function name
I=:rvalue
I=8I
J=:labels
call :ident_lookup
C=A
?C=0:bad_function
; read address
I=4C
:ignore_function_address
call :set_rax_to_immediate
; write call rax
J=d4
I=:call_rax
D=d2
syscall x1
; we're done!
return
2022-01-06 11:57:55 -05:00
2022-01-06 13:13:12 -05:00
:mov_[rsp_offset]_rax_prefix
x48
x89
x84
x24
:call_rax
xff
xd0
2022-01-06 11:57:55 -05:00
:binary_op
reserve d1
:rvalue_binary_op
; move past ' '
J+=d1
; store binary op
D=1J
C=:binary_op
1C=D
; make sure space follows operator
J+=d1
C=1J
D=x20
?C!D:bad_term
; set rsi to second operand
J+=d1
I=J
call :set_rax_to_term
call :set_rsi_to_rax
; now set rax to first operand
I=:rvalue
I=8I
call :set_rax_to_term
; and combine
C=:binary_op
C=1C
D='+
?C=D:rvalue_add
D='-
?C=D:rvalue_sub
D='*
?C=D:rvalue_mul
D='/
?C=D:rvalue_div
D='%
?C=D:rvalue_rem
D='&
?C=D:rvalue_and
D='|
?C=D:rvalue_or
D='^
?C=D:rvalue_xor
D='<
?C=D:rvalue_shl
D='>
?C=D:rvalue_shr
!:bad_term
:rvalue_add
call :set_rbx_to_rsi
J=d4
I=:add_rax_rbx
D=d3
syscall x1
return
:add_rax_rbx
x48
x01
xd8
:rvalue_sub
call :set_rbx_to_rsi
J=d4
I=:sub_rax_rbx
D=d3
syscall x1
return
:sub_rax_rbx
x48
x29
xd8
:rvalue_mul
call :set_rbx_to_rsi
J=d4
I=:imul_rbx
D=d3
syscall x1
return
:imul_rbx
x48
xf7
xeb
:rvalue_div
call :set_rbx_to_rsi
call :zero_rdx
J=d4
I=:idiv_rbx
D=d3
syscall x1
return
:idiv_rbx
x48
xf7
xfb
:rvalue_rem
call :set_rbx_to_rsi
call :zero_rdx
J=d4
I=:idiv_rbx
D=d3
syscall x1
call :set_rax_to_rdx
return
:rvalue_and
call :set_rbx_to_rsi
J=d4
I=:and_rax_rbx
D=d3
syscall x1
return
:and_rax_rbx
x48
x21
xd8
:rvalue_or
call :set_rbx_to_rsi
J=d4
I=:or_rax_rbx
D=d3
syscall x1
return
:or_rax_rbx
x48
x09
xd8
:rvalue_xor
call :set_rbx_to_rsi
J=d4
I=:xor_rax_rbx
D=d3
syscall x1
return
:xor_rax_rbx
x48
x31
xd8
:rvalue_shl
call :set_rcx_to_rsi
J=d4
I=:shl_rax_cl
D=d3
syscall x1
return
:shl_rax_cl
x48
xd3
xe0
:rvalue_shr
call :set_rcx_to_rsi
J=d4
I=:shr_rax_cl
D=d3
syscall x1
return
:shr_rax_cl
x48
xd3
xe8
:rvalue_addressof
I+=d1
!:set_rax_to_address_of_variable
:rvalue_bitwise_not
I+=d1
call :set_rax_to_term
J=d4
I=:not_rax
D=d3
syscall x1
return
:not_rax
x48
xf7
xd0
:rvalue_dereference_size
reserve d1
:rvalue_dereference
I+=d1
D=1I
C=:rvalue_dereference_size
1C=D
I+=d1
call :set_rax_to_variable
call :set_rbx_to_rax
call :zero_rax
C=:rvalue_dereference_size
C=1C
D='1
?C=D:set_al_to_[rbx]
D='2
?C=D:set_ax_to_[rbx]
D='4
?C=D:set_eax_to_[rbx]
D='8
?C=D:set_rax_to_[rbx]
!:bad_term
; set <rax> to address of variable in rsi
:set_rax_to_address_of_variable
J=:local_variables
call :ident_lookup
C=A
2022-01-06 11:57:55 -05:00
?C=0:try_global
; it's a local variable
2022-01-06 11:57:55 -05:00
; read the offset from <rbp>
D=4C
2022-01-06 11:57:55 -05:00
; put negated offset in rbp
R=d0
R-=D
2022-01-06 11:57:55 -05:00
; lea rax, [rbp+
J=d4
2022-01-06 11:57:55 -05:00
I=:lea_rax_rbp_offset_prefix
D=d3
syscall x1
2022-01-06 11:57:55 -05:00
; offset]
J=d4
2022-01-06 11:57:55 -05:00
I=:imm64
4I=R
D=d4
syscall x1
2022-01-05 23:44:04 -05:00
return
2022-01-06 11:57:55 -05:00
:try_global
J=:global_variables
call :ident_lookup
C=A
?C=0:bad_variable
; it's a global variable
; get its address
C=4C
2022-01-06 11:57:55 -05:00
; put address in rax
I=C
!:set_rax_to_immediate
2022-01-06 13:53:52 -05:00
2022-01-05 23:44:04 -05:00
:number_is_negative
reserve d1
:term_number
2022-01-06 11:57:55 -05:00
call :read_number
I=A
!:set_rax_to_immediate
; set rax to the number in the string at rsi
:read_number
2022-01-05 23:44:04 -05:00
C=1I
2022-01-06 13:53:52 -05:00
D=''
?C=D:read_char
2022-01-05 23:44:04 -05:00
D='-
; set rdx to 0 if number is positive, 1 if negative
2022-01-06 11:57:55 -05:00
?C=D:read_number_negative
2022-01-05 23:44:04 -05:00
D=d0
2022-01-06 11:57:55 -05:00
!:read_number_cont
:read_number_negative
2022-01-05 23:44:04 -05:00
D=d1
I+=d1
2022-01-06 11:57:55 -05:00
:read_number_cont
2022-01-05 23:44:04 -05:00
; store away negativity
C=:number_is_negative
1C=D
2022-01-06 13:53:52 -05:00
; check if number starts with 0-9
2022-01-05 23:44:04 -05:00
C=1I
2022-01-06 13:13:12 -05:00
D='9
?C>D:bad_number
2022-01-05 23:44:04 -05:00
D='0
2022-01-06 13:13:12 -05:00
?C<D:bad_number
?C=D:number_starting_with0
2022-01-05 23:44:04 -05:00
; it's a decimal number
; rbp will store the number
R=d0
:decimal_number_loop
C=1I
D='9
?C>D:decimal_number_loop_end
D='0
?C<D:decimal_number_loop_end
C-=D
; multiply by 10
B=d10
A=R
mul
R=A
; add this digit
R+=C
I+=d1
!:decimal_number_loop
:decimal_number_loop_end
2022-01-06 11:57:55 -05:00
!:read_number_output
2022-01-06 13:13:12 -05:00
2022-01-06 13:53:52 -05:00
:read_char
I+=d1
R=1I
I+=d1
!:read_number_output
2022-01-06 13:13:12 -05:00
:number_starting_with0
2022-01-05 23:44:04 -05:00
I+=d1
2022-01-06 13:13:12 -05:00
C=1I
2022-01-05 23:44:04 -05:00
D='x
2022-01-06 13:13:12 -05:00
?C=D:read_hex_number
; otherwise, it should just be 0
R=d0
2022-01-06 13:53:52 -05:00
!:read_number_output
2022-01-06 13:13:12 -05:00
:read_hex_number
2022-01-05 23:44:04 -05:00
I+=d1
; rbp will store the number
R=d0
:hex_number_loop
C=1I
D='0
?C<D:hex_number_loop_end
D=d58
?C<D:hex_number_0123456789
D='a
?C<D:hex_number_loop_end
D='f
?C>D:hex_number_loop_end
; one of the digits a-f
D=xffffffffffffffa9
!:hex_number_digit
:hex_number_0123456789
D=xffffffffffffffd0
:hex_number_digit
C+=D
; shift left by 4
R<=d4
; add digit
R+=C
I+=d1
!:hex_number_loop
:hex_number_loop_end
2022-01-06 11:57:55 -05:00
!:read_number_output
2022-01-05 23:44:04 -05:00
2022-01-06 11:57:55 -05:00
:read_number_output
2022-01-06 13:13:12 -05:00
; first, make sure number is followed by space/newline/appropriate punctuation
2022-01-06 11:57:55 -05:00
C=1I
D=x20
?C=D:read_number_valid
2022-01-06 13:13:12 -05:00
D=',
?C=D:read_number_valid
D=')
?C=D:read_number_valid
2022-01-06 11:57:55 -05:00
D=xa
?C=D:read_number_valid
!:bad_number
:read_number_valid
; we now have the *unsigned* number in rbp. take the sign into consideration
2022-01-05 23:44:04 -05:00
C=:number_is_negative
D=1C
?D=0:number_not_negative
; R = -R
C=R
R=d0
R-=C
:number_not_negative
2022-01-06 11:57:55 -05:00
; finally, return
A=R
return
2022-01-05 23:44:04 -05:00
; set <rax> to the immediate in rsi.
:set_rax_to_immediate
C=:imm64
8C=I
; write prefix
J=d4
D=d2
I=:mov_rax_imm64_prefix
syscall x1
; write immediate
J=d4
D=d8
I=:imm64
syscall x1
return
2022-01-06 11:57:55 -05:00
:zero_rax
J=d4
I=:xor_eax_eax
D=d2
syscall x1
return
:xor_eax_eax
x31
xc0
:zero_rdx
J=d4
I=:xor_edx_edx
D=d2
syscall x1
return
:xor_edx_edx
x31
xd2
:set_rbx_to_rax
J=d4
I=:mov_rbx_rax
D=d3
syscall x1
return
:mov_rbx_rax
B=A
:set_rbx_to_rsi
J=d4
I=:mov_rbx_rsi
D=d3
syscall x1
return
:mov_rbx_rsi
B=I
:set_rcx_to_rsi
J=d4
I=:mov_rcx_rsi
D=d3
syscall x1
return
:mov_rcx_rsi
C=I
:set_rax_to_rdx
J=d4
I=:mov_rax_rdx
D=d3
syscall x1
return
:mov_rax_rdx
A=D
2022-01-06 11:57:55 -05:00
:set_rsi_to_rax
J=d4
I=:mov_rsi_rax
D=d3
syscall x1
return
:mov_rsi_rax
I=A
2022-01-06 11:57:55 -05:00
:set_rax_to_[rbx]
J=d4
I=:mov_rax_[rbx]
D=d3
syscall x1
return
:mov_rax_[rbx]
x48
x8b
x03
:set_eax_to_[rbx]
J=d4
I=:mov_eax_[rbx]
D=d2
syscall x1
return
:mov_eax_[rbx]
x8b
x03
:set_ax_to_[rbx]
J=d4
I=:mov_ax_[rbx]
D=d3
syscall x1
return
:mov_ax_[rbx]
x66
x8b
x03
:set_al_to_[rbx]
J=d4
I=:mov_al_[rbx]
D=d2
syscall x1
return
:mov_al_[rbx]
x8a
x03
2022-01-05 23:44:04 -05:00
:mov_rax_imm64_prefix
x48
xb8
align
:imm64
reserve d8
2022-01-06 11:57:55 -05:00
; prefix for lea rax, [rbp+IMM32]
:lea_rax_rbp_offset_prefix
x48
2022-01-06 11:57:55 -05:00
x8d
2022-01-05 23:44:04 -05:00
x85
2021-11-19 09:52:27 -05:00
2022-01-05 23:44:04 -05:00
:input_filename
str in04b
x0
2021-11-19 09:52:27 -05:00
2022-01-05 23:44:04 -05:00
:output_filename
str out04b
2021-11-19 09:52:27 -05:00
x0
:input_file_error
B=:input_file_error_message
!:general_error
:input_file_error_message
str Couldn't open input file.
xa
x0
:output_file_error
B=:output_file_error_message
!:general_error
:output_file_error_message
str Couldn't open output file.
xa
x0
:bad_identifier
B=:bad_identifier_error_message
!:program_error
:bad_identifier_error_message
str Bad identifier.
xa
x0
2021-11-20 11:38:34 -05:00
:bad_label
B=:bad_label_error_message
!:program_error
:bad_label_error_message
str Bad label.
xa
x0
2021-11-19 09:52:27 -05:00
2022-01-06 11:57:55 -05:00
:bad_variable
B=:bad_variable_error_message
!:program_error
:bad_variable_error_message
str No such variable.
xa
x0
2022-01-06 13:13:12 -05:00
:bad_function
B=:bad_function_error_message
!:program_error
:bad_function_error_message
str No such function.
xa
x0
2022-01-06 13:53:52 -05:00
:bad_byte
B=:bad_byte_error_message
!:program_error
:bad_byte_error_message
str Byte not in range 0-255.
xa
x0
2022-01-06 11:57:55 -05:00
:bad_number
B=:bad_number_error_message
!:program_error
:bad_number_error_message
str Bad number.
xa
x0
:bad_term
B=:bad_term_error_message
!:program_error
:bad_term_error_message
str Bad term.
xa
x0
2022-01-06 13:13:12 -05:00
:bad_call
B=:bad_call_error_message
!:program_error
:bad_call_error_message
str Bad function call.
xa
x0
2021-11-20 12:47:26 -05:00
:label_redefinition
B=:label_redefinition_error_message
!:program_error
:label_redefinition_error_message
str Label redefinition.
xa
x0
:global_redeclaration
B=:global_redeclaration_error_message
!:program_error
:global_redeclaration_error_message
str Global variable declared twice.
xa
x0
:local_redeclaration
B=:local_redeclaration_error_message
!:program_error
:local_redeclaration_error_message
str Local variable declared twice.
xa
x0
2021-11-19 09:52:27 -05:00
:general_error
call :eputs
J=d1
syscall x3c
:program_error
R=B
B=:"Line"
call :eputs
D=:line_number
D=8D
B=D
call :eputn
B=:line_number_separator
call :eputs
B=R
call :eputs
J=d1
syscall x3c
:"Line"
str Line
x20
x0
:line_number_separator
str :
x20
x0
:strlen
I=B
D=B
:strlen_loop
C=1I
?C=0:strlen_ret
I+=d1
!:strlen_loop
:strlen_ret
I-=D
A=I
return
; check if strings in rdi and rsi are equal, up to terminator in rcx
:string=
D=1I
A=1J
?D!A:return_0
?D=C:return_1
I+=d1
J+=d1
!:string=
; check if strings in rdi and rsi are equal, up to the first non-identifier character
:ident=
D=1I
B=D
call :isident
; I ended
?A=0:ident=_I_end
D=1J
B=D
call :isident
; J ended, but I didn't
?A=0:return_0
; we haven't reached the end of either
D=1I
A=1J
?D!A:return_0
I+=d1
J+=d1
!:ident=
:ident=_I_end
D=1J
B=D
call :isident
; check if J also ended
?A=0:return_1
; J didn't end
!:return_0
:return_0
A=d0
return
:return_1
A=d1
return
:return_2
A=d2
return
:return_3
A=d3
return
:return_4
A=d4
return
:return_5
A=d5
return
:return_6
A=d6
return
:return_7
A=d7
return
:return_8
A=d8
return
2021-11-20 12:47:26 -05:00
:return_J
A=J
return
2021-11-19 09:52:27 -05:00
; write the character in rbx to the file in rdi.
:fputc
C=B
I=S
I-=d1
1I=C
D=d1
syscall x1
return
; write the string in rbx to stderr
:eputs
J=B
call :strlen
D=A
I=J
J=d2
syscall x1
return
; write rbx in decimal to stderr
:eputn
I=B
J=S
J-=d1
:eputn_loop
D=d0
; divide by 10
B=d10
A=I
div
; quotient is new number
I=A
; add remainder to string
D+='0
1J=D
J-=d1
?I!0:eputn_loop
D=S
D-=J
I=J
J=d2
syscall x1
return
; copy rdx bytes from rsi to rdi.
; this copies from the left: if you're doing an overlapped copy, rsi should be greater than rdi
:memcpy
?D=0:return_0
A=1I
1J=A
I+=d1
J+=d1
D-=d1
!:memcpy
2021-11-20 11:38:34 -05:00
; copy from rdi to rsi, until byte cl is reached
:memccpy
D=1I
1J=D
I+=d1
J+=d1
?D!C:memccpy
return
2021-11-19 23:27:08 -05:00
:"global"
str global
x20
:"argument"
str argument
x20
:"local"
str local
x20
:"return"
str return
x20
2022-01-06 13:53:52 -05:00
:"return\n"
str return
xa
:"byte"
str byte
x20
2022-01-06 14:02:30 -05:00
:"string"
str string
x20
2021-11-19 23:27:08 -05:00
:"function"
str function
xa
2022-01-06 11:57:55 -05:00
:zero
x0
2021-11-19 23:27:08 -05:00
2021-11-19 09:52:27 -05:00
; put a 0 byte before the line (this is important for removing whitespace at the end of the line,
; specifically, we don't want this to be a space character)
x0
:line
reserve d1000
align
:global_variables_end
reserve d8
:static_memory_end
reserve d8
2021-11-19 23:27:08 -05:00
:local_variables_end
reserve d8
:stack_end
reserve d8
2021-11-20 11:38:34 -05:00
:labels_end
reserve d8
2021-11-19 09:52:27 -05:00
:line_number
reserve d8
:global_variables
reserve d50000
2021-11-19 23:27:08 -05:00
:local_variables
reserve d20000
2021-11-20 11:38:34 -05:00
:labels
2021-11-19 23:27:08 -05:00
reserve d200000
:second_pass
reserve d1
2021-11-19 23:27:08 -05:00
:ELF_header
x7f
x45
x4c
x46
x02
x01
x01
reserve d9
x02
x00
x3e
x00
x01
x00
x00
x00
x78
x00
x40
x00
x00
x00
x00
x00
x40
x00
x00
x00
x00
x00
x00
x00
reserve d12
x40
x00
x38
x00
x01
x00
x00
x00
x00
x00
x00
x00
x01
x00
2021-11-19 09:52:27 -05:00
x00
2021-11-19 23:27:08 -05:00
x00
x07
x00
x00
x00
x78
x00
x00
x00
x00
x00
x00
x00
x78
x00
x40
x00
x00
x00
x00
x00
reserve d8
x00
x00
x20
x00
x00
x00
x00
x00
x00
x00
x20
x00
x00
x00
x00
x00
x00
x10
x00
x00
x00
x00
x00
x00
; NOTE: we shouldn't end the file with a reserve; we don't handle that properly