asm.lisp (9972 bytes)
1 ;;; asm.lisp -- layer 2: a two-pass Z80 assembler hosted in SBCL. 2 ;;; Source is a list of forms: a bare symbol is a label, a list is an 3 ;;; instruction or directive, e.g. (ld a (@ hl)), (jp nz loop), (dw docol). 4 ;;; (@ x) marks memory indirection: (@ hl), (@ ix 3) = (ix+3), (@ var) = (var). 5 ;;; Immediates are numbers, label symbols, or (+ ...) / (- ...) / (lo x) / (hi x). 6 7 (defpackage :z80 (:use :cl) (:export #:assemble #:insn->text)) 8 (in-package :z80) 9 10 (defvar *labels*) 11 (defvar *pc*) 12 (defvar *out*) 13 (defvar *final* nil "True on pass 2, when every label must resolve.") 14 15 (defun nm (x) (and (symbolp x) (symbol-name x))) 16 (defun is (x name) (equal (nm x) name)) 17 18 (defparameter +r8+ '("B" "C" "D" "E" "H" "L" nil "A")) 19 (defparameter +r16+ '("BC" "DE" "HL" "SP")) 20 (defparameter +cc+ '("NZ" "Z" "NC" "C" "PO" "PE" "P" "M")) 21 (defparameter +alu+ '("ADD" "ADC" "SUB" "SBC" "AND" "XOR" "OR" "CP")) 22 (defparameter +rot+ '("RLC" "RRC" "RL" "RR" "SLA" "SRA" nil "SRL")) 23 (defparameter +simple+ 24 '(("NOP" #x00) ("RLCA" #x07) ("RRCA" #x0F) ("RLA" #x17) ("RRA" #x1F) 25 ("CPL" #x2F) ("SCF" #x37) ("CCF" #x3F) ("HALT" #x76) ("DI" #xF3) ("EI" #xFB) 26 ("RET" #xC9) ("EXX" #xD9) ("NEG" #xED #x44) ("LDIR" #xED #xB0) 27 ("LDDR" #xED #xB8) ("CPIR" #xED #xB1))) 28 29 (defun r8 (x) 30 "Register code 0-7 of X, (@ hl) being 6, or NIL." 31 (cond ((and (consp x) (is (car x) "@") (is (cadr x) "HL") (null (cddr x))) 6) 32 ((symbolp x) (position (nm x) +r8+ :test #'equal)))) 33 (defun r16 (x &optional (list +r16+)) (and (symbolp x) (position (nm x) list :test #'equal))) 34 (defun ixd (x) "Displacement of (@ ix d), or NIL." 35 (and (consp x) (is (car x) "@") (is (cadr x) "IX") (or (caddr x) 0))) 36 (defun mem (x) "Address expression of (@ expr), or NIL." 37 (and (consp x) (is (car x) "@") (not (r16 (cadr x) '("HL" "BC" "DE" "SP" "IX"))) (cadr x))) 38 (defun imm-p (x) (or (integerp x) (and (symbolp x) (not (r8 x)) (not (r16 x '("BC" "DE" "HL" "SP" "IX" "AF")))) 39 (and (consp x) (not (is (car x) "@"))))) 40 41 (defun value (x) 42 (cond ((integerp x) x) 43 ((characterp x) (char-code x)) 44 ((symbolp x) (or (gethash x *labels*) 45 (if *final* (error "undefined label ~s" x) 0))) 46 ((is (car x) "+") (reduce #'+ (mapcar #'value (cdr x)))) 47 ((is (car x) "-") (apply #'- (mapcar #'value (cdr x)))) 48 ((is (car x) "LO") (logand (value (cadr x)) 255)) 49 ((is (car x) "HI") (logand (ash (value (cadr x)) -8) 255)) 50 (t (error "bad expression ~s" x)))) 51 52 (defun b (&rest bytes) (dolist (x bytes) (vector-push-extend (logand x 255) *out*) (incf *pc*))) 53 (defun w (x) (let ((v (value x))) (b v (ash v -8)))) 54 (defun d8 (x) (let ((v (value x))) 55 (when (and *final* (not (<= -128 v 127))) (error "displacement ~d out of range" v)) v)) 56 (defun rel (x) (let ((v (- (value x) (+ *pc* 1)))) 57 (when (and *final* (not (<= -128 v 127))) (error "jr to ~s out of range" x)) v)) 58 59 (defun insn (form) 60 (let* ((op (nm (car form))) (x (cadr form)) (y (caddr form)) (n (length (cdr form)))) 61 (macrolet ((when-match (test &body body) `(when ,test ,@body (return-from insn)))) 62 (let ((s (assoc op +simple+ :test #'equal))) 63 (when-match (and s (= n 0)) (apply #'b (cdr s)))) 64 (cond 65 ((equal op "DB") (when-match t (dolist (v (cdr form)) (if (stringp v) (map nil (lambda (c) (b (char-code c))) v) (b (value v)))))) 66 ((equal op "DW") (when-match t (mapc #'w (cdr form)))) 67 ((equal op "LD") 68 (let ((rx (r8 x)) (ry (r8 y))) 69 (when-match (and rx ry (not (= rx ry 6))) (b (+ #x40 (* 8 rx) ry))) 70 (when-match (and rx (ixd y)) (b #xDD (+ #x46 (* 8 rx)) (d8 (ixd y)))) 71 (when-match (and (ixd x) ry) (b #xDD (+ #x70 ry) (d8 (ixd x)))) 72 (when-match (and (ixd x) (imm-p y)) (b #xDD #x36 (d8 (ixd x)) (value y))) 73 (when-match (and (is x "A") (consp y) (is (car y) "@") (is (cadr y) "BC")) (b #x0A)) 74 (when-match (and (is x "A") (consp y) (is (cadr y) "DE") (is (car y) "@")) (b #x1A)) 75 (when-match (and (is y "A") (consp x) (is (cadr x) "BC") (is (car x) "@")) (b #x02)) 76 (when-match (and (is y "A") (consp x) (is (cadr x) "DE") (is (car x) "@")) (b #x12)) 77 (when-match (and (is x "A") (mem y)) (b #x3A) (w (mem y))) 78 (when-match (and (is y "A") (mem x)) (b #x32) (w (mem x))) 79 (when-match (and rx (imm-p y)) (b (+ #x06 (* 8 rx)) (value y))) 80 (when-match (and (is x "SP") (is y "HL")) (b #xF9)) 81 (when-match (and (is x "SP") (is y "IX")) (b #xDD #xF9)) 82 (when-match (and (is x "IX") (imm-p y)) (b #xDD #x21) (w y)) 83 (when-match (and (is x "IX") (mem y)) (b #xDD #x2A) (w (mem y))) 84 (when-match (and (is y "IX") (mem x)) (b #xDD #x22) (w (mem x))) 85 (when-match (and (is x "HL") (mem y)) (b #x2A) (w (mem y))) 86 (when-match (and (is y "HL") (mem x)) (b #x22) (w (mem x))) 87 (when-match (and (r16 x) (mem y)) (b #xED (+ #x4B (* 16 (r16 x)))) (w (mem y))) 88 (when-match (and (r16 y) (mem x)) (b #xED (+ #x43 (* 16 (r16 y)))) (w (mem x))) 89 (when-match (and (r16 x) (imm-p y)) (b (+ #x01 (* 16 (r16 x)))) (w y)))) 90 ((member op '("PUSH" "POP") :test #'equal) 91 (let ((base (if (equal op "PUSH") #xC5 #xC1))) 92 (when-match (is x "IX") (b #xDD (+ base #x20))) 93 (when-match t (b (+ base (* 16 (r16 x '("BC" "DE" "HL" "AF")))))))) 94 ((member op +alu+ :test #'equal) 95 (let* ((k (position op +alu+ :test #'equal)) 96 (src (if (and (= n 2) (is x "A")) y (if (= n 1) x nil)))) 97 (when-match (and (= n 2) (is x "HL") (r16 y)) 98 (case k (0 (b (+ #x09 (* 16 (r16 y))))) (1 (b #xED (+ #x4A (* 16 (r16 y))))) 99 (3 (b #xED (+ #x42 (* 16 (r16 y))))) (t (error "bad ~s" form)))) 100 (when-match (and (= n 2) (is x "IX") (= k 0)) 101 (b #xDD (+ #x09 (* 16 (r16 y '("BC" "DE" "IX" "SP")))))) 102 (when-match (and src (r8 src)) (b (+ #x80 (* 8 k) (r8 src)))) 103 (when-match (and src (ixd src)) (b #xDD (+ #x86 (* 8 k)) (d8 (ixd src)))) 104 (when-match (and src (imm-p src)) (b (+ #xC6 (* 8 k)) (value src))))) 105 ((member op '("INC" "DEC") :test #'equal) 106 (let ((dec (equal op "DEC"))) 107 (when-match (is x "IX") (b #xDD (if dec #x2B #x23))) 108 (when-match (ixd x) (b #xDD (if dec #x35 #x34) (d8 (ixd x)))) 109 (when-match (r8 x) (b (+ (if dec 5 4) (* 8 (r8 x))))) 110 (when-match (r16 x) (b (+ (if dec #x0B #x03) (* 16 (r16 x))))))) 111 ((equal op "JP") 112 (when-match (and (consp x) (is (cadr x) "HL")) (b #xE9)) 113 (when-match (and (consp x) (is (cadr x) "IX")) (b #xDD #xE9)) 114 (when-match (= n 2) (b (+ #xC2 (* 8 (r16 x +cc+)))) (w y)) 115 (when-match t (b #xC3) (w x))) 116 ((equal op "CALL") 117 (when-match (= n 2) (b (+ #xC4 (* 8 (r16 x +cc+)))) (w y)) 118 (when-match t (b #xCD) (w x))) 119 ((equal op "RET") (when-match t (b (+ #xC0 (* 8 (r16 x +cc+)))))) 120 ((equal op "JR") 121 (when-match (= n 2) (b (+ #x20 (* 8 (r16 x '("NZ" "Z" "NC" "C"))))) (b (rel y))) 122 (when-match t (b #x18) (b (rel x)))) 123 ((equal op "DJNZ") (when-match t (b #x10) (b (rel x)))) 124 ((equal op "RST") (when-match t (b (+ #xC7 (value x))))) 125 ((equal op "EX") 126 (when-match (is x "DE") (b #xEB)) 127 (when-match (and (consp x) (is y "HL")) (b #xE3)) 128 (when-match (and (consp x) (is y "IX")) (b #xDD #xE3)) 129 (when-match (is x "AF") (b #x08))) 130 ((equal op "IN") (when-match (is x "A") (b #xDB (value (mem y))))) 131 ((equal op "OUT") (when-match (is y "A") (b #xD3 (value (mem x))))) 132 ((member op +rot+ :test #'equal) 133 (when-match (r8 x) (b #xCB (+ (* 8 (position op +rot+ :test #'equal)) (r8 x))))) 134 ((member op '("BIT" "RES" "SET") :test #'equal) 135 (when-match (r8 y) (b #xCB (+ (* 64 (1+ (position op '("BIT" "RES" "SET") :test #'equal))) 136 (* 8 (value x)) (r8 y)))))) 137 (error "cannot assemble ~s" form)))) 138 139 (defun assemble (forms) 140 "Assemble FORMS at address 0. Return the byte vector and the label table." 141 (let ((*labels* (make-hash-table))) 142 (dolist (*final* '(nil t)) 143 (let ((*pc* 0) (*out* (make-array 0 :element-type '(unsigned-byte 8) :adjustable t :fill-pointer 0))) 144 (dolist (f forms) 145 (cond ((symbolp f) 146 (when (and (not *final*) (gethash f *labels*)) (error "duplicate label ~s" f)) 147 (setf (gethash f *labels*) *pc*)) 148 ((is (car f) "EQU") (setf (gethash (cadr f) *labels*) (value (caddr f)))) 149 (t (insn f)))) 150 (when *final* (return-from assemble (values (coerce *out* '(simple-array (unsigned-byte 8) (*))) *labels*))))))) 151 152 ;;; Standard-syntax printer, so the reference assembler can check our bytes. 153 (defun opnd->text (x) 154 (cond ((and (consp x) (is (car x) "@")) 155 (if (and (is (cadr x) "IX") (cddr x)) (format nil "(ix+~d)" (caddr x)) (format nil "(~a)" (opnd->text (cadr x))))) 156 ((consp x) (format nil "~{~a~^~a~}" (cdr (loop for a in (cdr x) collect (car x) collect (opnd->text a))))) 157 ((stringp x) (format nil "'~a'" x)) 158 ((is x "AF") "af") 159 ((symbolp x) (string-downcase (nm x))) 160 (t (princ-to-string x)))) 161 (defun insn->text (form) 162 (let ((op (string-downcase (nm (car form))))) 163 (cond ((and (equal op "ex") (is (cadr form) "AF")) "ex af,af'") 164 ((member op '("add" "adc" "sbc") :test #'equal) 165 (format nil "~a ~{~a~^,~}" op (mapcar #'opnd->text (if (= (length form) 2) (list 'a (cadr form)) (cdr form))))) 166 ((equal op "dw") (format nil "defw ~{~a~^,~}" (mapcar #'opnd->text (cdr form)))) 167 ((equal op "db") (format nil "defb ~{~a~^,~}" (mapcar #'opnd->text (cdr form)))) 168 (t (format nil "~a ~{~a~^,~}" op (mapcar #'opnd->text (cdr form)))))))