On this page:
wrap-x64-run-time
wrap-x64-boilerplate
8.10

3.4 Ptr Run-Time System

William J. Bowman <wjb@williamjbowman.com>

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?
Wraps v, a string representing a sequence of x64 instructions in Intel syntax, with the CPSC 411 run-time system.

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.

Currently only supports Linux and macOS.

Examples:
> (require racket/pretty)
> (pretty-display (wrap-x64-run-time "mov rax, 5"))

mov rax, 5

procedure

(wrap-x64-boilerplate e)  string?

  e : string?
Wraps e, a string representing a sequence of x64 instructions in Intel syntax, with necessary boilerplate to compile via nasm.

Currently only supports Linux and macOS.

Examples:
> (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