-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathz80.lisp
228 lines (183 loc) · 7.91 KB
/
z80.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
(in-package :z80)
(defclass cpu ()
((ram :initform (make-array 65536 :initial-element #x00) :accessor ram)
(elapsed-cycles :initform 0)
(interrupts-enabled? :initform T :accessor interrupts-enabled?)
(halted? :initform nil :accessor halted?)
;; This z80 emulator only implements the CPU. This peripherals list
;; allows code using the emulator to attach peripherals on the I/O
;; ports that will be written to/read from when the IN/OUT
;; instructions are executed.
(peripherals :initform (list (make-instance 'simple-io-peripheral))
:accessor peripherals :initarg :peripherals)
;; R: contains the DRAM refresh count.
(r :initform 0 :accessor reg-r)
;; I: the IV register
(i :initform 0 :accessor reg-i)
;; Index registers
(ix :initform 0)
(iy :initform 0)
;; AF: 8-bit accumulator (A) and flag bits (F) carry, zero, minus,
;; parity/overflow, half-carry (used for BCD), and an
;; Add/Subtract flag (usually called N) also for BCD
;; BC: 16-bit data/address register or two 8-bit registers
;; DE: 16-bit data/address register or two 8-bit registers
;; HL: 16-bit accumulator/address register or two 8-bit registers
;; RR': 16-bit shadow registers
;; SP: stack pointer, 16 bits
;; PC: program counter, 16 bits
(af :initform 0)
(bc :initform 0)
(de :initform 0)
(hl :initform 0)
(af% :initform 0)
(bc% :initform 0)
(de% :initform 0)
(hl% :initform 0)
(sp :initform 0 :accessor sp)
(pc :initform 0 :accessor pc)))
(defparameter 16-bit-register->8-bit-registers (make-hash-table))
(defun 16-bit-register->8-bit-registers (16-bit-register-place)
(gethash 16-bit-register-place 16-bit-register->8-bit-registers))
(defmacro define-register-operators (register-name upper-name lower-name)
(let ((upper-accessor (intern (format nil "REG-~S" upper-name)))
(lower-accessor (intern (format nil "REG-~S" lower-name)))
(whole-accessor (intern (format nil "REG-~S" register-name)))
(mem-accessor (intern (format nil "MEM-~S" register-name)))
(c (gensym))
(value (gensym))
(upper-reg-value (gensym))
(lower-reg-value (gensym)))
`(progn
(defun ,upper-accessor (,c)
(ash (slot-value ,c ',register-name) -8))
(defun ,lower-accessor (,c)
(logand (slot-value ,c ',register-name) #xFF))
(defun ,whole-accessor (,c)
(slot-value ,c ',register-name))
(defun (setf ,upper-accessor) (,value ,c)
(let ((,lower-reg-value (,lower-accessor ,c)))
(setf (slot-value ,c ',register-name)
(logior (ash (logand ,value #xFF) 8) ,lower-reg-value))))
(defun (setf ,lower-accessor) (,value ,c)
(let ((,upper-reg-value (,upper-accessor ,c)))
(setf (slot-value ,c ',register-name)
(logior (ash ,upper-reg-value 8) (logand ,value #xFF)))))
(defun (setf ,whole-accessor) (,value ,c)
(setf (slot-value ,c ',register-name) (logand ,value #xFFFF)))
(defun ,mem-accessor (,c)
(elt (ram ,c) (,whole-accessor ,c)))
(defun (setf ,mem-accessor) (,value ,c)
(setf (elt (ram ,c) (,whole-accessor ,c)) ,value))
(setf (gethash ',whole-accessor 16-bit-register->8-bit-registers)
(cons ',upper-accessor ',lower-accessor)))))
(define-register-operators ix ixh ixl)
(define-register-operators iy iyh iyl)
(define-register-operators af a f)
(define-register-operators bc b c)
(define-register-operators de d e)
(define-register-operators hl h l)
(define-register-operators sp s p)
(define-register-operators pc pc-p pc-c)
(define-register-operators af% a% f%)
(define-register-operators bc% b% c%)
(define-register-operators de% d% e%)
(define-register-operators hl% h% l%)
(define-register-operators sp% s% sp%)
(defmethod load-ram-from-seq ((cpu cpu) rom &key (offset 0))
(replace (ram cpu) rom :start1 offset))
(defmethod load-ram-from-rom-file ((cpu cpu) rom-path &key (offset 0))
(let ((rom (alexandria:read-file-into-byte-vector rom-path)))
(load-ram-from-seq cpu rom :offset offset)))
(defmethod read-byte-from-ram ((cpu cpu) &key (address (pc cpu)))
(logand #xFFFF (elt (ram cpu) address)))
(defmethod fetch-byte-from-ram ((cpu cpu))
(read-byte-from-ram cpu :address (1+ (pc cpu))))
(defmethod read-word-from-ram ((cpu cpu) &key (address (pc cpu)))
(logior (logand #xFFFF (elt (ram cpu) address))
(logand #xFFFF (ash (elt (ram cpu) (1+ address)) 8))))
(defmethod fetch-word-from-ram ((cpu cpu) &key (address (1+ (pc cpu))))
(read-word-from-ram cpu :address address))
(defmethod write-word-to-ram ((cpu cpu) address value)
(setf (elt (ram cpu) address) (rshift value 8))
(setf (elt (ram cpu) (1+ address)) (logand #x00FF value)))
(defmethod read-port ((cpu cpu) port-id)
(let ((peripheral (nth port-id (peripherals cpu))))
(if peripheral
(read-from peripheral)
0)))
(defmethod write-port ((cpu cpu) (peripheral peripheral) value)
(write-to peripheral value))
(defmethod write-port ((cpu cpu) port-id value)
(let ((peripheral (nth port-id (peripherals cpu))))
(when peripheral
(write-to peripheral value))))
(defmethod port-c ((cpu cpu))
(read-from (elt (peripherals cpu) (reg-c cpu))))
(defmethod (setf port-c) (value (cpu cpu))
(when (>= (length (peripherals cpu)) (reg-c cpu))
(write-port cpu (elt (peripherals cpu) (reg-c cpu)) value)))
(defmethod flag-s ((cpu cpu))
(logbitp s-flag-pos (reg-f cpu)))
(defmethod flag-h ((cpu cpu))
(logbitp h-flag-pos (reg-f cpu)))
(defmethod flag-z ((cpu cpu))
(logbitp z-flag-pos (reg-f cpu)))
(defmethod flag-nz ((cpu cpu))
(not (flag-z cpu)))
(defmethod flag-c ((cpu cpu))
(logbitp c-flag-pos (reg-f cpu)))
(defun (setf flag-c) (value cpu)
(setf (reg-f cpu) (logior (reg-f cpu) (ash value c-flag-pos))))
(defmethod flag-nc ((cpu cpu))
(not (flag-c cpu)))
(defmethod flag-p ((cpu cpu))
(logbitp p-flag-pos (reg-f cpu)))
(defmethod flag-po ((cpu cpu))
(warn "flag-po must be implemented to interpret flag-po as flag-p = 0")
(logbitp p-flag-pos (reg-f cpu)))
(defmethod flag-pe ((cpu cpu))
(warn "flag-pe must be implemented to interpret flag-po as flag-p = 1")
(logbitp p-flag-pos (reg-f cpu)))
(defmethod flag-m ((cpu cpu))
(warn "flag-m must be implemented to interpret flag-s as flag-s = 1")
(logbitp n-flag-pos (reg-f cpu)))
(defmethod flag-n ((cpu cpu))
(logbitp n-flag-pos (reg-f cpu)))
(defmethod flag-h ((cpu cpu))
(logbitp h-flag-pos (reg-f cpu)))
(defmethod reset-cpu ((cpu cpu))
(setf (slot-value cpu 'r) #x00)
(setf (slot-value cpu 'i) #x00)
(setf (reg-af cpu) #x0000
(reg-bc cpu) #x0000
(reg-hl cpu) #x0000
(reg-ix cpu) #x0000
(reg-iy cpu) #x0000)
(setf (reg-pc cpu) #x0000))
(defmethod execute-instruction ((cpu cpu) next-instruction opcode)
(funcall (microcode next-instruction) cpu opcode))
(defgeneric execute-next-instruction (cpu &optional instruction-table handle-pc))
(defmethod execute-next-instruction ((cpu cpu) &optional (instruction-table unprefixed-table) (handle-pc t))
(let* ((opcode (elt (ram cpu) (pc cpu)))
(next-instruction (next-instruction instruction-table opcode))
(orig-pc (pc cpu)))
(when logging-enabled
(debug-cpu cpu))
(execute-instruction cpu next-instruction opcode)
(when (and (eq orig-pc (pc cpu)) handle-pc)
(incf (pc cpu) (instruction-size instruction-table next-instruction)))
next-instruction))
(defun emulate-rom (cpu rom-path &key (starting-pc 0) (max-instructions single-float-positive-infinity))
(load-ram-from-rom-file cpu rom-path)
(emulate cpu :starting-pc starting-pc :max-instructions max-instructions))
(defun emulate (cpu &key (starting-pc 0) (max-instructions single-float-positive-infinity))
(setf (pc cpu) starting-pc)
(let ((num-instructions 0))
(loop
do
(execute-next-instruction cpu)
(incf num-instructions)
while (and (not (>= num-instructions max-instructions))
(not (halted? cpu))))
cpu))