Skip to content

Instantly share code, notes, and snippets.

@SeijiEmery
Last active September 19, 2017 05:07
Show Gist options
  • Save SeijiEmery/7a4b8265fefa086e7b8164726071334a to your computer and use it in GitHub Desktop.
Save SeijiEmery/7a4b8265fefa086e7b8164726071334a to your computer and use it in GitHub Desktop.
sketch for macro-assembler with lisp syntax and builtin testcases, may build this at some point (yes, I'm a bit nuts)
;
; Would be neat to have enumerable enums like this:
;
(enum colors
(red 0xff0000ff)
(green 0x00ff00ff)
(blue 0x0000ffff)
(alpha 0x000000ff))
;
; BSD, x86_64 syscalls
;
(proc exit
(params
(in rdi rv)
(local rax _)
)
(body
(mov rax SYSCALL_EXIT)
(syscall)
)
)
(proc write
(params
(in rdi fd)
(in rsi buffer)
(in rdx length)
(local rax _)
)
(body
(mov rax SYSCALL_WRITE)
(syscall)
)
)
(proc read
(params
(in rdi fd)
(in rsi buffer)
(in rdx length)
(local rax _)
)
(body
(mov rax SYSCALL_READ)
(syscall)
)
)
;
; A few string algorithms
;
(proc strncpy
(params
(in rsi src)
(in rdi dst)
(in rcx len)
(local rax _)
)
(body
; Zero-length case
(test len len)
(jz end)
(clear rax)
(label top)
; If --len == 0, return
(dec len)
(jl end)
; If *src == 0, return
(mov al [src])
(test al al)
(jz end)
; Copy byte, and repeat
(mov [dst] al)
(inc src)
(inc dst)
(jmp top)
(label end)
)
)
(proc memcmp
(params
(in rsi s1)
(in rdi s2)
(in rcx len) ; Must be (min (length-of-bytes s1) (length-of-bytes s2))
(out flags result)
)
(body
(cld)
(cmp rcx, rcx)
(repe cmpsb)
)
(tests
(should-compare-equal
(data-array i8 first (1 2 3 4 5 6 7))
(data-array i8 second (1 2 3 4 5 6 7))
(call-proc memcmp (s1 first) (s2 second) (len (length-of-bytes first)))
(require-flags z)
)
(should-compare-less
(data-array i8 first (1 2 3 4 4 6 7))
(data-array i8 second (1 2 3 4 5 6 7))
(call-proc memcmp (s1 first) (s2 second) (len (length-of-bytes first)))
(require-flags l)
)
(should-compare-greater
(data-array i8 first (1 2 3 4 5 6 7))
(data-array i8 second (1 2 3 4 4 6 7))
(call-proc memcmp (s1 first) (s2 second) (len (length-of-bytes first)))
(require-flags g)
)
(should-handle-single-byte
(data-array i8 first (1))
(data-array i8 second (2))
(call-proc memcmp (s1 first) (s2 second) (len (length-of-bytes first)))
(require-flags g)
)
(should-handle-zero
(call-proc memcmp (s1 0) (s2 0) (len 0))
(require-flags z)
)
(should-handle-zero-1
(data-array i8 first (1 2 3 4 5 6 7))
(call-proc memcmp (s1 first) (s2 0) (len 0))
(require-flags z)
)
(should-handle-zero-2
(data-array i8 first (1 2 3 4 5 6 7))
(call-proc memcmp (s1 0) (s2 first) (len 0))
(require-flags z)
)
)
)
; Writes digits of an unsigned 64-bit value as to a buffer.
; Can write decimal binary (base 2), (base 10), hexadecimal (base 16), etc.
; Uses '0'-'9' for base 2-10, 'A'-'Z' for base 11-36. Base 37+ is undefined.
(proc write-uint
(params
(in rax value)
(in rbx base (default 10))
(inout rdi buffer (require non-null))
(inout rcx length (require non-zero))
(local rdx digit)
)
(body
; save original length
(push length)
; save TOS
(push rbp)
(mov rbp rsp)
; Write digits to buffer (stack memory, reversed)
(label write-digits)
; Divide rax by base, convert remainder (rdx) to ascii digit
(clear rdx)
(idiv base)
(add digit 0x30) ; add 0x30 to convert to ascii '0'-'9' (0x30-0x39)
(cmp digit 0x39) ; if >= '9', convert to ascii 'A'-'Z' (0x41-0x5A)
(if-g (add digit 8))
; Save digit to stack memory
(mov [rsp] digit)
(dec rsp)
(test value value)
(jnz write-digits)
; calculate # digits written in rdx
(mov rdx rsp)
(sub rdx rbp)
; calculate rdx = min(length, rdx)
(cmp rdx length)
(cmovg rdx length)
; digits written backwards, but stack grows backwards
; => can now just memcpy from stack memory
(inline-proc memcpy (src rsp) (dst buffer) (length rdx))
; restore stack + original length
(mov rsp rbp)
(pop rbp)
(pop length)
; update buffer + length values to point to end of written value
(add buffer rdx)
(sub length rdx)
)
(tests
; TODO: check operating correctly, check for buffer overflows, edge cases, etc.
)
)
; Writes digits of a signed 64-bit value as to a buffer.
; Can write binary (base 2), decimal (base 10), hexadecimal (base 16), etc.
; Uses '0'-'9' for base 2-10, 'A'-'Z' for base 11-36. Base 37+ is undefined.
(proc write-int
(params
(in rax value)
(in rbx base (default 10))
(inout rdi buffer (require non-null))
(inout rcx length (require non-zero))
)
(body
(cmp value 0)
(if-z ; if value == 0 (special case)
(mov [buffer] byte '0')
(inc buffer)
(dec length)
(jmp end)
)
(if-l ; if value < 0 (negative case)
(neg value) ; make value positive
(require-gt value 0) ; assert(value > 0)
; write '-'
(mov [buffer] byte '-')
(inc buffer)
(dec length)
; stop iff we cannot write any more bytes (length MAY be zero)
(jz end)
)
(inline-proc write-uint (value value) (base base) (buffer buffer) (length length))
(label end)
)
(tests
; TODO: check operating correctly, check for buffer overflows, edge cases, etc.
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment