3.4 Ptr Run-Time System
(require cpsc411/ptr-run-time) | package: cpsc411-lib |
This library provides the run-time system for supporting the CPSC 411 languages that can print ptr-encoded data.
procedure
(wrap-x64-run-time v) → string?
v : string?
This run-time system prints the final return value, which must be a ptr encoded data type, to the standard output port, as an ASCII string.
> (require racket/pretty) > (pretty-display (wrap-x64-run-time "mov rax, 5")) mov rax, 5
procedure
(wrap-x64-boilerplate e) → string?
e : string?
Currently only supports Linux and macOS.
> (require racket/pretty) > (pretty-display (wrap-x64-boilerplate "mov rax, 5"))
global start
section .text
start:
; setup heap with mmap
mov rax, 9
mov rdi, 0 ; ask OS to find a page-aligned place
mov rsi, 134217728 ; mmap this many bytes
mov rdx, 3 ; permissions: read and write
mov r10, 34 ; type of map: memory
mov r8, -1 ; -1 file descriptor for no file
mov r9, 0 ; no offset
syscall
mov r12, rax
mov rbp, rsp
mov r15, done
mov rax, 5
jmp done
; Convert RAX to a string, left in buffer msg
; The result should be a number in RAX
; build the string backwards, then reverse
done:
mov rax, rax
; exit after printing.
mov rsp, rbp
mov r13, exit
printer:
mov r10, rax
; if fixnum
and r10, 7
cmp r10, 0
je fixnum_to_string
; if pair
mov r10, rax
and r10, 7
cmp r10, 1
je print_pair
; ; if vector
mov r10, rax
and r10, 7
cmp r10, 3
je print_vector
; if procedure
mov r10, rax
and r10, 7
cmp r10, 2
je print_procedure
; if boolean
mov r10, rax
and r10, 247
cmp r10, 6
je boolean_to_string
; if void
mov r10, rax
and r10, 255
cmp r10, 30
je void_to_string
; if empty
mov r10, rax
and r10, 255
cmp r10, 22
je empty_to_string
; if ascii
mov r10, rax
and r10, 255
cmp r10, 46
je ascii_to_string
; if error
mov r10, rax
and r10, 255
cmp r10, 62
je error_to_string
invalid_to_string:
mov rsi, invalid_msg
mov rdx, invalid_len
jmp print_msg
print_vector:
mov r10, rax
mov rsi, scratch
; print left-paren
mov BYTE [rsi], '#'
mov BYTE [rsi + 1], '('
mov rdx, 2
mov rax, 1
mov rdi, 1
syscall
; remove ptr tag
sub r10, 3
; load length
mov r9, [r10 + 0]
; move pointer to base of payload
add r10, 8
; convert length fixnum to int64
sar r9, 3
mov r8, 0
cmp r9, r8
je finish_vector
.loop:
; print r8th element
mov rax, [r10 + r8*8]
push r13
push r10
push r8
push r9
mov r13, .loop_return
jmp printer
.loop_return:
pop r9
pop r8
pop r10
pop r13
inc r8
cmp r9, r8
je finish_vector
mov rsi, scratch
mov BYTE [rsi], ' '
mov rdx, 1
mov rax, 1
mov rdi, 1
syscall
jmp .loop
finish_vector:
mov rsi, scratch
mov BYTE [rsi], ')'
mov rdx, 1
mov rax, 1
mov rdi, 1
syscall
jmp r13
print_procedure:
mov rsi, procedure_msg
mov rdx, procedure_len
jmp print_msg
print_pair:
mov r10, rax
mov rsi, scratch
; print left-paren
mov BYTE [rsi], '('
mov rdx, 1
mov rax, 1
mov rdi, 1
syscall
; print first element
push r13
push r10
mov r13, print_second_element
mov rax, [r10 + -1]
jmp printer
print_second_element:
pop r10
pop r13
mov rsi, scratch
; print the . part of a pair
mov BYTE [rsi], ' '
mov BYTE [rsi + 1], '.'
mov BYTE [rsi + 2], ' '
mov rdx, 3
mov rax, 1
mov rdi, 1
syscall
mov rax, [r10 + 7]
push r13
mov r13, print_final_paren
jmp printer
print_final_paren:
pop r13
mov rsi, scratch
mov BYTE [rsi], ')'
mov rdx, 1
mov rax, 1
mov rdi, 1
syscall
jmp r13
error_to_string:
sar rax, 8
mov r10, rax
mov rsi, error_msg
mov rdx, error_len
mov rax, 1
mov rdi, 2
syscall
mov rax, 60
mov rdi, r10
syscall
boolean_to_string:
xor rax, 6
cmp rax, 0
je false_to_string
mov rsi, true_msg
mov rdx, true_len
jmp print_msg
false_to_string:
mov rsi, false_msg
mov rdx, false_len
jmp print_msg
void_to_string:
mov rsi, void_msg
mov rdx, void_len
jmp print_msg
empty_to_string:
mov rsi, empty_msg
mov rdx, empty_len
jmp print_msg
ascii_to_string:
sar rax, 8
mov rsi, ascii_char_msg
mov rdx, rax
mov BYTE [rsi + 2], dl
mov rdx, ascii_char_len
jmp print_msg
fixnum_to_string:
sar rax, 3
mov rdi, 0 ; index into msg, starting at beginning
mov r12, 10 ; divide by 10; idiv requires register arg
mov rsi, fixnum_msg
mov r15, 0 ; clear r15 to store negative flag
cmp rax, 0 ; if negative
js neg
loop:
mov rdx, 0 ; extend rax to rdx
idiv r12 ; signed divide RDX:RAX by r12, with result
; stored in RAX ← Quotient, RDX ← Remainder.
add rdx, 48 ; convert digit to ASCII char
mov BYTE [rsi + rdi], dl ; mov char into msg
inc rdi
cmp rax, 0
jne loop
cmp r15, 0 ; if number if negative, add - as final character
jl add_minus
; rdi contains the length of the msg
; msg is in rsi
reverse_msg:
mov rdx, rdi ; preserve the length for printing
dec rdi ; length -> final index
mov r9, 0 ; first character
rev_loop:
cmp rdi, r9
jle print_msg
; Until rdi <= r9, swap [rsi + rdi] and [rsi + r9]
; Save last character into register, move first character
mov r8b, BYTE [rsi + rdi]
mov r10b, BYTE [rsi + r9]
mov BYTE [rsi + rdi], r10b
mov BYTE [rsi + r9], r8b
inc r9
dec rdi
jmp rev_loop
print_msg:
mov rax, 1
mov rdi, 1 ; And I want it to write to stdout
; The message pointer is in rsi
; length in rdx
syscall
jmp r13
exit:
mov rax, 60 ; I'm about to call the OS sys_exit function
mov rdi, 0 ; The exit code is 0
syscall
neg:
mov r15, -1
imul rax, -1
jmp loop
add_minus:
mov BYTE [rsi + rdi], 45
inc rdi
jmp reverse_msg
section .bss
scratch: resb 8
fixnum_msg: resb 19
section .data
dummy: db 0 ; Mac isn't happy with an empty .data
invalid_msg: db 'Invalid data returned'
invalid_len: equ $-invalid_msg
true_msg: db '#t'
true_len: equ $-true_msg
false_msg: db '#f'
false_len: equ $-false_msg
empty_msg: db '()'
empty_len: equ $-empty_msg
void_msg: db ''
void_len: equ $-void_msg
ascii_char_msg: db '#\ '
ascii_char_len: equ $-ascii_char_msg
error_msg: db 'Run-time error; see exit code'
error_len: equ $-error_msg
procedure_msg: db '#<procedure>'
procedure_len: equ $-procedure_msg