Recently Written · git

forthlisp-z80

Z80 target for forthlisp: Z80 Forth kernel built by an SBCL assembler, z80pack cpmsim

git clone https://github.com/equwal/forthlisp-z80

Log | Files | Refs


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)))))))