aboutsummaryrefslogtreecommitdiff
path: root/MOS6502.lisp
blob: 2d70c61228fbdf5d447b4af1dd70bf405085dd12 (plain) (blame)
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
;;;; Processor

;;; Processor function
;;;
;;; Each time the *CPU* is called, it executes one instruction
;;;
;;; The implementation uses closures for the internal registers.
(defparameter *CPU* 
  ;; As far as I can tell, there are "secret" internal buffers in the 6502 where fetched
  ;; data is stored. These should be address bus high (ABH), address bus low (ABL) and
  ;; adder holder register (ADD).
  ;; To save myself the headache, I've just named these opcode and loc, since thats what
  ;; they're used for, and they will be only "internal" to the processor.
  ;; http://www.weihenstephan.org/~michaste/pagetable/6502/6502.jpg
  ;; https://retrocomputing.stackexchange.com/questions/19750/where-does-the-6502-store-the-low-order-byte-after-fetching-for-read-instruction
  (let ((PC (elt *M* #xFFFD)) (AC 0) (X 0) (Y 0) (SR 0) (SP #xFF) (opcode) (loc)) 
    #'(lambda ()
        (setf opcode (elt *M* PC)) (incf PC)  ; fetch opcode, increment PC
        (cond ((eql opcode +OP_BRK+)
               (incf PC)                        ; read next instruction and throw it away
               (setf (elt *M* SP) PC) (decf SP) ; push PC on stack
               (setf SR (logior SR +SR_I+))
               (setf (elt *M* SP) SR) (decf SP) ; push SR on stack
               (setf PC (elt *M* #xFFFE)))

              ((eql opcode +OP_INCa+)
               (setf loc (elt *M* PC)) (incf PC) ; fetch (low and high byte of) addr
               (incf (elt *M* loc))              ; do operation and write new value
               (setf SR (logior SR +SR_N+ +SR_Z+)))

              ))))

;;; External memory, where the zero page, stack and general purpose memory resides
;;; Another file has to implement it.
(defvar *M*
  (make-array (1+ #xFFFF) :element-type 'integer :initial-element 0))

;;; SR flags
(defconstant +SR_N+ #b10000000)
(defconstant +SR_V+ #b01000000)
(defconstant +SR_D+ #b00001000)
(defconstant +SR_I+ #b00000100)
(defconstant +SR_Z+ #b00000010)
(defconstant +SR_C+ #b00000001)

;;; Processor opcodes
;;; Ends with a small letter, depending on address mode:
;;;   ac | accumulator
;;;   a  | absolute
;;;   ax | absolute, X-indexed
;;;   ay | absolute, Y-indexed
;;;   d  | immediate
;;;      | implied
;;;   i  | indirect
;;;   xi | X-indexed, indirect
;;;   iy | indirect, Y-indexed
;;;   r  | relative
;;;   z  | zeropage
;;;   zx | zeropage, X-indexed
;;;   zy | zeropage, Y-indexed
(defconstant +OP_BRK+   #x00)
(defconstant +OP_ORAxi+ #x01)
(defconstant +OP_ORAz+  #x05)
(defconstant +OP_ASLz+  #x06)
(defconstant +OP_PHP+   #x08)
(defconstant +OP_ORAd+  #x09)
(defconstant +OP_ASLac+ #x0A)
(defconstant +OP_ORAa+  #x0D)
(defconstant +OP_ASLa+  #x0E)


(defconstant +OP_BPLr+  #x10)
(defconstant +OP_ORAiy+ #x11)
(defconstant +OP_ORAzx+ #x15)
(defconstant +OP_ASLzx+ #x16)
(defconstant +OP_CLC+   #x18)
(defconstant +OP_ORAay+ #x19)
(defconstant +OP_ORAax+ #x1D)
(defconstant +OP_ASLax+ #x1E)

(defconstant +OP_JSRa+  #x20)
(defconstant +OP_ANDxi+ #x21)
(defconstant +OP_BITz+  #x24)
(defconstant +OP_ANDz+  #x25)
(defconstant +OP_ROLz+  #x26)
(defconstant +OP_PLP+   #x28)
(defconstant +OP_ANDd+  #x29)
(defconstant +OP_ROLac+ #x2A)
(defconstant +OP_BITa+  #x2C)
(defconstant +OP_ANDa+  #x2D)
(defconstant +OP_ROLa+  #x2E)

(defconstant +OP_INCa+  #xEE)

;;; Helpers for lisp
(defun 2+ (val) (+ 2 val))