├── .gitignore ├── LICENSE ├── README.md ├── images ├── giko011.gif ├── nestest.gif └── super-mario-bros.gif ├── nes-cartridge.el ├── nes-color.el ├── nes-cpu.el ├── nes-dma.el ├── nes-instruction.el ├── nes-interrupt.el ├── nes-keypad.el ├── nes-ppu.el ├── nes-util.el └── nes.el /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Wataru MIYAGUNI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A NES Emulator 2 | ========== 3 | 4 | | [nestest.nes](https://wiki.nesdev.com/w/index.php/Emulator_tests) | [giko011.nes](http://gikofami.fc2web.com/nes/nes011.html) | super-mario-bros.nes (100 times speed :sob: ) | 5 | | --- | --- | --- | 6 | | ![nestest.nes](./images/nestest.gif) | ![giko011.nes](./images/giko011.gif) | ![super-mario-bros.gif](./images/super-mario-bros.gif) | 7 | 8 | Requirements 9 | ---------- 10 | 11 | Emacs 25 or higher 12 | 13 | Usage 14 | ---------- 15 | 16 | TODO 17 | -------------------------------------------------------------------------------- /images/giko011.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongo/emacs-nes/02b8fe34a2b1826837bfa4e55aba887eded38440/images/giko011.gif -------------------------------------------------------------------------------- /images/nestest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongo/emacs-nes/02b8fe34a2b1826837bfa4e55aba887eded38440/images/nestest.gif -------------------------------------------------------------------------------- /images/super-mario-bros.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gongo/emacs-nes/02b8fe34a2b1826837bfa4e55aba887eded38440/images/super-mario-bros.gif -------------------------------------------------------------------------------- /nes-cartridge.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (eval-when-compile (require 'cl)) 4 | 5 | (require 'bindat) 6 | 7 | (defconst nes/cartridge-ines-header-spec 8 | '((constant vec 4 u8) 9 | (size-of-prg-rom u8) 10 | (size-of-chr-rom u8) 11 | (flags6 u8) 12 | (flags7 u8) 13 | (size-of-prg-ram u8) 14 | (flags9 u8) 15 | (flags10 u8) 16 | (zero-pad vec 5 u8))) 17 | 18 | ;; 19 | ;; see https://wiki.nesdev.com/w/index.php/INES#iNES_file_format 20 | ;; 21 | (defstruct (nes/cartridge 22 | (:conc-name nes/cartridge->)) 23 | header 24 | trainer 25 | prg-rom 26 | chr-rom 27 | pc-inst-rom ;; PlayChoice 10 28 | pc-prom-data 29 | pc-prom-counter-out-data) 30 | 31 | (defstruct (nes/cartridge-header 32 | (:conc-name nes/cartridge-header->)) 33 | constant 34 | size-of-prg-rom 35 | size-of-chr-rom 36 | flags6 37 | flags7 38 | size-of-prg-ram 39 | flags9 40 | flags10 41 | zero-pad) 42 | 43 | 44 | (defun nes/cartridge--load-header (filepath) 45 | (let* ((data 46 | (with-temp-buffer 47 | ;; The first 16 bytes are the iNES header 48 | (insert-file-contents-literally filepath nil 0 16) 49 | (buffer-substring-no-properties (point-min) (point-max)))) 50 | (decoded 51 | (bindat-unpack nes/cartridge-ines-header-spec (encode-coding-string data 'raw-text)))) 52 | (make-nes/cartridge-header 53 | :constant (bindat-get-field decoded 'constant) 54 | :size-of-prg-rom (bindat-get-field decoded 'size-of-prg-rom) 55 | :size-of-chr-rom (bindat-get-field decoded 'size-of-chr-rom) 56 | :flags6 (bindat-get-field decoded 'flags6) 57 | :flags7 (bindat-get-field decoded 'flags7) 58 | :size-of-prg-ram (bindat-get-field decoded 'size-of-prg-ram) 59 | :flags9 (bindat-get-field decoded 'flags9) 60 | :flags10 (bindat-get-field decoded 'flags10) 61 | :zero-pad (bindat-get-field decoded 'zero-pad)))) 62 | 63 | (defun nes/cartridge-load (filepath) 64 | (let* ((header 65 | (nes/cartridge--load-header filepath)) 66 | (trainer-exists 67 | (eq (logand (lsh (nes/cartridge-header->flags6 header) -2)) 1)) 68 | (playchoice-exists 69 | (eq (logand (lsh (nes/cartridge-header->flags7 header) -1)) 1)) 70 | (body-spec 71 | `((trainer vec 72 | ,(if trainer-exists 512 0) 73 | u8) 74 | (prg-rom vec 75 | ,(* 16384 (nes/cartridge-header->size-of-prg-rom header)) 76 | u8) 77 | (chr-rom vec 78 | ,(* 8192 (nes/cartridge-header->size-of-chr-rom header)) 79 | u8) 80 | (pc-inst-rom vec 81 | ,(if playchoice-exists 8192 0) 82 | u8) 83 | (pc-prom-data vec 84 | ,(if playchoice-exists 16 0) 85 | u8) 86 | (pc-prom-counter-out-data vec 87 | ,(if playchoice-exists 16 0) 88 | u8) 89 | )) 90 | (body 91 | (with-temp-buffer 92 | (insert-file-contents-literally filepath nil 16) 93 | (buffer-substring-no-properties (point-min) (point-max)))) 94 | (body-decoded 95 | (bindat-unpack body-spec (encode-coding-string body 'raw-text)))) 96 | 97 | (make-nes/cartridge 98 | :header header 99 | :trainer (bindat-get-field body-decoded 'trainer) 100 | :prg-rom (bindat-get-field body-decoded 'prg-rom) 101 | :chr-rom (bindat-get-field body-decoded 'chr-rom) 102 | :pc-inst-rom (bindat-get-field body-decoded 'pc-inst-rom) 103 | :pc-prom-data (bindat-get-field body-decoded 'pc-prom-data) 104 | :pc-prom-counter-out-data (bindat-get-field body-decoded 'pc-prom-counter-out-data)))) 105 | 106 | (defun nes/cartridge-read-from-prg-rom (cart addr) 107 | (let ((rom (nes/cartridge->prg-rom cart))) 108 | (aref rom (mod addr (length rom))))) 109 | 110 | (provide 'nes-cartridge) 111 | -------------------------------------------------------------------------------- /nes-color.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (eval-when-compile (require 'cl)) 4 | (require 'cl-lib) 5 | 6 | (defconst nes/color:COLORS 7 | [ 8 | (#x66 #x66 #x66) 9 | (#x00 #x2A #x88) 10 | (#x14 #x12 #xA7) 11 | (#x3B #x00 #xA4) 12 | (#x5C #x00 #x7E) 13 | (#x6E #x00 #x40) 14 | (#x6C #x06 #x00) 15 | (#x56 #x1D #x00) 16 | (#x33 #x35 #x00) 17 | (#x0B #x48 #x00) 18 | (#x00 #x52 #x00) 19 | (#x00 #x4F #x08) 20 | (#x00 #x40 #x4D) 21 | (#x00 #x00 #x00) 22 | (#x00 #x00 #x00) 23 | (#x00 #x00 #x00) 24 | (#xAD #xAD #xAD) 25 | (#x15 #x5F #xD9) 26 | (#x42 #x40 #xFF) 27 | (#x75 #x27 #xFE) 28 | (#xA0 #x1A #xCC) 29 | (#xB7 #x1E #x7B) 30 | (#xB5 #x31 #x20) 31 | (#x99 #x4E #x00) 32 | (#x6B #x6D #x00) 33 | (#x38 #x87 #x00) 34 | (#x0C #x93 #x00) 35 | (#x00 #x8F #x32) 36 | (#x00 #x7C #x8D) 37 | (#x00 #x00 #x00) 38 | (#x00 #x00 #x00) 39 | (#x00 #x00 #x00) 40 | (#xFF #xFE #xFF) 41 | (#x64 #xB0 #xFF) 42 | (#x92 #x90 #xFF) 43 | (#xC6 #x76 #xFF) 44 | (#xF3 #x6A #xFF) 45 | (#xFE #x6E #xCC) 46 | (#xFE #x81 #x70) 47 | (#xEA #x9E #x22) 48 | (#xBC #xBE #x00) 49 | (#x88 #xD8 #x00) 50 | (#x5C #xE4 #x30) 51 | (#x45 #xE0 #x82) 52 | (#x48 #xCD #xDE) 53 | (#x4F #x4F #x4F) 54 | (#x00 #x00 #x00) 55 | (#x00 #x00 #x00) 56 | (#xFF #xFE #xFF) 57 | (#xC0 #xDF #xFF) 58 | (#xD3 #xD2 #xFF) 59 | (#xE8 #xC8 #xFF) 60 | (#xFB #xC2 #xFF) 61 | (#xFE #xC4 #xEA) 62 | (#xFE #xCC #xC5) 63 | (#xF7 #xD8 #xA5) 64 | (#xE4 #xE5 #x94) 65 | (#xCF #xEF #x96) 66 | (#xBD #xF4 #xAB) 67 | (#xB3 #xF3 #xCC) 68 | (#xB5 #xEB #xF2) 69 | (#xB8 #xB8 #xB8) 70 | (#x00 #x00 #x00) 71 | (#x00 #x00 #x00) 72 | ]) 73 | 74 | (defconst nes/colors 75 | (map 'vector 76 | (lambda (color) 77 | (let ((normalize0 (/ (nth 0 color) 255.0)) 78 | (normalize1 (/ (nth 1 color) 255.0)) 79 | (normalize2 (/ (nth 2 color) 255.0))) 80 | `( 81 | ((glyph colorize) 82 | (t ? )) 83 | 84 | ((color-x color-x) 85 | (color-tty color-tty) 86 | (t mono-tty)) 87 | 88 | (((glyph color-x) [,normalize0 ,normalize1 ,normalize2]) 89 | (color-tty ,(format "#%02X%02X%02X" (nth 0 color) 90 | (nth 1 color) 91 | (nth 2 color))) 92 | ))) 93 | ) 94 | nes/color:COLORS 95 | )) 96 | 97 | (provide 'nes-color) 98 | -------------------------------------------------------------------------------- /nes-cpu.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (eval-when-compile (require 'cl)) 4 | 5 | (require 'nes-dma) 6 | (require 'nes-instruction) 7 | (require 'nes-interrupt) 8 | (require 'nes-keypad) 9 | (require 'nes-ppu) 10 | (require 'nes-util) 11 | 12 | ;; https://wiki.nesdev.com/w/index.php/CPU_registers 13 | (defstruct (nes/cpu-register 14 | (:conc-name nes/cpu-register->)) 15 | (acc 0) 16 | (idx-x 0) 17 | (idx-y 0) 18 | (pc 0) 19 | (sp #x01fd) 20 | 21 | ;; status register 22 | (sr-carry nil) 23 | (sr-zero nil) 24 | (sr-interrupt t) 25 | (sr-decimal nil) 26 | (sr-break t) 27 | (sr-reserved t) 28 | (sr-overflow nil) 29 | (sr-negative nil)) 30 | 31 | (defstruct (nes/cpu-bus 32 | (:conc-name nes/cpu-bus->)) 33 | (ram nil) 34 | (prg-rom (lambda (ignored))) 35 | ) 36 | 37 | (defstruct (nes/cpu 38 | (:conc-name nes/cpu->)) 39 | (cycles 0) 40 | (ppu nil) 41 | (dma nil) 42 | (keypad nil) 43 | (bus (make-nes/cpu-bus)) 44 | (register (make-nes/cpu-register)) 45 | (interrupt nil) 46 | ) 47 | 48 | (defun nes/cpu--bus-read (c addr) 49 | (let ((b (nes/cpu->bus c)) 50 | (ppu (nes/cpu->ppu c))) 51 | (cond 52 | ((< addr #x0800) (aref (nes/cpu-bus->ram b) addr)) 53 | ((< addr #x2000) (nes/cpu--bus-read c (- addr #x0800))) 54 | ((< addr #x4000) (nes/ppu-read ppu (+ (mod addr #x0008) #x2000))) 55 | ((eq addr #x4016) (nes/keypad-read (nes/cpu->keypad c))) 56 | ;; ((eq addr #x4017) (nes/keypad-read (nes/cpu->keypad c))) ;; 2P 57 | ((>= addr #x8000) (funcall (nes/cpu-bus->prg-rom b) addr)) 58 | (t 0) 59 | ))) 60 | 61 | (defun nes/cpu--bus-write (c addr data) 62 | (let ((b (nes/cpu->bus c)) 63 | (ppu (nes/cpu->ppu c))) 64 | (cond 65 | ((< addr #x2000) (aset (nes/cpu-bus->ram b) (% addr #x0800) data)) 66 | ((< addr #x4000) (nes/ppu-write ppu (+ (% addr #x0008) #x2000) data)) 67 | ;; ((< addr #x4014) ( ... )) ;; APU 68 | ((eq addr #x4014) (nes/dma-request-transfer (nes/cpu->dma c) data)) 69 | ((eq addr #x4016) (nes/keypad-write (nes/cpu->keypad c) data)) 70 | ;; ((eq addr #x4017) (nes/keypad-write (nes/cpu->keypad c) data)) ;; 2P 71 | ))) 72 | 73 | (defun nes/cpu-read (c addr &optional size) 74 | (setq size (or size :byte)) 75 | (setq addr (logand addr #xffff)) 76 | (if (eq size :word) 77 | (logior 78 | (logand (nes/cpu--bus-read c addr) #xFF) 79 | (logand (lsh (nes/cpu--bus-read c (1+ addr)) 8) #xFF00) 80 | ) 81 | (logand (nes/cpu--bus-read c addr) #xFF))) 82 | 83 | (defun nes/cpu-write (c addr data) 84 | (nes/cpu--bus-write c addr data)) 85 | 86 | (defun nes/cpu-push (c data) 87 | (let* ((register (nes/cpu->register c)) 88 | (addr (nes/cpu-register->sp register))) 89 | (nes/cpu-write c (logior #x100 (logand addr #x0ff)) data) 90 | (setf (nes/cpu-register->sp register) (1- addr)))) 91 | 92 | (defun nes/cpu-pull (c) 93 | (let* ((register (nes/cpu->register c)) 94 | (addr (1+ (nes/cpu-register->sp register)))) 95 | (setf (nes/cpu-register->sp register) addr) 96 | (nes/cpu-read c (logior #x100 (logand addr #x0ff))))) 97 | 98 | (defun nes/cpu-push-status-register (c) 99 | (let ((r (nes/cpu->register c))) 100 | (nes/cpu-push c 101 | (logior (lsh (if (nes/cpu-register->sr-negative r) 1 0) 7) 102 | (lsh (if (nes/cpu-register->sr-overflow r) 1 0) 6) 103 | (lsh (if (nes/cpu-register->sr-reserved r) 1 0) 5) 104 | (lsh (if (nes/cpu-register->sr-break r) 1 0) 4) 105 | (lsh (if (nes/cpu-register->sr-decimal r) 1 0) 3) 106 | (lsh (if (nes/cpu-register->sr-interrupt r) 1 0) 2) 107 | (lsh (if (nes/cpu-register->sr-zero r) 1 0) 1) 108 | (lsh (if (nes/cpu-register->sr-carry r) 1 0) 0))))) 109 | 110 | (defun nes/cpu-pull-status-register (c) 111 | (let ((data (nes/cpu-pull c)) 112 | (r (nes/cpu->register c))) 113 | (setf (nes/cpu-register->sr-negative r) (nes--logbitp 7 data)) 114 | (setf (nes/cpu-register->sr-overflow r) (nes--logbitp 6 data)) 115 | (setf (nes/cpu-register->sr-reserved r) (nes--logbitp 5 data)) 116 | (setf (nes/cpu-register->sr-break r) (nes--logbitp 4 data)) 117 | (setf (nes/cpu-register->sr-decimal r) (nes--logbitp 3 data)) 118 | (setf (nes/cpu-register->sr-interrupt r) (nes--logbitp 2 data)) 119 | (setf (nes/cpu-register->sr-zero r) (nes--logbitp 1 data)) 120 | (setf (nes/cpu-register->sr-carry r) (nes--logbitp 0 data)))) 121 | 122 | 123 | (defun nes/cpu-reset (c) 124 | (let ((r (nes/cpu->register c))) 125 | (setf (nes/cpu-register->sp r) #xfd) 126 | (setf (nes/cpu-register->pc r) (nes/cpu-read c #xfffc :word)) 127 | (setf (nes/cpu-register->sr-carry r) nil) 128 | (setf (nes/cpu-register->sr-zero r) nil) 129 | (setf (nes/cpu-register->sr-interrupt r) t) 130 | (setf (nes/cpu-register->sr-decimal r) nil) 131 | (setf (nes/cpu-register->sr-break r) t) 132 | (setf (nes/cpu-register->sr-reserved r) t) 133 | (setf (nes/cpu-register->sr-overflow r) nil) 134 | (setf (nes/cpu-register->sr-negative r) nil) 135 | )) 136 | 137 | (defun nes/cpu-nmi (c) 138 | (let ((register (nes/cpu->register c))) 139 | (nes/interrupt-deassert-nmi (nes/cpu->interrupt c)) 140 | (setf (nes/cpu-register->sr-break register) nil) 141 | (nes/cpu-push c (logand #xFF (lsh (nes/cpu-register->pc register) -8))) 142 | (nes/cpu-push c (logand #xFF (nes/cpu-register->pc register))) 143 | (nes/cpu-push-status-register c) 144 | (setf (nes/cpu-register->sr-interrupt register) t) 145 | (setf (nes/cpu-register->pc register) (nes/cpu-read c #xFFFA :word)))) 146 | 147 | (defun nes/cpu-irq (c) 148 | (let ((register (nes/cpu->register c))) 149 | (nes/cpu-push c (logand #xff (lsh (nes/cpu-register->pc register) -8))) 150 | (nes/cpu-push c (logand #xff (nes/cpu-register->pc register))) 151 | (nes/cpu-push-status-register c) 152 | (setf (nes/cpu-register->sr-interrupt register) t) 153 | (setf (nes/cpu-register->pc register) (nes/cpu-read c #xfffe :word)))) 154 | 155 | (defun nes/cpu--get-instruction-operand-and-cycle (c mode) 156 | (let ((register (nes/cpu->register c))) 157 | (case mode 158 | (:accumulator 159 | '(nil . 0)) 160 | 161 | (:implied 162 | '(nil . 0)) 163 | 164 | (:immediate 165 | (cons (nes/cpu--fetch c) 0)) 166 | 167 | (:relative 168 | (let* ((base-addr (nes/cpu--fetch c)) 169 | (addr (if (< base-addr #x80) 170 | (+ base-addr (nes/cpu-register->pc register)) 171 | (- (+ base-addr (nes/cpu-register->pc register)) 256))) 172 | (cycle (if (not (eq (logand addr #xFF00) (logand (nes/cpu-register->pc register) #xFF00))) 1 0))) 173 | (cons addr cycle))) 174 | 175 | (:zero-page 176 | (cons (nes/cpu--fetch c) 0)) 177 | 178 | (:zero-page-x 179 | (cons (logand (+ (nes/cpu--fetch c) 180 | (nes/cpu-register->idx-x register)) 181 | #xFF) 182 | 0)) 183 | 184 | (:zero-page-y 185 | (cons (logand (+ (nes/cpu--fetch c) 186 | (nes/cpu-register->idx-y register)) 187 | #xFF) 188 | 0)) 189 | 190 | (:absolute 191 | (cons (nes/cpu--fetch c :word) 0)) 192 | 193 | (:absolute-x 194 | (let* ((addr (nes/cpu--fetch c :word)) 195 | (idx-x (nes/cpu-register->idx-x register)) 196 | (cycle (if (eq (logand addr #xFF00) (logand (+ addr idx-x) #xFF00)) 197 | 0 1))) 198 | (cons (logand (+ addr idx-x) #xFFFF) cycle))) 199 | 200 | (:absolute-y 201 | (let* ((addr (nes/cpu--fetch c :word)) 202 | (idx-y (nes/cpu-register->idx-y register)) 203 | (cycle (if (eq (logand addr #xFF00) (logand (+ addr idx-y) #xFF00)) 204 | 0 1))) 205 | (cons (logand (+ addr idx-y) #xFFFF) cycle))) 206 | 207 | (:pre-indexed-indirect 208 | (let* ((base-addr (logand (+ (nes/cpu--fetch c) (nes/cpu-register->idx-x register)) 209 | #xFF)) 210 | (addr (logand (+ (nes/cpu-read c base-addr) 211 | (lsh (nes/cpu-read c (logand (1+ base-addr) #xFF)) 8)) 212 | #xFFFF)) 213 | (cycle (if (/= (logand addr #xFF00) (logand base-addr #xFF00)) 1 0))) 214 | (cons addr cycle))) 215 | 216 | (:post-indexed-indirect 217 | (let* ((data-addr (nes/cpu--fetch c)) 218 | (base-addr (+ (nes/cpu-read c data-addr) 219 | (lsh (nes/cpu-read c (logand (1+ data-addr) #xFF)) 8))) 220 | (addr (logand (+ base-addr (nes/cpu-register->idx-y register)) #xFFFF)) 221 | (cycle (if (/= (logand addr #xFF00) (logand base-addr #xFF00)) 1 0))) 222 | (cons addr cycle))) 223 | 224 | (:indirect-absolute 225 | (let ((data-addr (nes/cpu--fetch c :word))) 226 | (cons (logand (+ (nes/cpu-read c data-addr) 227 | (lsh (nes/cpu-read c (logior (logand data-addr #xFF00) 228 | (logand (1+ (logand data-addr #xFF)) #xFF))) 229 | 8)) 230 | #xFFFF) 231 | 0)))))) 232 | 233 | (defun nes/cpu--fetch (c &optional size) 234 | (let ((size (or size :byte)) 235 | (addr (nes/cpu-register->pc (nes/cpu->register c)))) 236 | (incf (nes/cpu-register->pc (nes/cpu->register c)) (if (eq size :word) 2 1)) 237 | (nes/cpu-read c addr size))) 238 | 239 | (defun nes/cpu-set-working-ram (cpu ram) 240 | (setf (nes/cpu-bus->ram (nes/cpu->bus cpu)) ram)) 241 | 242 | (defun nes/cpu-set-program-rom (cpu rom-func) 243 | (setf (nes/cpu-bus->prg-rom (nes/cpu->bus cpu)) rom-func)) 244 | 245 | (defun nes/cpu-step (c) 246 | (when (nes/interrupt->nmi (nes/cpu->interrupt c)) 247 | (nes/cpu-nmi c)) 248 | (nes/interrupt-clear (nes/cpu->interrupt c)) 249 | (let* ((opcode (nes/cpu--fetch c)) 250 | (inst (aref nes/instruction:MAP opcode)) 251 | (operand-and-cycle (nes/cpu--get-instruction-operand-and-cycle c (nes/instruction->mode inst))) 252 | (operand (car operand-and-cycle)) 253 | (inst-cycle (aref nes/instruction:CYCLES opcode)) 254 | (add-cycle (cdr operand-and-cycle))) 255 | (setf (nes/cpu->cycles c) 0) 256 | (funcall (nes/instruction->func inst) c operand (nes/instruction->mode inst)) 257 | (+ (nes/cpu->cycles c) inst-cycle add-cycle) 258 | )) 259 | 260 | (defun nes/cpu-init (c) 261 | (nes/cpu-reset c)) 262 | 263 | (provide 'nes-cpu) 264 | -------------------------------------------------------------------------------- /nes-dma.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (eval-when-compile (require 'cl)) 4 | 5 | (require 'nes-ppu) 6 | 7 | (defconst nes/dma:TRANSFER-BYTESIZE #x0100) 8 | 9 | (defstruct (nes/dma 10 | (:conc-name nes/dma->)) 11 | (ppu nil) 12 | (ram nil) 13 | (ram-addr #x0000) 14 | (processing nil)) 15 | 16 | (defun nes/dma-processing-p (dma) 17 | (nes/dma->processing dma)) 18 | 19 | (defun nes/dma-request-transfer (dma address) 20 | (setf (nes/dma->processing dma) t) 21 | (setf (nes/dma->ram-addr dma) (lsh address 8))) 22 | 23 | (defun nes/dma-transfer (dma) 24 | (when (nes/dma-processing-p dma) 25 | (dotimes (i nes/dma:TRANSFER-BYTESIZE) 26 | (nes/ppu-transfer-sprite-data (nes/dma->ppu dma) 27 | i 28 | (aref (nes/dma->ram dma) (+ (nes/dma->ram-addr dma) i)))) 29 | (setf (nes/dma->processing dma) nil))) 30 | 31 | (provide 'nes-dma) 32 | -------------------------------------------------------------------------------- /nes-instruction.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (eval-when-compile (require 'cl)) 4 | 5 | (defconst nes/instruction:CYCLES 6 | [7 6 2 8 3 3 5 5 3 2 2 2 4 4 6 6 ;; 0x00 7 | 2 5 2 8 4 4 6 6 2 4 2 7 4 4 6 7 ;; 0x10 8 | 6 6 2 8 3 3 5 5 4 2 2 2 4 4 6 6 ;; 0x20 9 | 2 5 2 8 4 4 6 6 2 4 2 7 4 4 6 7 ;; 0x30 10 | 6 6 2 8 3 3 5 5 3 2 2 2 3 4 6 6 ;; 0x40 11 | 2 5 2 8 4 4 6 6 2 4 2 7 4 4 6 7 ;; 0x50 12 | 6 6 2 8 3 3 5 5 4 2 2 2 5 4 6 6 ;; 0x60 13 | 2 5 2 8 4 4 6 6 2 4 2 7 4 4 6 7 ;; 0x70 14 | 2 6 2 6 3 3 3 3 2 2 2 2 4 4 4 4 ;; 0x80 15 | 2 6 2 6 4 4 4 4 2 4 2 5 5 4 5 5 ;; 0x90 16 | 2 6 2 6 3 3 3 3 2 2 2 2 4 4 4 4 ;; 0xA0 17 | 2 5 2 5 4 4 4 4 2 4 2 4 4 4 4 4 ;; 0xB0 18 | 2 6 2 8 3 3 5 5 2 2 2 2 4 4 6 6 ;; 0xC0 19 | 2 5 2 8 4 4 6 6 2 4 2 7 4 4 7 7 ;; 0xD0 20 | 2 6 3 8 3 3 5 5 2 2 2 2 4 4 6 6 ;; 0xE0 21 | 2 5 2 8 4 4 6 6 2 4 2 7 4 4 7 7 ;; 0xF0 22 | ] 23 | ) 24 | 25 | (defconst nes/instruction:MAP (make-vector 256 nil)) 26 | 27 | (defstruct (nes/instruction 28 | (:conc-name nes/instruction->)) 29 | name 30 | func 31 | mode 32 | cycle) 33 | 34 | 35 | (aset nes/instruction:MAP #xA9 (make-nes/instruction :func #'nes/instruction-lda :name "LDA" :mode :immediate)) 36 | (aset nes/instruction:MAP #xA5 (make-nes/instruction :func #'nes/instruction-lda :name "LDA" :mode :zero-page)) 37 | (aset nes/instruction:MAP #xAD (make-nes/instruction :func #'nes/instruction-lda :name "LDA" :mode :absolute)) 38 | (aset nes/instruction:MAP #xB5 (make-nes/instruction :func #'nes/instruction-lda :name "LDA" :mode :zero-page-x)) 39 | (aset nes/instruction:MAP #xBD (make-nes/instruction :func #'nes/instruction-lda :name "LDA" :mode :absolute-x)) 40 | (aset nes/instruction:MAP #xB9 (make-nes/instruction :func #'nes/instruction-lda :name "LDA" :mode :absolute-y)) 41 | (aset nes/instruction:MAP #xA1 (make-nes/instruction :func #'nes/instruction-lda :name "LDA" :mode :pre-indexed-indirect)) 42 | (aset nes/instruction:MAP #xB1 (make-nes/instruction :func #'nes/instruction-lda :name "LDA" :mode :post-indexed-indirect)) 43 | (aset nes/instruction:MAP #xA2 (make-nes/instruction :func #'nes/instruction-ldx :name "LDX" :mode :immediate)) 44 | (aset nes/instruction:MAP #xA6 (make-nes/instruction :func #'nes/instruction-ldx :name "LDX" :mode :zero-page)) 45 | (aset nes/instruction:MAP #xAE (make-nes/instruction :func #'nes/instruction-ldx :name "LDX" :mode :absolute)) 46 | (aset nes/instruction:MAP #xB6 (make-nes/instruction :func #'nes/instruction-ldx :name "LDX" :mode :zero-page-y)) 47 | (aset nes/instruction:MAP #xBE (make-nes/instruction :func #'nes/instruction-ldx :name "LDX" :mode :absolute-y)) 48 | (aset nes/instruction:MAP #xA0 (make-nes/instruction :func #'nes/instruction-ldy :name "LDY" :mode :immediate)) 49 | (aset nes/instruction:MAP #xA4 (make-nes/instruction :func #'nes/instruction-ldy :name "LDY" :mode :zero-page)) 50 | (aset nes/instruction:MAP #xAC (make-nes/instruction :func #'nes/instruction-ldy :name "LDY" :mode :absolute)) 51 | (aset nes/instruction:MAP #xB4 (make-nes/instruction :func #'nes/instruction-ldy :name "LDY" :mode :zero-page-x)) 52 | (aset nes/instruction:MAP #xBC (make-nes/instruction :func #'nes/instruction-ldy :name "LDY" :mode :absolute-x)) 53 | 54 | (aset nes/instruction:MAP #x85 (make-nes/instruction :func #'nes/instruction-sta :name "STA" :mode :zero-page)) 55 | (aset nes/instruction:MAP #x8D (make-nes/instruction :func #'nes/instruction-sta :name "STA" :mode :absolute)) 56 | (aset nes/instruction:MAP #x95 (make-nes/instruction :func #'nes/instruction-sta :name "STA" :mode :zero-page-x)) 57 | (aset nes/instruction:MAP #x9D (make-nes/instruction :func #'nes/instruction-sta :name "STA" :mode :absolute-x)) 58 | (aset nes/instruction:MAP #x99 (make-nes/instruction :func #'nes/instruction-sta :name "STA" :mode :absolute-y)) 59 | (aset nes/instruction:MAP #x81 (make-nes/instruction :func #'nes/instruction-sta :name "STA" :mode :pre-indexed-indirect)) 60 | (aset nes/instruction:MAP #x91 (make-nes/instruction :func #'nes/instruction-sta :name "STA" :mode :post-indexed-indirect)) 61 | 62 | (aset nes/instruction:MAP #x86 (make-nes/instruction :func #'nes/instruction-stx :name "STX" :mode :zero-page)) 63 | (aset nes/instruction:MAP #x8E (make-nes/instruction :func #'nes/instruction-stx :name "STX" :mode :absolute)) 64 | (aset nes/instruction:MAP #x96 (make-nes/instruction :func #'nes/instruction-stx :name "STX" :mode :zero-page-y)) 65 | 66 | (aset nes/instruction:MAP #x84 (make-nes/instruction :func #'nes/instruction-sty :name "STY" :mode :zero-page)) 67 | (aset nes/instruction:MAP #x8C (make-nes/instruction :func #'nes/instruction-sty :name "STY" :mode :absolute)) 68 | (aset nes/instruction:MAP #x94 (make-nes/instruction :func #'nes/instruction-sty :name "STY" :mode :zero-page-x)) 69 | 70 | (aset nes/instruction:MAP #x8A (make-nes/instruction :func #'nes/instruction-txa :name "TXA" :mode :implied)) 71 | (aset nes/instruction:MAP #x98 (make-nes/instruction :func #'nes/instruction-tya :name "TYA" :mode :implied)) 72 | (aset nes/instruction:MAP #x9A (make-nes/instruction :func #'nes/instruction-txs :name "TXS" :mode :implied)) 73 | (aset nes/instruction:MAP #xA8 (make-nes/instruction :func #'nes/instruction-tay :name "TAY" :mode :implied)) 74 | (aset nes/instruction:MAP #xAA (make-nes/instruction :func #'nes/instruction-tax :name "TAX" :mode :implied)) 75 | (aset nes/instruction:MAP #xBA (make-nes/instruction :func #'nes/instruction-tsx :name "TSX" :mode :implied)) 76 | 77 | (aset nes/instruction:MAP #x08 (make-nes/instruction :func #'nes/instruction-php :name "PHP" :mode :implied)) 78 | (aset nes/instruction:MAP #x28 (make-nes/instruction :func #'nes/instruction-plp :name "PLP" :mode :implied)) 79 | (aset nes/instruction:MAP #x48 (make-nes/instruction :func #'nes/instruction-pha :name "PHA" :mode :implied)) 80 | (aset nes/instruction:MAP #x68 (make-nes/instruction :func #'nes/instruction-pla :name "PLA" :mode :implied)) 81 | 82 | (aset nes/instruction:MAP #x69 (make-nes/instruction :func #'nes/instruction-adc :name "ADC" :mode :immediate)) 83 | (aset nes/instruction:MAP #x65 (make-nes/instruction :func #'nes/instruction-adc :name "ADC" :mode :zero-page)) 84 | (aset nes/instruction:MAP #x6D (make-nes/instruction :func #'nes/instruction-adc :name "ADC" :mode :absolute)) 85 | (aset nes/instruction:MAP #x75 (make-nes/instruction :func #'nes/instruction-adc :name "ADC" :mode :zero-page-x)) 86 | (aset nes/instruction:MAP #x7D (make-nes/instruction :func #'nes/instruction-adc :name "ADC" :mode :absolute-x)) 87 | (aset nes/instruction:MAP #x79 (make-nes/instruction :func #'nes/instruction-adc :name "ADC" :mode :absolute-y)) 88 | (aset nes/instruction:MAP #x61 (make-nes/instruction :func #'nes/instruction-adc :name "ADC" :mode :pre-indexed-indirect)) 89 | (aset nes/instruction:MAP #x71 (make-nes/instruction :func #'nes/instruction-adc :name "ADC" :mode :post-indexed-indirect)) 90 | 91 | (aset nes/instruction:MAP #xE9 (make-nes/instruction :func #'nes/instruction-sbc :name "SBC" :mode :immediate)) 92 | (aset nes/instruction:MAP #xE5 (make-nes/instruction :func #'nes/instruction-sbc :name "SBC" :mode :zero-page)) 93 | (aset nes/instruction:MAP #xED (make-nes/instruction :func #'nes/instruction-sbc :name "SBC" :mode :absolute)) 94 | (aset nes/instruction:MAP #xF5 (make-nes/instruction :func #'nes/instruction-sbc :name "SBC" :mode :zero-page-x)) 95 | (aset nes/instruction:MAP #xFD (make-nes/instruction :func #'nes/instruction-sbc :name "SBC" :mode :absolute-x)) 96 | (aset nes/instruction:MAP #xF9 (make-nes/instruction :func #'nes/instruction-sbc :name "SBC" :mode :absolute-y)) 97 | (aset nes/instruction:MAP #xE1 (make-nes/instruction :func #'nes/instruction-sbc :name "SBC" :mode :pre-indexed-indirect)) 98 | (aset nes/instruction:MAP #xF1 (make-nes/instruction :func #'nes/instruction-sbc :name "SBC" :mode :post-indexed-indirect)) 99 | 100 | (aset nes/instruction:MAP #xE0 (make-nes/instruction :func #'nes/instruction-cpx :name "CPX" :mode :immediate)) 101 | (aset nes/instruction:MAP #xE4 (make-nes/instruction :func #'nes/instruction-cpx :name "CPX" :mode :zero-page)) 102 | (aset nes/instruction:MAP #xEC (make-nes/instruction :func #'nes/instruction-cpx :name "CPX" :mode :absolute)) 103 | (aset nes/instruction:MAP #xC0 (make-nes/instruction :func #'nes/instruction-cpy :name "CPY" :mode :immediate)) 104 | (aset nes/instruction:MAP #xC4 (make-nes/instruction :func #'nes/instruction-cpy :name "CPY" :mode :zero-page)) 105 | (aset nes/instruction:MAP #xCC (make-nes/instruction :func #'nes/instruction-cpy :name "CPY" :mode :absolute)) 106 | 107 | (aset nes/instruction:MAP #xC9 (make-nes/instruction :func #'nes/instruction-cmp :name "CMP" :mode :immediate)) 108 | (aset nes/instruction:MAP #xC5 (make-nes/instruction :func #'nes/instruction-cmp :name "CMP" :mode :zero-page)) 109 | (aset nes/instruction:MAP #xCD (make-nes/instruction :func #'nes/instruction-cmp :name "CMP" :mode :absolute)) 110 | (aset nes/instruction:MAP #xD5 (make-nes/instruction :func #'nes/instruction-cmp :name "CMP" :mode :zero-page-x)) 111 | (aset nes/instruction:MAP #xDD (make-nes/instruction :func #'nes/instruction-cmp :name "CMP" :mode :absolute-x)) 112 | (aset nes/instruction:MAP #xD9 (make-nes/instruction :func #'nes/instruction-cmp :name "CMP" :mode :absolute-y)) 113 | (aset nes/instruction:MAP #xC1 (make-nes/instruction :func #'nes/instruction-cmp :name "CMP" :mode :pre-indexed-indirect)) 114 | (aset nes/instruction:MAP #xD1 (make-nes/instruction :func #'nes/instruction-cmp :name "CMP" :mode :post-indexed-indirect)) 115 | 116 | (aset nes/instruction:MAP #x29 (make-nes/instruction :func #'nes/instruction-and :name "AND" :mode :immediate)) 117 | (aset nes/instruction:MAP #x25 (make-nes/instruction :func #'nes/instruction-and :name "AND" :mode :zero-page)) 118 | (aset nes/instruction:MAP #x2D (make-nes/instruction :func #'nes/instruction-and :name "AND" :mode :absolute)) 119 | (aset nes/instruction:MAP #x35 (make-nes/instruction :func #'nes/instruction-and :name "AND" :mode :zero-page-x)) 120 | (aset nes/instruction:MAP #x3D (make-nes/instruction :func #'nes/instruction-and :name "AND" :mode :absolute-x)) 121 | (aset nes/instruction:MAP #x39 (make-nes/instruction :func #'nes/instruction-and :name "AND" :mode :absolute-y)) 122 | (aset nes/instruction:MAP #x21 (make-nes/instruction :func #'nes/instruction-and :name "AND" :mode :pre-indexed-indirect)) 123 | (aset nes/instruction:MAP #x31 (make-nes/instruction :func #'nes/instruction-and :name "AND" :mode :post-indexed-indirect)) 124 | (aset nes/instruction:MAP #x49 (make-nes/instruction :func #'nes/instruction-eor :name "EOR" :mode :immediate)) 125 | (aset nes/instruction:MAP #x45 (make-nes/instruction :func #'nes/instruction-eor :name "EOR" :mode :zero-page)) 126 | (aset nes/instruction:MAP #x4D (make-nes/instruction :func #'nes/instruction-eor :name "EOR" :mode :absolute)) 127 | (aset nes/instruction:MAP #x55 (make-nes/instruction :func #'nes/instruction-eor :name "EOR" :mode :zero-page-x)) 128 | (aset nes/instruction:MAP #x5D (make-nes/instruction :func #'nes/instruction-eor :name "EOR" :mode :absolute-x)) 129 | (aset nes/instruction:MAP #x59 (make-nes/instruction :func #'nes/instruction-eor :name "EOR" :mode :absolute-y)) 130 | (aset nes/instruction:MAP #x41 (make-nes/instruction :func #'nes/instruction-eor :name "EOR" :mode :pre-indexed-indirect)) 131 | (aset nes/instruction:MAP #x51 (make-nes/instruction :func #'nes/instruction-eor :name "EOR" :mode :post-indexed-indirect)) 132 | (aset nes/instruction:MAP #x09 (make-nes/instruction :func #'nes/instruction-ora :name "ORA" :mode :immediate)) 133 | (aset nes/instruction:MAP #x05 (make-nes/instruction :func #'nes/instruction-ora :name "ORA" :mode :zero-page)) 134 | (aset nes/instruction:MAP #x0D (make-nes/instruction :func #'nes/instruction-ora :name "ORA" :mode :absolute)) 135 | (aset nes/instruction:MAP #x15 (make-nes/instruction :func #'nes/instruction-ora :name "ORA" :mode :zero-page-x)) 136 | (aset nes/instruction:MAP #x1D (make-nes/instruction :func #'nes/instruction-ora :name "ORA" :mode :absolute-x)) 137 | (aset nes/instruction:MAP #x19 (make-nes/instruction :func #'nes/instruction-ora :name "ORA" :mode :absolute-y)) 138 | (aset nes/instruction:MAP #x01 (make-nes/instruction :func #'nes/instruction-ora :name "ORA" :mode :pre-indexed-indirect)) 139 | (aset nes/instruction:MAP #x11 (make-nes/instruction :func #'nes/instruction-ora :name "ORA" :mode :post-indexed-indirect)) 140 | (aset nes/instruction:MAP #x24 (make-nes/instruction :func #'nes/instruction-bit :name "BIT" :mode :zero-page)) 141 | (aset nes/instruction:MAP #x2C (make-nes/instruction :func #'nes/instruction-bit :name "BIT" :mode :absolute)) 142 | (aset nes/instruction:MAP #x0A (make-nes/instruction :func #'nes/instruction-asl :name "ASL" :mode :accumulator)) 143 | (aset nes/instruction:MAP #x06 (make-nes/instruction :func #'nes/instruction-asl :name "ASL" :mode :zero-page)) 144 | (aset nes/instruction:MAP #x0E (make-nes/instruction :func #'nes/instruction-asl :name "ASL" :mode :absolute)) 145 | (aset nes/instruction:MAP #x16 (make-nes/instruction :func #'nes/instruction-asl :name "ASL" :mode :zero-page-x)) 146 | (aset nes/instruction:MAP #x1E (make-nes/instruction :func #'nes/instruction-asl :name "ASL" :mode :absolute-x)) 147 | (aset nes/instruction:MAP #x4A (make-nes/instruction :func #'nes/instruction-lsr :name "LSR" :mode :accumulator)) 148 | (aset nes/instruction:MAP #x46 (make-nes/instruction :func #'nes/instruction-lsr :name "LSR" :mode :zero-page)) 149 | (aset nes/instruction:MAP #x4E (make-nes/instruction :func #'nes/instruction-lsr :name "LSR" :mode :absolute)) 150 | (aset nes/instruction:MAP #x56 (make-nes/instruction :func #'nes/instruction-lsr :name "LSR" :mode :zero-page-x)) 151 | (aset nes/instruction:MAP #x5E (make-nes/instruction :func #'nes/instruction-lsr :name "LSR" :mode :absolute-x)) 152 | (aset nes/instruction:MAP #x2A (make-nes/instruction :func #'nes/instruction-rol :name "ROL" :mode :accumulator)) 153 | (aset nes/instruction:MAP #x26 (make-nes/instruction :func #'nes/instruction-rol :name "ROL" :mode :zero-page)) 154 | (aset nes/instruction:MAP #x2E (make-nes/instruction :func #'nes/instruction-rol :name "ROL" :mode :absolute)) 155 | (aset nes/instruction:MAP #x36 (make-nes/instruction :func #'nes/instruction-rol :name "ROL" :mode :zero-page-x)) 156 | (aset nes/instruction:MAP #x3E (make-nes/instruction :func #'nes/instruction-rol :name "ROL" :mode :absolute-x)) 157 | (aset nes/instruction:MAP #x6A (make-nes/instruction :func #'nes/instruction-ror :name "ROR" :mode :accumulator)) 158 | (aset nes/instruction:MAP #x66 (make-nes/instruction :func #'nes/instruction-ror :name "ROR" :mode :zero-page)) 159 | (aset nes/instruction:MAP #x6E (make-nes/instruction :func #'nes/instruction-ror :name "ROR" :mode :absolute)) 160 | (aset nes/instruction:MAP #x76 (make-nes/instruction :func #'nes/instruction-ror :name "ROR" :mode :zero-page-x)) 161 | (aset nes/instruction:MAP #x7E (make-nes/instruction :func #'nes/instruction-ror :name "ROR" :mode :absolute-x)) 162 | (aset nes/instruction:MAP #xE8 (make-nes/instruction :func #'nes/instruction-inx :name "INX" :mode :implied)) 163 | (aset nes/instruction:MAP #xC8 (make-nes/instruction :func #'nes/instruction-iny :name "INY" :mode :implied)) 164 | (aset nes/instruction:MAP #xE6 (make-nes/instruction :func #'nes/instruction-inc :name "INC" :mode :zero-page)) 165 | (aset nes/instruction:MAP #xEE (make-nes/instruction :func #'nes/instruction-inc :name "INC" :mode :absolute)) 166 | (aset nes/instruction:MAP #xF6 (make-nes/instruction :func #'nes/instruction-inc :name "INC" :mode :zero-page-x)) 167 | (aset nes/instruction:MAP #xFE (make-nes/instruction :func #'nes/instruction-inc :name "INC" :mode :absolute-x)) 168 | (aset nes/instruction:MAP #xCA (make-nes/instruction :func #'nes/instruction-dex :name "DEX" :mode :implied)) 169 | (aset nes/instruction:MAP #x88 (make-nes/instruction :func #'nes/instruction-dey :name "DEY" :mode :implied)) 170 | (aset nes/instruction:MAP #xC6 (make-nes/instruction :func #'nes/instruction-dec :name "DEC" :mode :zero-page)) 171 | (aset nes/instruction:MAP #xCE (make-nes/instruction :func #'nes/instruction-dec :name "DEC" :mode :absolute)) 172 | (aset nes/instruction:MAP #xD6 (make-nes/instruction :func #'nes/instruction-dec :name "DEC" :mode :zero-page-x)) 173 | (aset nes/instruction:MAP #xDE (make-nes/instruction :func #'nes/instruction-dec :name "DEC" :mode :absolute-x)) 174 | (aset nes/instruction:MAP #x18 (make-nes/instruction :func #'nes/instruction-clc :name "CLC" :mode :implied)) 175 | (aset nes/instruction:MAP #x58 (make-nes/instruction :func #'nes/instruction-cli :name "CLI" :mode :implied)) 176 | (aset nes/instruction:MAP #xB8 (make-nes/instruction :func #'nes/instruction-clv :name "CLV" :mode :implied)) 177 | (aset nes/instruction:MAP #x38 (make-nes/instruction :func #'nes/instruction-sec :name "SEC" :mode :implied)) 178 | (aset nes/instruction:MAP #x78 (make-nes/instruction :func #'nes/instruction-sei :name "SEI" :mode :implied)) 179 | (aset nes/instruction:MAP #xEA (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 180 | 181 | (aset nes/instruction:MAP #x00 (make-nes/instruction :func #'nes/instruction-brk :name "BRK" :mode :implied)) 182 | 183 | (aset nes/instruction:MAP #x20 (make-nes/instruction :func #'nes/instruction-jsr :name "JSR" :mode :absolute)) 184 | (aset nes/instruction:MAP #x4C (make-nes/instruction :func #'nes/instruction-jmp :name "JMP" :mode :absolute)) 185 | (aset nes/instruction:MAP #x6C (make-nes/instruction :func #'nes/instruction-jmp :name "JMP" :mode :indirect-absolute)) 186 | (aset nes/instruction:MAP #x40 (make-nes/instruction :func #'nes/instruction-rti :name "RTI" :mode :implied)) 187 | (aset nes/instruction:MAP #x60 (make-nes/instruction :func #'nes/instruction-rts :name "RTS" :mode :implied)) 188 | (aset nes/instruction:MAP #x10 (make-nes/instruction :func #'nes/instruction-bpl :name "BPL" :mode :relative)) 189 | (aset nes/instruction:MAP #x30 (make-nes/instruction :func #'nes/instruction-bmi :name "BMI" :mode :relative)) 190 | (aset nes/instruction:MAP #x50 (make-nes/instruction :func #'nes/instruction-bvc :name "BVC" :mode :relative)) 191 | (aset nes/instruction:MAP #x70 (make-nes/instruction :func #'nes/instruction-bvs :name "BVS" :mode :relative)) 192 | (aset nes/instruction:MAP #x90 (make-nes/instruction :func #'nes/instruction-bcc :name "BCC" :mode :relative)) 193 | (aset nes/instruction:MAP #xB0 (make-nes/instruction :func #'nes/instruction-bcs :name "BCS" :mode :relative)) 194 | (aset nes/instruction:MAP #xD0 (make-nes/instruction :func #'nes/instruction-bne :name "BNE" :mode :relative)) 195 | (aset nes/instruction:MAP #xF0 (make-nes/instruction :func #'nes/instruction-beq :name "BEQ" :mode :relative)) 196 | (aset nes/instruction:MAP #xF8 (make-nes/instruction :func #'nes/instruction-sed :name "SED" :mode :implied)) 197 | (aset nes/instruction:MAP #xD8 (make-nes/instruction :func #'nes/instruction-cld :name "CLD" :mode :implied)) 198 | 199 | ;; Unofficial Opecodes 200 | 201 | (aset nes/instruction:MAP #x02 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 202 | (aset nes/instruction:MAP #x12 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 203 | (aset nes/instruction:MAP #x1A (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 204 | (aset nes/instruction:MAP #x22 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 205 | (aset nes/instruction:MAP #x32 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 206 | (aset nes/instruction:MAP #x3A (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 207 | (aset nes/instruction:MAP #x42 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 208 | (aset nes/instruction:MAP #x52 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 209 | (aset nes/instruction:MAP #x5A (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 210 | (aset nes/instruction:MAP #x62 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 211 | (aset nes/instruction:MAP #x72 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 212 | (aset nes/instruction:MAP #x7A (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 213 | (aset nes/instruction:MAP #x92 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 214 | (aset nes/instruction:MAP #xB2 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 215 | (aset nes/instruction:MAP #xD2 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 216 | (aset nes/instruction:MAP #xDA (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 217 | (aset nes/instruction:MAP #xF2 (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 218 | (aset nes/instruction:MAP #xFA (make-nes/instruction :func #'nes/instruction-nop :name "NOP" :mode :implied)) 219 | (aset nes/instruction:MAP #x04 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 220 | (aset nes/instruction:MAP #x14 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 221 | (aset nes/instruction:MAP #x34 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 222 | (aset nes/instruction:MAP #x44 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 223 | (aset nes/instruction:MAP #x54 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 224 | (aset nes/instruction:MAP #x64 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 225 | (aset nes/instruction:MAP #x74 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 226 | (aset nes/instruction:MAP #x80 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 227 | (aset nes/instruction:MAP #x82 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 228 | (aset nes/instruction:MAP #x89 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 229 | (aset nes/instruction:MAP #xC2 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 230 | (aset nes/instruction:MAP #xD4 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 231 | (aset nes/instruction:MAP #xE2 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 232 | (aset nes/instruction:MAP #xF4 (make-nes/instruction :func #'nes/instruction-nopd :name "NOPD" :mode :implied)) 233 | (aset nes/instruction:MAP #x0C (make-nes/instruction :func #'nes/instruction-nopi :name "NOPI" :mode :implied)) 234 | (aset nes/instruction:MAP #x1C (make-nes/instruction :func #'nes/instruction-nopi :name "NOPI" :mode :implied)) 235 | (aset nes/instruction:MAP #x3C (make-nes/instruction :func #'nes/instruction-nopi :name "NOPI" :mode :implied)) 236 | (aset nes/instruction:MAP #x5C (make-nes/instruction :func #'nes/instruction-nopi :name "NOPI" :mode :implied)) 237 | (aset nes/instruction:MAP #x7C (make-nes/instruction :func #'nes/instruction-nopi :name "NOPI" :mode :implied)) 238 | (aset nes/instruction:MAP #xDC (make-nes/instruction :func #'nes/instruction-nopi :name "NOPI" :mode :implied)) 239 | (aset nes/instruction:MAP #xFC (make-nes/instruction :func #'nes/instruction-nopi :name "NOPI" :mode :implied)) 240 | (aset nes/instruction:MAP #xA7 (make-nes/instruction :func #'nes/instruction-lax :name "LAX" :mode :zero-page)) 241 | (aset nes/instruction:MAP #xB7 (make-nes/instruction :func #'nes/instruction-lax :name "LAX" :mode :zero-page-y)) 242 | (aset nes/instruction:MAP #xAF (make-nes/instruction :func #'nes/instruction-lax :name "LAX" :mode :absolute)) 243 | (aset nes/instruction:MAP #xBF (make-nes/instruction :func #'nes/instruction-lax :name "LAX" :mode :absolute-y)) 244 | (aset nes/instruction:MAP #xA3 (make-nes/instruction :func #'nes/instruction-lax :name "LAX" :mode :pre-indexed-indirect)) 245 | (aset nes/instruction:MAP #xB3 (make-nes/instruction :func #'nes/instruction-lax :name "LAX" :mode :post-indexed-indirect)) 246 | (aset nes/instruction:MAP #x87 (make-nes/instruction :func #'nes/instruction-sax :name "SAX" :mode :zero-page)) 247 | (aset nes/instruction:MAP #x97 (make-nes/instruction :func #'nes/instruction-sax :name "SAX" :mode :zero-page-y)) 248 | (aset nes/instruction:MAP #x8F (make-nes/instruction :func #'nes/instruction-sax :name "SAX" :mode :absolute)) 249 | (aset nes/instruction:MAP #x83 (make-nes/instruction :func #'nes/instruction-sax :name "SAX" :mode :pre-indexed-indirect)) 250 | (aset nes/instruction:MAP #xEB (make-nes/instruction :func #'nes/instruction-sbc :name "SBC" :mode :immediate)) 251 | (aset nes/instruction:MAP #xC7 (make-nes/instruction :func #'nes/instruction-dcp :name "DCP" :mode :zero-page)) 252 | (aset nes/instruction:MAP #xD7 (make-nes/instruction :func #'nes/instruction-dcp :name "DCP" :mode :zero-page-x)) 253 | (aset nes/instruction:MAP #xCF (make-nes/instruction :func #'nes/instruction-dcp :name "DCP" :mode :absolute)) 254 | (aset nes/instruction:MAP #xDF (make-nes/instruction :func #'nes/instruction-dcp :name "DCP" :mode :absolute-x)) 255 | (aset nes/instruction:MAP #xDB (make-nes/instruction :func #'nes/instruction-dcp :name "DCP" :mode :absolute-y)) 256 | (aset nes/instruction:MAP #xC3 (make-nes/instruction :func #'nes/instruction-dcp :name "DCP" :mode :pre-indexed-indirect)) 257 | (aset nes/instruction:MAP #xD3 (make-nes/instruction :func #'nes/instruction-dcp :name "DCP" :mode :post-indexed-indirect)) 258 | (aset nes/instruction:MAP #xE7 (make-nes/instruction :func #'nes/instruction-isb :name "ISB" :mode :zero-page)) 259 | (aset nes/instruction:MAP #xF7 (make-nes/instruction :func #'nes/instruction-isb :name "ISB" :mode :zero-page-x)) 260 | (aset nes/instruction:MAP #xEF (make-nes/instruction :func #'nes/instruction-isb :name "ISB" :mode :absolute)) 261 | (aset nes/instruction:MAP #xFF (make-nes/instruction :func #'nes/instruction-isb :name "ISB" :mode :absolute-x)) 262 | (aset nes/instruction:MAP #xFB (make-nes/instruction :func #'nes/instruction-isb :name "ISB" :mode :absolute-y)) 263 | (aset nes/instruction:MAP #xE3 (make-nes/instruction :func #'nes/instruction-isb :name "ISB" :mode :pre-indexed-indirect)) 264 | (aset nes/instruction:MAP #xF3 (make-nes/instruction :func #'nes/instruction-isb :name "ISB" :mode :post-indexed-indirect)) 265 | (aset nes/instruction:MAP #x07 (make-nes/instruction :func #'nes/instruction-slo :name "SLO" :mode :zero-page)) 266 | (aset nes/instruction:MAP #x17 (make-nes/instruction :func #'nes/instruction-slo :name "SLO" :mode :zero-page-x)) 267 | (aset nes/instruction:MAP #x0F (make-nes/instruction :func #'nes/instruction-slo :name "SLO" :mode :absolute)) 268 | (aset nes/instruction:MAP #x1F (make-nes/instruction :func #'nes/instruction-slo :name "SLO" :mode :absolute-x)) 269 | (aset nes/instruction:MAP #x1B (make-nes/instruction :func #'nes/instruction-slo :name "SLO" :mode :absolute-y)) 270 | (aset nes/instruction:MAP #x03 (make-nes/instruction :func #'nes/instruction-slo :name "SLO" :mode :pre-indexed-indirect)) 271 | (aset nes/instruction:MAP #x13 (make-nes/instruction :func #'nes/instruction-slo :name "SLO" :mode :post-indexed-indirect)) 272 | (aset nes/instruction:MAP #x27 (make-nes/instruction :func #'nes/instruction-rla :name "RLA" :mode :zero-page)) 273 | (aset nes/instruction:MAP #x37 (make-nes/instruction :func #'nes/instruction-rla :name "RLA" :mode :zero-page-x)) 274 | (aset nes/instruction:MAP #x2F (make-nes/instruction :func #'nes/instruction-rla :name "RLA" :mode :absolute)) 275 | (aset nes/instruction:MAP #x3F (make-nes/instruction :func #'nes/instruction-rla :name "RLA" :mode :absolute-x)) 276 | (aset nes/instruction:MAP #x3B (make-nes/instruction :func #'nes/instruction-rla :name "RLA" :mode :absolute-y)) 277 | (aset nes/instruction:MAP #x23 (make-nes/instruction :func #'nes/instruction-rla :name "RLA" :mode :pre-indexed-indirect)) 278 | (aset nes/instruction:MAP #x33 (make-nes/instruction :func #'nes/instruction-rla :name "RLA" :mode :post-indexed-indirect)) 279 | (aset nes/instruction:MAP #x47 (make-nes/instruction :func #'nes/instruction-sre :name "SRE" :mode :zero-page)) 280 | (aset nes/instruction:MAP #x57 (make-nes/instruction :func #'nes/instruction-sre :name "SRE" :mode :zero-page-x)) 281 | (aset nes/instruction:MAP #x4F (make-nes/instruction :func #'nes/instruction-sre :name "SRE" :mode :absolute)) 282 | (aset nes/instruction:MAP #x5F (make-nes/instruction :func #'nes/instruction-sre :name "SRE" :mode :absolute-x)) 283 | (aset nes/instruction:MAP #x5B (make-nes/instruction :func #'nes/instruction-sre :name "SRE" :mode :absolute-y)) 284 | (aset nes/instruction:MAP #x43 (make-nes/instruction :func #'nes/instruction-sre :name "SRE" :mode :pre-indexed-indirect)) 285 | (aset nes/instruction:MAP #x53 (make-nes/instruction :func #'nes/instruction-sre :name "SRE" :mode :post-indexed-indirect)) 286 | (aset nes/instruction:MAP #x67 (make-nes/instruction :func #'nes/instruction-rra :name "RRA" :mode :zero-page)) 287 | (aset nes/instruction:MAP #x77 (make-nes/instruction :func #'nes/instruction-rra :name "RRA" :mode :zero-page-x)) 288 | (aset nes/instruction:MAP #x6F (make-nes/instruction :func #'nes/instruction-rra :name "RRA" :mode :absolute)) 289 | (aset nes/instruction:MAP #x7F (make-nes/instruction :func #'nes/instruction-rra :name "RRA" :mode :absolute-x)) 290 | (aset nes/instruction:MAP #x7B (make-nes/instruction :func #'nes/instruction-rra :name "RRA" :mode :absolute-y)) 291 | (aset nes/instruction:MAP #x63 (make-nes/instruction :func #'nes/instruction-rra :name "RRA" :mode :pre-indexed-indirect)) 292 | (aset nes/instruction:MAP #x73 (make-nes/instruction :func #'nes/instruction-rra :name "RRA" :mode :post-indexed-indirect)) 293 | 294 | 295 | (provide 'nes-instruction) 296 | 297 | ;; Responding to recursive calls from nes-cpu 298 | (require 'nes-cpu) 299 | 300 | (defsubst nes/instruction--set-zero-and-negative-flags (r data) 301 | (setf (nes/cpu-register->sr-zero r) (zerop (logand data #xFF))) 302 | (setf (nes/cpu-register->sr-negative r) (nes--logbitp 7 data))) 303 | 304 | (defun nes/instruction-lda (c addr-or-data mode) 305 | "Load Accumulator" 306 | (let ((data (if (eq mode :immediate) 307 | addr-or-data 308 | (nes/cpu-read c addr-or-data))) 309 | (register (nes/cpu->register c))) 310 | (setf (nes/cpu-register->acc register) data) 311 | (nes/instruction--set-zero-and-negative-flags register data))) 312 | 313 | (defun nes/instruction-ldx (c addr-or-data mode) 314 | "Load X Register" 315 | (let ((data (if (eq mode :immediate) 316 | addr-or-data 317 | (nes/cpu-read c addr-or-data))) 318 | (register (nes/cpu->register c))) 319 | (setf (nes/cpu-register->idx-x register) data) 320 | (nes/instruction--set-zero-and-negative-flags register data))) 321 | 322 | (defun nes/instruction-ldy (c addr-or-data mode) 323 | "Load Y Register" 324 | (let ((data (if (eq mode :immediate) 325 | addr-or-data 326 | (nes/cpu-read c addr-or-data))) 327 | (register (nes/cpu->register c))) 328 | (setf (nes/cpu-register->idx-y register) data) 329 | (nes/instruction--set-zero-and-negative-flags register data))) 330 | 331 | (defun nes/instruction-sta (c address _mode) 332 | "Store Accumulator" 333 | (nes/cpu-write c address (nes/cpu-register->acc (nes/cpu->register c)))) 334 | 335 | (defun nes/instruction-stx (c address _mode) 336 | "Store X Register" 337 | (nes/cpu-write c address (nes/cpu-register->idx-x (nes/cpu->register c)))) 338 | 339 | (defun nes/instruction-sty (c address _mode) 340 | "Store Y Register" 341 | (nes/cpu-write c address (nes/cpu-register->idx-y (nes/cpu->register c)))) 342 | 343 | (defun nes/instruction-tax (c _data _mode) 344 | "Transfer Accumulator to X" 345 | (let* ((register (nes/cpu->register c)) 346 | (data (nes/cpu-register->acc register))) 347 | (setf (nes/cpu-register->idx-x register) data) 348 | (nes/instruction--set-zero-and-negative-flags register data))) 349 | 350 | (defun nes/instruction-tay (c _data _mode) 351 | "Transfer Accumulator to Y" 352 | (let* ((register (nes/cpu->register c)) 353 | (data (nes/cpu-register->acc register))) 354 | (setf (nes/cpu-register->idx-y register) data) 355 | (nes/instruction--set-zero-and-negative-flags register data))) 356 | 357 | (defun nes/instruction-tsx (c _data _mode) 358 | "Transfer Stack Pointer to X" 359 | (let* ((register (nes/cpu->register c)) 360 | (data (nes/cpu-register->sp register))) 361 | (setf (nes/cpu-register->idx-x register) data) 362 | (nes/instruction--set-zero-and-negative-flags register data))) 363 | 364 | (defun nes/instruction-txa (c _data _mode) 365 | "Transfer X to Accumulator" 366 | (let* ((register (nes/cpu->register c)) 367 | (data (nes/cpu-register->idx-x register))) 368 | (setf (nes/cpu-register->acc register) data) 369 | (nes/instruction--set-zero-and-negative-flags register data))) 370 | 371 | (defun nes/instruction-txs (c _data _mode) 372 | "Transfer X to Stack Pointer" 373 | (let ((register (nes/cpu->register c))) 374 | (setf (nes/cpu-register->sp register) 375 | (nes/cpu-register->idx-x register)))) 376 | 377 | (defun nes/instruction-tya (c _data _mode) 378 | "Transfer Y to Accumulator" 379 | (let* ((register (nes/cpu->register c)) 380 | (data (nes/cpu-register->idx-y register))) 381 | (setf (nes/cpu-register->acc register) data) 382 | (nes/instruction--set-zero-and-negative-flags register data))) 383 | 384 | ;; 385 | ;; In the byte pushed, bit 5 is always set to 1, and bit 4 is 1 if from an instruction (PHP or BRK) 386 | ;; 387 | ;; see https://wiki.nesdev.com/w/index.php/Status_flags#The_B_flag 388 | ;; 389 | (defun nes/instruction-php (c _data _mode) 390 | "Push Processor Status" 391 | (let* ((r (nes/cpu->register c)) 392 | (old-break (nes/cpu-register->sr-break r)) 393 | (old-reserved (nes/cpu-register->sr-reserved r))) 394 | (setf (nes/cpu-register->sr-break r) t) 395 | (setf (nes/cpu-register->sr-reserved r) t) 396 | (nes/cpu-push-status-register c) 397 | (setf (nes/cpu-register->sr-break r) old-break) 398 | (setf (nes/cpu-register->sr-reserved r) old-reserved))) 399 | 400 | (defun nes/instruction-pha (c _data _mode) 401 | "Push Accumulator" 402 | (nes/cpu-push c (nes/cpu-register->acc (nes/cpu->register c)))) 403 | 404 | (defun nes/instruction-pla (c _data _mode) 405 | "Pull Accumulator" 406 | (let ((register (nes/cpu->register c)) 407 | (data (nes/cpu-pull c))) 408 | (setf (nes/cpu-register->acc register) data) 409 | (nes/instruction--set-zero-and-negative-flags register data))) 410 | 411 | ;; 412 | ;; Two instructions (PLP and RTI) pull a byte from the stack and set all the flags. They ignore bits 5 and 4. 413 | ;; 414 | ;; see https://wiki.nesdev.com/w/index.php/Status_flags#The_B_flag 415 | ;; 416 | (defun nes/instruction-plp (c _data _mode) 417 | "Pull Processor Status" 418 | (let* ((r (nes/cpu->register c)) 419 | (current-break (nes/cpu-register->sr-break r)) 420 | (current-reserved (nes/cpu-register->sr-reserved r))) 421 | (nes/cpu-pull-status-register c) 422 | (setf (nes/cpu-register->sr-break r) current-break) 423 | (setf (nes/cpu-register->sr-reserved r) current-reserved))) 424 | 425 | (defun nes/instruction-sec (c _data _mode) 426 | "Set Carry Flag" 427 | (setf (nes/cpu-register->sr-carry (nes/cpu->register c)) t)) 428 | 429 | (defun nes/instruction-sed (c _data _mode) 430 | "Set Decimal FLag" 431 | (setf (nes/cpu-register->sr-decimal (nes/cpu->register c)) t)) 432 | 433 | (defun nes/instruction-sei (c _data _mode) 434 | "Set Interrupt Disable" 435 | (setf (nes/cpu-register->sr-interrupt (nes/cpu->register c)) t)) 436 | 437 | (defun nes/instruction-clc (c _data _mode) 438 | "Clear Carry Flag" 439 | (setf (nes/cpu-register->sr-carry (nes/cpu->register c)) nil)) 440 | 441 | (defun nes/instruction-cld (c _data _mode) 442 | "Clear Decimal Mode" 443 | (setf (nes/cpu-register->sr-decimal (nes/cpu->register c)) nil)) 444 | 445 | (defun nes/instruction-cli (c _data _mode) 446 | "Clear Interrupt Disable" 447 | (setf (nes/cpu-register->sr-interrupt (nes/cpu->register c)) nil)) 448 | 449 | (defun nes/instruction-clv (c _data _mode) 450 | "Clear Overflow Flag" 451 | (setf (nes/cpu-register->sr-overflow (nes/cpu->register c)) nil)) 452 | 453 | (defun nes/instruction-inc (c address _mode) 454 | "Increment Memory" 455 | (let ((data (logand (1+ (nes/cpu-read c address)) #xff)) 456 | (register (nes/cpu->register c))) 457 | (nes/cpu-write c address data) 458 | (nes/instruction--set-zero-and-negative-flags register data))) 459 | 460 | (defun nes/instruction-inx (c _address _mode) 461 | "Increment X Register" 462 | (let* ((register (nes/cpu->register c)) 463 | (data (logand (1+ (nes/cpu-register->idx-x register)) #xff))) 464 | (setf (nes/cpu-register->idx-x register) data) 465 | (nes/instruction--set-zero-and-negative-flags register data))) 466 | 467 | (defun nes/instruction-iny (c _address _mode) 468 | "Increment Y Register" 469 | (let* ((register (nes/cpu->register c)) 470 | (data (logand (1+ (nes/cpu-register->idx-y register)) #xff))) 471 | (setf (nes/cpu-register->idx-y register) data) 472 | (nes/instruction--set-zero-and-negative-flags register data))) 473 | 474 | (defun nes/instruction-dec (c address _mode) 475 | "Decrement Memory" 476 | (let ((data (logand (1- (nes/cpu-read c address)) #xff)) 477 | (register (nes/cpu->register c))) 478 | (nes/cpu-write c address data) 479 | (nes/instruction--set-zero-and-negative-flags register data))) 480 | 481 | (defun nes/instruction-dex (c _address _mode) 482 | "Decrement X Register" 483 | (let* ((register (nes/cpu->register c)) 484 | (data (logand (1- (nes/cpu-register->idx-x register)) #xff))) 485 | (setf (nes/cpu-register->idx-x register) data) 486 | (nes/instruction--set-zero-and-negative-flags register data))) 487 | 488 | (defun nes/instruction-dey (c _address _mode) 489 | "Decrement Y Register" 490 | (let* ((register (nes/cpu->register c)) 491 | (data (logand (1- (nes/cpu-register->idx-y register)) #xff))) 492 | (setf (nes/cpu-register->idx-y register) data) 493 | (nes/instruction--set-zero-and-negative-flags register data))) 494 | 495 | (defun nes/instruction-adc (c addr-or-data mode) 496 | "Add with Carry" 497 | (let* ((register (nes/cpu->register c)) 498 | (data (if (eq mode :immediate) 499 | addr-or-data 500 | (nes/cpu-read c addr-or-data))) 501 | (acc (nes/cpu-register->acc register)) 502 | (result (+ acc 503 | data 504 | (if (nes/cpu-register->sr-carry register) 505 | 1 0)))) 506 | (nes/instruction--set-zero-and-negative-flags register result) 507 | (setf (nes/cpu-register->sr-carry register) (> result #xff)) 508 | (setf (nes/cpu-register->sr-overflow register) 509 | (and (not (nes--logbitp 7 (logxor acc data))) 510 | (nes--logbitp 7 (logxor acc result)))) 511 | (setf (nes/cpu-register->acc register) (logand result #xFF)))) 512 | 513 | (defun nes/instruction-sbc (c addr-or-data mode) 514 | "Subtract with Carry" 515 | (let* ((register (nes/cpu->register c)) 516 | (data (if (eq mode :immediate) 517 | addr-or-data 518 | (nes/cpu-read c addr-or-data))) 519 | (acc (nes/cpu-register->acc register)) 520 | (result (- acc 521 | data 522 | (if (nes/cpu-register->sr-carry register) 523 | 0 1)))) 524 | (nes/instruction--set-zero-and-negative-flags register result) 525 | (setf (nes/cpu-register->sr-carry register) (>= result 0)) 526 | (setf (nes/cpu-register->sr-overflow register) 527 | (and (nes--logbitp 7 (logxor acc data)) 528 | (nes--logbitp 7 (logxor acc result)))) 529 | (setf (nes/cpu-register->acc register) (logand result #xFF)))) 530 | 531 | (defun nes/instruction-cmp (c addr-or-data mode) 532 | "Compare" 533 | (let* ((register (nes/cpu->register c)) 534 | (data (if (eq mode :immediate) 535 | addr-or-data 536 | (nes/cpu-read c addr-or-data))) 537 | (acc (nes/cpu-register->acc register)) 538 | (compared (- acc data))) 539 | (nes/instruction--set-zero-and-negative-flags register compared) 540 | (setf (nes/cpu-register->sr-carry register) (>= compared 0)))) 541 | 542 | (defun nes/instruction-cpx (c addr-or-data mode) 543 | "Compare X Register" 544 | (let* ((register (nes/cpu->register c)) 545 | (data (if (eq mode :immediate) 546 | addr-or-data 547 | (nes/cpu-read c addr-or-data))) 548 | (x (nes/cpu-register->idx-x register)) 549 | (compared (- x data))) 550 | (nes/instruction--set-zero-and-negative-flags register compared) 551 | (setf (nes/cpu-register->sr-carry register) (>= compared 0)))) 552 | 553 | (defun nes/instruction-cpy (c addr-or-data mode) 554 | "Compare Y Register" 555 | (let* ((register (nes/cpu->register c)) 556 | (data (if (eq mode :immediate) 557 | addr-or-data 558 | (nes/cpu-read c addr-or-data))) 559 | (y (nes/cpu-register->idx-y register)) 560 | (compared (- y data))) 561 | (nes/instruction--set-zero-and-negative-flags register compared) 562 | (setf (nes/cpu-register->sr-carry register) (>= compared 0)))) 563 | 564 | (defun nes/instruction-and (c addr-or-data mode) 565 | "Logical AND" 566 | (let* ((register (nes/cpu->register c)) 567 | (data (if (eq mode :immediate) 568 | addr-or-data 569 | (nes/cpu-read c addr-or-data))) 570 | (acc (nes/cpu-register->acc register)) 571 | (result (logand acc data))) 572 | (nes/instruction--set-zero-and-negative-flags register result) 573 | (setf (nes/cpu-register->acc register) (logand result #xff)))) 574 | 575 | (defun nes/instruction-ora (c addr-or-data mode) 576 | "Logical Inclusive OR" 577 | (let* ((register (nes/cpu->register c)) 578 | (data (if (eq mode :immediate) 579 | addr-or-data 580 | (nes/cpu-read c addr-or-data))) 581 | (acc (nes/cpu-register->acc register)) 582 | (result (logior acc data))) 583 | (nes/instruction--set-zero-and-negative-flags register result) 584 | (setf (nes/cpu-register->acc register) (logand result #xff)))) 585 | 586 | (defun nes/instruction-eor (c addr-or-data mode) 587 | "Exclusive OR" 588 | (let* ((register (nes/cpu->register c)) 589 | (data (if (eq mode :immediate) 590 | addr-or-data 591 | (nes/cpu-read c addr-or-data))) 592 | (acc (nes/cpu-register->acc register)) 593 | (result (logxor acc data))) 594 | (nes/instruction--set-zero-and-negative-flags register result) 595 | (setf (nes/cpu-register->acc register) (logand result #xff)))) 596 | 597 | (defun nes/instruction-asl (c address mode) 598 | "Arithmetic Shift Left" 599 | (let* ((register (nes/cpu->register c)) 600 | (data (if (eq mode :accumulator) 601 | (nes/cpu-register->acc register) 602 | (nes/cpu-read c address))) 603 | (shifted (logand (lsh data 1) #xff))) 604 | (setf (nes/cpu-register->sr-carry register) (nes--logbitp 7 data)) 605 | (nes/instruction--set-zero-and-negative-flags register shifted) 606 | (if (eq mode :accumulator) 607 | (setf (nes/cpu-register->acc register) shifted) 608 | (nes/cpu-write c address shifted)))) 609 | 610 | (defun nes/instruction-lsr (c address mode) 611 | "Logical Shift Right" 612 | (let* ((register (nes/cpu->register c)) 613 | (data (logand (if (eq mode :accumulator) 614 | (nes/cpu-register->acc register) 615 | (nes/cpu-read c address)))) 616 | (shifted (logand (lsh data -1) #xff))) 617 | (setf (nes/cpu-register->sr-carry register) (nes--logbitp 0 data)) 618 | (nes/instruction--set-zero-and-negative-flags register shifted) 619 | (if (eq mode :accumulator) 620 | (setf (nes/cpu-register->acc register) shifted) 621 | (nes/cpu-write c address shifted)))) 622 | 623 | (defun nes/instruction-rol (c address mode) 624 | "Rotate Left" 625 | (let* ((register (nes/cpu->register c)) 626 | (data (logand (if (eq mode :accumulator) 627 | (nes/cpu-register->acc register) 628 | (nes/cpu-read c address)))) 629 | (carry (nes/cpu-register->sr-carry register)) 630 | (rotated (logand #xff 631 | (logior (lsh data 1) (if carry #x01 #x00))))) 632 | (setf (nes/cpu-register->sr-carry register) (nes--logbitp 7 data)) 633 | (nes/instruction--set-zero-and-negative-flags register rotated) 634 | (if (eq mode :accumulator) 635 | (setf (nes/cpu-register->acc register) rotated) 636 | (nes/cpu-write c address rotated)))) 637 | 638 | (defun nes/instruction-ror (c address mode) 639 | "Rotate Right" 640 | (let* ((register (nes/cpu->register c)) 641 | (data (logand (if (eq mode :accumulator) 642 | (nes/cpu-register->acc register) 643 | (nes/cpu-read c address)))) 644 | (carry (nes/cpu-register->sr-carry register)) 645 | (rotated (logand #xff 646 | (logior (lsh data -1) (if carry #x80 #x00))))) 647 | (setf (nes/cpu-register->sr-carry register) (/= (logand data #x01) 0)) 648 | (nes/instruction--set-zero-and-negative-flags register rotated) 649 | (if (eq mode :accumulator) 650 | (setf (nes/cpu-register->acc register) rotated) 651 | (nes/cpu-write c address rotated)))) 652 | 653 | 654 | (defun nes/instruction-jmp (c address _mode) 655 | "Jump" 656 | (setf (nes/cpu-register->pc (nes/cpu->register c)) address)) 657 | 658 | (defun nes/instruction-nop (_c _address _mode) 659 | "No Operation" 660 | ) 661 | 662 | (defun nes/instruction-bmi (c address _mode) 663 | (let ((register (nes/cpu->register c))) 664 | (when (nes/cpu-register->sr-negative register) 665 | (setf (nes/cpu->cycles c) (logand (1+ (nes/cpu->cycles c)) #xFFFF)) 666 | (setf (nes/cpu-register->pc register) address)))) 667 | 668 | (defun nes/instruction-bpl (c address _mode) 669 | (let ((register (nes/cpu->register c))) 670 | (unless (nes/cpu-register->sr-negative register) 671 | (setf (nes/cpu->cycles c) (logand (1+ (nes/cpu->cycles c)) #xFFFF)) 672 | (setf (nes/cpu-register->pc register) address)))) 673 | 674 | (defun nes/instruction-bvs (c address _mode) 675 | (let ((register (nes/cpu->register c))) 676 | (when (nes/cpu-register->sr-overflow register) 677 | (setf (nes/cpu->cycles c) (logand (1+ (nes/cpu->cycles c)) #xFFFF)) 678 | (setf (nes/cpu-register->pc register) address)))) 679 | 680 | (defun nes/instruction-bvc (c address _mode) 681 | (let ((register (nes/cpu->register c))) 682 | (unless (nes/cpu-register->sr-overflow register) 683 | (setf (nes/cpu->cycles c) (logand (1+ (nes/cpu->cycles c)) #xFFFF)) 684 | (setf (nes/cpu-register->pc register) address)))) 685 | 686 | (defun nes/instruction-beq (c address _mode) 687 | (let ((register (nes/cpu->register c))) 688 | (when (nes/cpu-register->sr-zero register) 689 | (setf (nes/cpu->cycles c) (logand (1+ (nes/cpu->cycles c)) #xFFFF)) 690 | (setf (nes/cpu-register->pc register) address)))) 691 | 692 | (defun nes/instruction-bne (c address _mode) 693 | (let ((register (nes/cpu->register c))) 694 | (unless (nes/cpu-register->sr-zero register) 695 | (setf (nes/cpu->cycles c) (logand (1+ (nes/cpu->cycles c)) #xFFFF)) 696 | (setf (nes/cpu-register->pc register) address)))) 697 | 698 | (defun nes/instruction-bcs (c address _mode) 699 | (let ((register (nes/cpu->register c))) 700 | (when (nes/cpu-register->sr-carry register) 701 | (setf (nes/cpu->cycles c) (logand (1+ (nes/cpu->cycles c)) #xFFFF)) 702 | (setf (nes/cpu-register->pc register) address)))) 703 | 704 | (defun nes/instruction-bcc (c address _mode) 705 | (let ((register (nes/cpu->register c))) 706 | (unless (nes/cpu-register->sr-carry register) 707 | (setf (nes/cpu->cycles c) (logand (1+ (nes/cpu->cycles c)) #xFFFF)) 708 | (setf (nes/cpu-register->pc register) address)))) 709 | 710 | (defun nes/instruction-brk (c _address _mode) 711 | (let* ((register (nes/cpu->register c)) 712 | (interrupt (nes/cpu-register->sr-interrupt register))) 713 | (setf (nes/cpu-register->sr-break register) t) 714 | (incf (nes/cpu-register->pc register)) 715 | (nes/cpu-push c (logand #xFF (lsh (nes/cpu-register->pc register) -8))) 716 | (nes/cpu-push c (logand #xFF (nes/cpu-register->pc register))) 717 | (nes/cpu-push-status-register c) 718 | (unless interrupt 719 | (setf (nes/cpu-register->sr-interrupt register) t) 720 | (setf (nes/cpu-register->pc register) (nes/cpu-read c #xFFFE :word))) 721 | (decf (nes/cpu-register->pc register)))) 722 | 723 | (defun nes/instruction-jsr (c address _mode) 724 | (let* ((register (nes/cpu->register c)) 725 | (pc (1- (nes/cpu-register->pc register)))) 726 | (nes/cpu-push c (logand (lsh pc -8) #xFF)) 727 | (nes/cpu-push c (logand pc #xFF)) 728 | (setf (nes/cpu-register->pc register) address))) 729 | 730 | (defun nes/instruction-rts (c _address _mode) 731 | (let ((register (nes/cpu->register c)) 732 | pc) 733 | (setq pc (logior (nes/cpu-pull c) 734 | (lsh (nes/cpu-pull c) 8))) 735 | (setf (nes/cpu-register->pc register) (1+ pc)))) 736 | 737 | (defun nes/instruction-rti (c _address _mode) 738 | (nes/cpu-pull-status-register c) 739 | (let ((register (nes/cpu->register c))) 740 | (setf (nes/cpu-register->sr-reserved register) t) 741 | (setf (nes/cpu-register->pc register) 742 | (logior (nes/cpu-pull c) 743 | (lsh (nes/cpu-pull c) 8))))) 744 | 745 | (defun nes/instruction-bit (c address _mode) 746 | (let* ((data (nes/cpu-read c address)) 747 | (register (nes/cpu->register c))) 748 | (setf (nes/cpu-register->sr-negative register) (nes--logbitp 7 data)) 749 | (setf (nes/cpu-register->sr-overflow register) (nes--logbitp 6 data)) 750 | (setf (nes/cpu-register->sr-zero register) (zerop (logand (nes/cpu-register->acc register) data))) 751 | )) 752 | 753 | ;; Unofficial Opecodes 754 | 755 | (defun nes/instruction-nopd (c _address _mode) 756 | (let ((r (nes/cpu->register c))) 757 | (setf (nes/cpu-register->pc r) (1+ (nes/cpu-register->pc r))))) 758 | 759 | (defun nes/instruction-nopi (c _address _mode) 760 | (let ((r (nes/cpu->register c))) 761 | (setf (nes/cpu-register->pc r) (+ (nes/cpu-register->pc r) 2)))) 762 | 763 | (defun nes/instruction-lax (c address _mode) 764 | (let ((data (nes/cpu-read c address)) 765 | (register (nes/cpu->register c))) 766 | (setf (nes/cpu-register->idx-x register) data) 767 | (setf (nes/cpu-register->acc register) data) 768 | (nes/instruction--set-zero-and-negative-flags register data))) 769 | 770 | (defun nes/instruction-sax (c address _mode) 771 | (let ((r (nes/cpu->register c))) 772 | (nes/cpu-write c address 773 | (logand (nes/cpu-register->acc r) 774 | (nes/cpu-register->idx-x r))))) 775 | 776 | (defun nes/instruction-dcp (c address _mode) 777 | (let* ((reg (nes/cpu->register c)) 778 | (operated (logand (1- (nes/cpu-read c address)) #xFF))) 779 | (setf (nes/cpu-register->sr-negative reg) 780 | (/= (logand (logand (- (nes/cpu-register->acc reg) operated) #x1FF) #x80) 0)) 781 | (setf (nes/cpu-register->sr-zero reg) 782 | (= (logand (- (nes/cpu-register->acc reg) operated) #x1FF) 0)) 783 | (nes/cpu-write c address operated))) 784 | 785 | (defun nes/instruction-isb (c addr _mode) 786 | (let* ((data (logand (1+ (nes/cpu-read c addr)) #xFF)) 787 | (reg (nes/cpu->register c)) 788 | (operated (+ (logand (lognot data) #xFF) 789 | (nes/cpu-register->acc reg) 790 | (if (nes/cpu-register->sr-carry reg) 1 0)))) 791 | (setf (nes/cpu-register->sr-overflow reg) 792 | (and (zerop (logand (logxor (nes/cpu-register->acc reg) data) #x80)) 793 | (not (zerop (logand (logxor (nes/cpu-register->acc reg) operated) #x80))))) 794 | (setf (nes/cpu-register->sr-carry reg) 795 | (> operated #xFF)) 796 | (nes/instruction--set-zero-and-negative-flags reg operated) 797 | (setf (nes/cpu-register->acc reg) (logand operated #xFF)) 798 | (nes/cpu-write c addr data))) 799 | 800 | (defun nes/instruction-slo (c addr _mode) 801 | (let* ((data (nes/cpu-read c addr)) 802 | (reg (nes/cpu->register c))) 803 | (setf (nes/cpu-register->sr-carry reg) (nes--logbitp 7 data)) 804 | (setq data (logand (lsh data 1) #xFF)) 805 | (setf (nes/cpu-register->acc reg) 806 | (logior (nes/cpu-register->acc reg) data)) 807 | (setf (nes/cpu-register->sr-negative reg) 808 | (nes--logbitp 7 (nes/cpu-register->acc reg))) 809 | (setf (nes/cpu-register->sr-zero reg) 810 | (= (logand (nes/cpu-register->acc reg) #xFF) 0)) 811 | (nes/cpu-write c addr data))) 812 | 813 | 814 | (defun nes/instruction-rla (c addr _mode) 815 | (let* ((data (nes/cpu-read c addr)) 816 | (reg (nes/cpu->register c))) 817 | (setq data (+ (lsh data 1) 818 | (if (nes/cpu-register->sr-carry reg) 1 0))) 819 | (setf (nes/cpu-register->sr-carry reg) 820 | (nes--logbitp 8 data)) 821 | (setf (nes/cpu-register->acc reg) 822 | (logand (logand (nes/cpu-register->acc reg) data) #xFF)) 823 | (setf (nes/cpu-register->sr-negative reg) 824 | (nes--logbitp 7 (nes/cpu-register->acc reg))) 825 | (setf (nes/cpu-register->sr-zero reg) 826 | (= (logand (nes/cpu-register->acc reg) #xFF) 0)) 827 | (nes/cpu-write c addr data))) 828 | 829 | (defun nes/instruction-sre (c addr _mode) 830 | (let* ((data (nes/cpu-read c addr)) 831 | (reg (nes/cpu->register c))) 832 | (setf (nes/cpu-register->sr-carry reg) 833 | (nes--logbitp 0 data)) 834 | (setq data (lsh data -1)) 835 | (setf (nes/cpu-register->acc reg) 836 | (logxor (nes/cpu-register->acc reg) data)) 837 | (setf (nes/cpu-register->sr-negative reg) 838 | (nes--logbitp 7 (nes/cpu-register->acc reg))) 839 | (setf (nes/cpu-register->sr-zero reg) 840 | (= (logand (nes/cpu-register->acc reg) #xFF) 0)) 841 | (nes/cpu-write c addr data))) 842 | 843 | (defun nes/instruction-rra (c addr _mode) 844 | (let* ((data (nes/cpu-read c addr)) 845 | (reg (nes/cpu->register c)) 846 | (carry (nes--logbitp 0 data)) 847 | operated) 848 | (setq data (logior (lsh data -1) 849 | (if (nes/cpu-register->sr-carry reg) #x80 #x00))) 850 | (setq operated (+ data (nes/cpu-register->acc reg) (if carry 1 0))) 851 | 852 | (setf (nes/cpu-register->sr-overflow reg) 853 | (and (not (nes--logbitp 7 (logxor (nes/cpu-register->acc reg) data))) 854 | (nes--logbitp 7 (logxor (nes/cpu-register->acc reg) operated)))) 855 | 856 | (setf (nes/cpu-register->sr-carry reg) 857 | (> operated #xFF)) 858 | (setf (nes/cpu-register->sr-negative reg) 859 | (nes--logbitp 7 operated)) 860 | (setf (nes/cpu-register->sr-zero reg) 861 | (= (logand operated #xFF) 0)) 862 | (setf (nes/cpu-register->acc reg) (logand operated #xFF)) 863 | (nes/cpu-write c addr data))) 864 | -------------------------------------------------------------------------------- /nes-interrupt.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (eval-when-compile (require 'cl)) 4 | 5 | (defstruct (nes/interrupt 6 | (:conc-name nes/interrupt->)) 7 | (irq nil) 8 | (nmi nil)) 9 | 10 | (defun nes/interrupt-assert-nmi (i) 11 | (setf (nes/interrupt->nmi i) t)) 12 | 13 | (defun nes/interrupt-deassert-nmi (i) 14 | (setf (nes/interrupt->nmi i) nil)) 15 | 16 | (defun nes/interrupt-clear (i) 17 | (setf (nes/interrupt->irq i) nil) 18 | (setf (nes/interrupt->nmi i) nil)) 19 | 20 | (provide 'nes-interrupt) 21 | -------------------------------------------------------------------------------- /nes-keypad.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (eval-when-compile (require 'cl)) 4 | 5 | (defvar nes/keypad:a (kbd ",")) 6 | (defvar nes/keypad:b (kbd ".")) 7 | (defvar nes/keypad:up (kbd "w")) 8 | (defvar nes/keypad:down (kbd "s")) 9 | (defvar nes/keypad:left (kbd "a")) 10 | (defvar nes/keypad:right (kbd "d")) 11 | (defvar nes/keypad:select (kbd "n")) 12 | (defvar nes/keypad:start (kbd "m")) 13 | 14 | (defstruct (nes/keypad 15 | (:conc-name nes/keypad->)) 16 | (index 0) 17 | (set-flag nil) 18 | (buffers (make-vector #x10 0)) ;; 1P + 2P 19 | (copies (make-vector #x10 0)) 20 | ) 21 | 22 | (defun nes/keypad-write (k value) 23 | (cond 24 | ((eq (logand #b01 value) 1) 25 | (setf (nes/keypad->set-flag k) t)) 26 | 27 | ((nes/keypad->set-flag k) 28 | (progn 29 | (setf (nes/keypad->set-flag k) nil) 30 | (setf (nes/keypad->copies k) (copy-sequence (nes/keypad->buffers k))) 31 | (setf (nes/keypad->index k) 0) 32 | (fillarray (nes/keypad->buffers k) 0))) 33 | )) 34 | 35 | (defun nes/keypad-read (k) 36 | (let ((value (aref (nes/keypad->copies k) (nes/keypad->index k)))) 37 | (setf (nes/keypad->index k) (mod (1+ (nes/keypad->index k)) #x10)) 38 | value)) 39 | 40 | (defun nes/keypad-check (k keycode) 41 | (aset (nes/keypad->buffers k) keycode 1)) 42 | 43 | (defun nes/keypad-init (k map) 44 | (lexical-let ((k k)) 45 | (define-key map nes/keypad:a (lambda () (interactive) (nes/keypad-check k 0))) 46 | (define-key map nes/keypad:b (lambda () (interactive) (nes/keypad-check k 1))) 47 | (define-key map nes/keypad:select (lambda () (interactive) (nes/keypad-check k 2))) 48 | (define-key map nes/keypad:start (lambda () (interactive) (nes/keypad-check k 3))) 49 | (define-key map nes/keypad:up (lambda () (interactive) (nes/keypad-check k 4))) 50 | (define-key map nes/keypad:down (lambda () (interactive) (nes/keypad-check k 5))) 51 | (define-key map nes/keypad:left (lambda () (interactive) (nes/keypad-check k 6))) 52 | (define-key map nes/keypad:right (lambda () (interactive) (nes/keypad-check k 7))) 53 | )) 54 | 55 | (provide 'nes-keypad) 56 | -------------------------------------------------------------------------------- /nes-ppu.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (require 'gamegrid) 4 | (require 'nes-util) 5 | (require 'nes-color) 6 | (require 'nes-interrupt) 7 | 8 | (defconst nes/ppu:SCREEN-WIDTH 256) 9 | (defconst nes/ppu:SCREEN-HEIGHT 240) 10 | 11 | (defconst nes/ppu:TILE-WIDTH 8) 12 | (defconst nes/ppu:TILE-HEIGHT 8) 13 | (defconst nes/ppu:TILES-COUNT-IN-HORIZONTAL-LINE (/ nes/ppu:SCREEN-WIDTH nes/ppu:TILE-WIDTH)) 14 | (defconst nes/ppu:TILES-COUNT-IN-VERTICAL-LINE (/ nes/ppu:SCREEN-HEIGHT nes/ppu:TILE-HEIGHT)) 15 | 16 | (defconst nes/ppu:BLOCK-WIDTH 16) 17 | (defconst nes/ppu:BLOCK-HEIGHT 16) 18 | 19 | ;; 20 | ;; Each byte of attribute table controls the palette of a 4x4 tile part of the name table. 21 | ;; 22 | ;; https://wiki.nesdev.com/w/index.php/PPU_attribute_tables 23 | ;; 24 | (defconst nes/ppu:ENCODED-ATTRIBUTE-WIDTH (* nes/ppu:TILE-WIDTH 4)) 25 | (defconst nes/ppu:ENCODED-ATTRIBUTE-HEIGHT (* nes/ppu:TILE-HEIGHT 4)) 26 | (defconst nes/ppu:ENCODED-ATTRIBUTE-COUNT-IN-HORIZONTAL-LINE (/ nes/ppu:SCREEN-WIDTH nes/ppu:ENCODED-ATTRIBUTE-WIDTH)) 27 | (defconst nes/ppu:ENCODED-ATTRIBUTE-COUNT-IN-VERTICAL-LINE (/ nes/ppu:SCREEN-WIDTH nes/ppu:ENCODED-ATTRIBUTE-HEIGHT)) 28 | 29 | (defconst nes/ppu:CYCLES-PER-LINE 341) 30 | (defconst nes/ppu:VBLANK-HEIGHT 22) 31 | 32 | (defconst nes/ppu:SPRITE-RAM-BYTESIZE #x0100) 33 | 34 | ;; 35 | ;; This library does not use gamegrid-set-face(10) 36 | ;; 37 | ;; Explain it using the following example. 38 | ;; 39 | ;; (defvar sample-10-options 40 | ;; '(((glyph colorize) 41 | ;; (t ?.)) 42 | ;; ((color-x color-x) 43 | ;; (mono-x grid-x) 44 | ;; (color-tty color-tty)) 45 | ;; (((glyph color-x) [1 0 0]) 46 | ;; (color-tty "red")))) 47 | ;; 48 | ;; (defun sample-display-options () 49 | ;; (let ((options (make-vector 256 nil))) 50 | ;; (dotimes (c 256) 51 | ;; (aset options c 52 | ;; (cond ((= c 1) 53 | ;; sample-1-options) 54 | ;; ((= c 2) 55 | ;; sample-2-options) 56 | ;; ;; 57 | ;; ;; skip 58 | ;; ;; 59 | ;; ((= c 10) 60 | ;; sample-10-options) 61 | ;; (t '(nil nil nil))))) 62 | ;; options)) 63 | ;; 64 | ;; (gamegrid-init (sample-display-options)) 65 | ;; 66 | ;; 1. gamegrid creates a dedicated display-table ( `bufefer-display-table' ) 67 | ;; 68 | ;; 2. In the case of the above example: 69 | ;; 70 | ;; (aref buffer-display-table 10) ;; => [46] ( ?. ) 71 | ;; 72 | ;; 3. This means, 10 (C-j) replaces 46 (.). 73 | ;; 74 | ;; 4. As a result, screen collapse!!! 75 | ;; 76 | ;; before 77 | ;; 78 | ;; ********** 79 | ;; ********** 80 | ;; ********** 81 | ;; ********** 82 | ;; 83 | ;; after 84 | ;; 85 | ;; **********.**********.**********.********** 86 | ;; 87 | ;; 88 | (defconst nes/ppu:COLOR-START-OFFSET 11) 89 | 90 | (defstruct (nes/ppu-bus 91 | (:conc-name nes/ppu-bus->)) 92 | (video-ram (make-vector #x2000 0)) 93 | character-ram ;; vector 94 | ) 95 | 96 | (defstruct (nes/ppu 97 | (:conc-name nes/ppu->)) 98 | (cycle 0) 99 | (line 0) 100 | (bus (make-nes/ppu-bus)) 101 | (sprite-ram (make-vector nes/ppu:SPRITE-RAM-BYTESIZE 0)) 102 | (sprite-ram-addr #x00) 103 | (scroll-x 0) 104 | (scroll-y 0) 105 | (interrupt nil) 106 | 107 | ;; Background temporary variables 108 | (name-table-byte 0) 109 | (attribute-table-byte 0) 110 | (tile-low-byte 0) 111 | (tile-hi-byte 0) 112 | (tile-data1 0) 113 | (tile-data2 0) 114 | 115 | ;; Sprite temporary variables 116 | (sprite-count 0) 117 | (sprite-patterns (make-vector 8 0)) 118 | (sprite-positions (make-vector 8 0)) 119 | (sprite-priorities (make-vector 8 0)) 120 | (sprite-indexes (make-vector 8 0)) 121 | 122 | ;; PPU registers 123 | (ppuctrl 0) 124 | (ppumask 0) 125 | (ppustatus 0) 126 | 127 | ;; PPU internal registers 128 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#PPU_internal_registers 129 | (v 0) 130 | (temp 0) ;; (setting-constant t) 131 | (x 0) 132 | (w nil) 133 | (f 0) 134 | 135 | ;; use $2007 PPUDATA 136 | (buffered-data 0) 137 | 138 | ;; NMI flags 139 | (nmi-previous nil) 140 | (nmi-delay 0) 141 | ) 142 | 143 | (defun nes/ppu--bus-read (b addr) 144 | (when (nes/ppu-p b) 145 | (setq b (nes/ppu->bus b))) 146 | (cond 147 | ((<= addr #x1FFF) (aref (nes/ppu-bus->character-ram b) addr)) 148 | 149 | ((<= addr #x27FF) (aref (nes/ppu-bus->video-ram b) (- addr #x2000))) ;; nametable 0 - 1 150 | ((<= addr #x2FFF) (nes/ppu--bus-read b (- addr #x0800))) ;; nametable 2 - 3 (mirror to 0 - 1) 151 | ((<= addr #x3EFF) (nes/ppu--bus-read b (- addr #x1000))) ;; mirror to #x2000 - #x2EFF 152 | ((or (eq addr #x3F04) 153 | (eq addr #x3F08) 154 | (eq addr #x3F0C)) (nes/ppu--bus-read b #x3F00)) ;; Use #x3F00 because these are unique data and not used by PPU 155 | ((or (eq addr #x3F10) 156 | (eq addr #x3F14) 157 | (eq addr #x3F18) 158 | (eq addr #x3F1C)) (nes/ppu--bus-read b (- addr #x0010))) ;; mirror to #x3F00, #x3F04, #x3F08 and #x3F0C 159 | ((<= addr #x3F1F) (aref (nes/ppu-bus->video-ram b) (- addr #x2000))) 160 | ((<= addr #x3FFF) (nes/ppu--bus-read b (- addr #x0020))) ;; mirror to #x3F00 - #x3F1F 161 | ;; (t 0) 162 | )) 163 | 164 | (defun nes/ppu--bus-write (b addr value) 165 | (when (nes/ppu-p b) 166 | (setq b (nes/ppu->bus b))) 167 | (cond 168 | ((<= addr #x1FFF) (aset (nes/ppu-bus->character-ram b) addr value)) 169 | ((<= addr #x27FF) (aset (nes/ppu-bus->video-ram b) (- addr #x2000) value)) 170 | ((<= addr #x2FFF) (nes/ppu--bus-write b (- addr #x0800) value)) 171 | ((<= addr #x3EFF) (nes/ppu--bus-write b (- addr #x1000) value)) ;; mirror to #x2000 - #x2EFF 172 | ((or (eq addr #x3F10) 173 | (eq addr #x3F14) 174 | (eq addr #x3F18) 175 | (eq addr #x3F1C)) (nes/ppu--bus-write b (- addr #x0010) value)) ;; mirror to #x3F00, #x3F04, #x3F08 and #x3F0C 176 | ((<= addr #x3F1F) (aset (nes/ppu-bus->video-ram b) (- addr #x2000) value)) 177 | ((<= addr #x3FFF) (nes/ppu--bus-write b (- addr #x0020) value)) ;; mirror to #x3F00 - #x3F1F 178 | ;; (t 0) 179 | )) 180 | 181 | (defun nes/ppu-read (p addr) 182 | (case addr 183 | (#x2002 (nes/ppu--read-status p)) 184 | (#x2004 (nes/ppu--read-oam-data p)) 185 | (#x2007 (nes/ppu--read-data p)) 186 | (t 0) 187 | )) 188 | 189 | (defun nes/ppu-write (p addr value) 190 | (setq value (logand #xFF value)) 191 | (case addr 192 | (#x2000 (nes/ppu--write-control p value)) 193 | (#x2001 (nes/ppu--write-mask p value)) 194 | (#x2003 (nes/ppu--write-oam-address p value)) 195 | (#x2004 (nes/ppu--write-oam-data p value)) 196 | (#x2005 (nes/ppu--write-scroll p value)) 197 | (#x2006 (nes/ppu--write-address p value)) 198 | (#x2007 (nes/ppu--write-data p value)) 199 | ;;(t 0) 200 | )) 201 | 202 | ;; 203 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#Register_controls 204 | ;; 205 | ;; $2000 write 206 | ;; 207 | (defsubst nes/ppu--write-control (ppu value) 208 | (setf (nes/ppu->ppuctrl ppu) value) 209 | (nes/ppu--nmi-change ppu) 210 | ;; t: ...BA.. ........ = d: ......BA 211 | (setf (nes/ppu->temp ppu) (logior (logand (nes/ppu->temp ppu) #xF3FF) 212 | (lsh (logand value #x03) 10)))) 213 | 214 | ;; 215 | ;; https://wiki.nesdev.com/w/index.php/PPU_registers 216 | ;; 217 | ;; $2001 write 218 | ;; 219 | (defsubst nes/ppu--write-mask (ppu value) 220 | (setf (nes/ppu->ppumask ppu) value)) 221 | 222 | ;; 223 | ;; https://wiki.nesdev.com/w/index.php/PPU_registers 224 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#Register_controls 225 | ;; 226 | ;; $2002 read 227 | ;; 228 | (defsubst nes/ppu--read-status (ppu) 229 | (let ((data (nes/ppu->ppustatus ppu))) 230 | (nes/ppu--clear-vblank ppu) 231 | (setf (nes/ppu->w ppu) nil) 232 | data)) 233 | 234 | ;; 235 | ;; https://wiki.nesdev.com/w/index.php/PPU_registers 236 | ;; 237 | ;; $2003 write 238 | ;; 239 | (defsubst nes/ppu--write-oam-address (ppu value) 240 | (setf (nes/ppu->sprite-ram-addr ppu) value)) 241 | 242 | ;; 243 | ;; https://wiki.nesdev.com/w/index.php/PPU_registers 244 | ;; 245 | ;; $2004 read 246 | ;; 247 | (defsubst nes/ppu--read-oam-data (ppu) 248 | (nes/ppu--read-from-sprite-ram ppu (nes/ppu->sprite-ram-addr ppu))) 249 | 250 | ;; 251 | ;; https://wiki.nesdev.com/w/index.php/PPU_registers 252 | ;; 253 | ;; $2004 write 254 | ;; 255 | (defsubst nes/ppu--write-oam-data (ppu value) 256 | (let ((addr (nes/ppu->sprite-ram-addr ppu))) 257 | (aset (nes/ppu->sprite-ram ppu) addr value) 258 | (setf (nes/ppu->sprite-ram-addr ppu) (logand (incf addr) #xFF)))) 259 | 260 | ;; 261 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#Register_controls 262 | ;; 263 | ;; $2005 write 264 | ;; 265 | (defsubst nes/ppu--write-scroll (ppu value) 266 | (if (nes/ppu->w ppu) 267 | (progn 268 | ;; 269 | ;; t: CBA..HG FED..... = d: HGFEDCBA 270 | ;; w: = 0 271 | ;; 272 | (setf (nes/ppu->temp ppu) (logior (logand (nes/ppu->temp ppu) #x8FFF) 273 | (lsh (logand value #x07) 12))) 274 | (setf (nes/ppu->temp ppu) (logior (logand (nes/ppu->temp ppu) #xFC1F) 275 | (lsh (logand value #xF8) 2))) 276 | (setf (nes/ppu->w ppu) nil)) 277 | (progn 278 | ;; 279 | ;; t: ....... ...HGFED = d: HGFED... 280 | ;; x: CBA = d: .....CBA 281 | ;; w: = 1 282 | ;; 283 | (setf (nes/ppu->temp ppu) (logior (logand (nes/ppu->temp ppu) #xFFE0) 284 | (lsh (logand value #xFF) -3))) 285 | (setf (nes/ppu->x ppu) (logand value #x07)) 286 | (setf (nes/ppu->w ppu) t)))) 287 | 288 | ;; 289 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#Register_controls 290 | ;; 291 | ;; $2006 write 292 | ;; 293 | (defsubst nes/ppu--write-address (ppu value) 294 | (if (nes/ppu->w ppu) 295 | (progn 296 | ;; 297 | ;; t: ....... HGFEDCBA = d: HGFEDCBA 298 | ;; v = t 299 | ;; w: = 0 300 | ;; 301 | (setf (nes/ppu->temp ppu) (logior (logand (nes/ppu->temp ppu) #xFF00) 302 | (logand value #xFF))) 303 | (setf (nes/ppu->v ppu) (nes/ppu->temp ppu)) 304 | (setf (nes/ppu->w ppu) nil)) 305 | (progn 306 | ;; 307 | ;; t: .FEDCBA ........ = d: ..FEDCBA 308 | ;; t: X...... ........ = 0 309 | ;; w: = 1 310 | ;; 311 | (setf (nes/ppu->temp ppu) (logior (logand (nes/ppu->temp ppu) #x80FF) 312 | (lsh (logand value #x3F) 8))) 313 | (setf (nes/ppu->w ppu) t)))) 314 | 315 | ;; 316 | ;; https://wiki.nesdev.com/w/index.php/PPU_registers 317 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#Register_controls 318 | ;; 319 | ;; $2007 read 320 | ;; 321 | (defsubst nes/ppu--read-data (ppu) 322 | (let* ((vram-address (nes/ppu->v ppu)) 323 | (bus (nes/ppu->bus ppu)) 324 | (read-value (nes/ppu--bus-read bus vram-address)) 325 | (return-value 0)) 326 | (if (< (% vram-address #x4000) #x3F00) 327 | (progn 328 | (setq return-value (nes/ppu->buffered-data ppu)) 329 | (setf (nes/ppu->buffered-data ppu) read-value)) 330 | (progn 331 | (setq return-value read-value) 332 | (setf (nes/ppu->buffered-data ppu) (nes/ppu--bus-read bus (- vram-address #x1000))))) 333 | 334 | (incf (nes/ppu->v ppu) (nes/ppu--vram-address-increment-offset ppu)) 335 | return-value)) 336 | 337 | ;; 338 | ;; https://wiki.nesdev.com/w/index.php/PPU_registers 339 | ;; 340 | ;; $2007 write 341 | ;; 342 | (defsubst nes/ppu--write-data (ppu value) 343 | (nes/ppu--bus-write ppu (nes/ppu->v ppu) value) 344 | (incf (nes/ppu->v ppu) (nes/ppu--vram-address-increment-offset ppu))) 345 | 346 | (defun nes/ppu--read-from-sprite-ram (p index) 347 | (aref (nes/ppu->sprite-ram p) index)) 348 | 349 | (defun nes/ppu--read-from-palette-table (p index) 350 | (nes/ppu--bus-read (nes/ppu->bus p) (+ #x3F00 index))) 351 | 352 | ;; 353 | ;; https://wiki.nesdev.com/w/index.php/PPU_rendering#Line-by-line_timing 354 | ;; 355 | (defun nes/ppu-step (ppu) 356 | (nes/ppu--tick ppu) 357 | 358 | (let* ((rendering-enabled-p (or (nes/ppu--background-enabled-p ppu) 359 | (nes/ppu--sprite-enabled-p ppu))) 360 | (scanline (nes/ppu->line ppu)) 361 | (pre-render-line-p (eq scanline 261)) 362 | (visible-line-p (< scanline 240)) 363 | (render-line-p (or pre-render-line-p visible-line-p)) 364 | 365 | (cycle (nes/ppu->cycle ppu)) 366 | (pre-fetch-cycle-p (<= 321 cycle 336)) 367 | (visible-cycle-p (<= 1 cycle 256)) 368 | (fetch-cycle-p (or pre-fetch-cycle-p visible-cycle-p)) 369 | ) 370 | 371 | (when rendering-enabled-p 372 | ;; 373 | ;; background logic 374 | ;; 375 | (when (and visible-line-p visible-cycle-p) 376 | (nes/ppu--render-pixel ppu)) 377 | 378 | (when (and render-line-p fetch-cycle-p) 379 | (setf (nes/ppu->tile-data2 ppu) (logior (logand #xFFFFFFF0 (lsh (nes/ppu->tile-data2 ppu) 4)) 380 | (logand (lsh (nes/ppu->tile-data1 ppu) -28) #b1111))) 381 | (setf (nes/ppu->tile-data1 ppu) (logand #xFFFFFFFF (lsh (nes/ppu->tile-data1 ppu) 4))) 382 | (case (% cycle 8) 383 | (1 (nes/ppu--fetch-current-name-table-byte ppu)) 384 | (3 (nes/ppu--fetch-current-attribute-table-byte ppu)) 385 | (5 (nes/ppu--fetch-current-tile-low-byte ppu)) 386 | (7 (nes/ppu--fetch-current-tile-hi-byte ppu)) 387 | (0 (nes/ppu--store-current-tile-data ppu)))) 388 | 389 | (when (and pre-render-line-p (<= 280 cycle 304)) 390 | (nes/ppu--copy-y ppu)) 391 | 392 | (when render-line-p 393 | (when (and fetch-cycle-p (zerop (% cycle 8))) 394 | (nes/ppu--increment-x ppu)) 395 | (when (eq cycle 256) 396 | (nes/ppu--increment-y ppu)) 397 | (when (eq cycle 257) 398 | (nes/ppu--copy-x ppu))) 399 | 400 | ;; sprite logic 401 | (when (eq cycle 257) 402 | (if visible-line-p 403 | (nes/ppu--evaluate-sprites ppu) 404 | (setf (nes/ppu->sprite-count ppu) 0))) 405 | ) 406 | 407 | (when (and (eq scanline 241) (eq cycle 1)) 408 | (nes/ppu--set-vblank ppu)) 409 | 410 | (when (and pre-render-line-p (eq cycle 1)) 411 | (nes/ppu--clear-vblank ppu) 412 | (nes/ppu--clear-sprite-zero-hit ppu) 413 | (nes/ppu--clear-sprite-overflow ppu) 414 | ) 415 | )) 416 | 417 | (defun* nes/ppu--tick (ppu) 418 | (when (> (nes/ppu->nmi-delay ppu) 0) 419 | (decf (nes/ppu->nmi-delay ppu)) 420 | (when (and (zerop (nes/ppu->nmi-delay ppu)) 421 | (nes/ppu--generate-nmi-p ppu) 422 | (nes/ppu--vblank-p ppu)) 423 | (nes/interrupt-assert-nmi (nes/ppu->interrupt ppu)))) 424 | 425 | (when (or (nes/ppu--background-enabled-p ppu) 426 | (nes/ppu--sprite-enabled-p ppu)) 427 | (when (and (eq (nes/ppu->f ppu) 1) 428 | (eq (nes/ppu->line ppu) 261) 429 | (eq (nes/ppu->cycle ppu) 339)) 430 | (setf (nes/ppu->cycle ppu) 0) 431 | (setf (nes/ppu->line ppu) 0) 432 | (setf (nes/ppu->f ppu) (logxor (nes/ppu->f ppu) 1)) 433 | (return-from nes/ppu--tick))) 434 | 435 | (incf (nes/ppu->cycle ppu)) 436 | 437 | (let ((right-end-cycle-p (>= (nes/ppu->cycle ppu) nes/ppu:CYCLES-PER-LINE)) 438 | (bottom-end-line-p (>= (nes/ppu->line ppu) (+ nes/ppu:SCREEN-HEIGHT nes/ppu:VBLANK-HEIGHT))) 439 | ) 440 | (when right-end-cycle-p 441 | (setf (nes/ppu->cycle ppu) 0) 442 | (incf (nes/ppu->line ppu)) 443 | (when bottom-end-line-p 444 | (setf (nes/ppu->line ppu) 0) 445 | (setf (nes/ppu->f ppu) (logxor (nes/ppu->f ppu) 1))) 446 | )) 447 | ) 448 | 449 | ;; 450 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#At_dot_257_of_each_scanline 451 | ;; 452 | ;; v: ....F.. ...EDCBA = t: ....F.. ...EDCBA 453 | ;; 454 | (defun nes/ppu--copy-x (ppu) 455 | (setf (nes/ppu->v ppu) (logior (logand (nes/ppu->v ppu) #xFBE0) 456 | (logand (nes/ppu->temp ppu) #x041F)))) 457 | 458 | ;; 459 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#During_dots_280_to_304_of_the_pre-render_scanline_.28end_of_vblank.29 460 | ;; 461 | ;; v: IHGF.ED CBA..... = t: IHGF.ED CBA..... 462 | ;; 463 | (defun nes/ppu--copy-y (ppu) 464 | (setf (nes/ppu->v ppu) (logior (logand (nes/ppu->v ppu) #x841F) 465 | (logand (nes/ppu->temp ppu) #x7BE0)))) 466 | 467 | ;; 468 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#Coarse_X_increment 469 | ;; 470 | (defun nes/ppu--increment-x (ppu) 471 | (if (eq (nes/ppu--coarse-x-scroll ppu) 31) 472 | (progn 473 | (nes/ppu--clear-coarse-x-scroll ppu) 474 | (nes/ppu--toggle-horizontal-name-table-select ppu)) 475 | (nes/ppu--increment-coarse-x-scroll ppu))) 476 | 477 | ;; 478 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#Y_increment 479 | ;; 480 | (defun nes/ppu--increment-y (ppu) 481 | (if (< (nes/ppu--fine-y-scroll ppu) 7) 482 | (nes/ppu--increment-fine-y-scroll ppu) 483 | (let ((coarse-y (nes/ppu--coarse-y-scroll ppu)) 484 | (y 0)) 485 | (nes/ppu--clear-fine-y-scroll ppu) 486 | (case coarse-y 487 | (29 488 | (nes/ppu--toggle-vertical-name-table-select ppu)) 489 | ;; (31 490 | ;; (setq y 0)) 491 | (t 492 | (setq y (1+ coarse-y)) 493 | )) 494 | (nes/ppu--set-coarse-y-scroll ppu y)))) 495 | 496 | (defun nes/ppu--nmi-change (ppu) 497 | (let ((nmi (and (nes/ppu--generate-nmi-p ppu) 498 | (nes/ppu--vblank-p ppu)))) 499 | (when (and nmi (null (nes/ppu->nmi-previous ppu))) 500 | (setf (nes/ppu->nmi-delay ppu) 15)) 501 | (setf (nes/ppu->nmi-previous ppu) nmi))) 502 | 503 | ;; 504 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#Tile_and_attribute_fetching 505 | ;; 506 | ;; Name table (address) 507 | ;; 508 | ;; (x) 509 | ;; 0 256 512 510 | ;; 0 +--------+--------+ 511 | ;; | | | 512 | ;; | 0x2000 | 0x2400 | 513 | ;; | | | 514 | ;; 240 +--------+--------+ 515 | ;; | | | 516 | ;; | 0x2800 | 0x2C00 | 517 | ;; | | | 518 | ;; 480 +--------+--------+ 519 | ;; (y) 520 | ;; 521 | (defun nes/ppu--fetch-current-name-table-byte (ppu) 522 | "Return nametable byte (character id)" 523 | (let ((address (logior #x2000 (logand (nes/ppu->v ppu) #x0FFF)))) 524 | (setf (nes/ppu->name-table-byte ppu) (nes/ppu--bus-read ppu address)))) 525 | 526 | ;; 527 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling#Tile_and_attribute_fetching 528 | ;; 529 | ;; 530 | ;; Attribute table id (address) 531 | ;; 532 | ;; 0 256 512 533 | ;; 0 +--------+--------+ 534 | ;; | | | 535 | ;; | 0x23C0 | 0x27C0 | 536 | ;; | | | 537 | ;; 240 +--------+--------+ 538 | ;; | | | 539 | ;; | 0x2BC0 | 0x2FC0 | 540 | ;; | | | 541 | ;; 480 +--------+--------+ 542 | ;; (y) 543 | ;; 544 | (defun nes/ppu--fetch-current-attribute-table-byte (ppu) 545 | (let* ((v (nes/ppu->v ppu)) 546 | (address (logior #x23C0 547 | (logand #x0C00 v) 548 | (logand #x0038 (lsh v -4)) 549 | (logand #x0007 (lsh v -2)))) 550 | (shift (logior (logand #x04 (lsh v -4)) 551 | (logand #x02 v))) 552 | ) 553 | (setf (nes/ppu->attribute-table-byte ppu) 554 | (lsh (logand (lsh (nes/ppu--bus-read ppu address) (- shift)) #x03) 2)))) 555 | 556 | ;; 557 | ;; https://wiki.nesdev.com/w/index.php/PPU_scrolling 558 | ;; 559 | ;; yyy NN YYYYY XXXXX 560 | ;; ||| || ||||| +++++-- coarse X scroll 561 | ;; ||| || +++++-------- coarse Y scroll 562 | ;; ||| ++-------------- nametable select 563 | ;; +++----------------- fine Y scroll 564 | ;; 565 | ;; memo: 16 bytes in one character (low + hi) 566 | ;; 567 | (defun nes/ppu--current-tile-low-byte-address (ppu) 568 | (let ((offset (nes/ppu--background-pattern-table-address-offset ppu)) 569 | (tile (nes/ppu->name-table-byte ppu))) 570 | (+ (* tile 16) (nes/ppu--fine-y-scroll ppu) offset))) 571 | 572 | (defun nes/ppu--fetch-current-tile-low-byte (ppu) 573 | (let ((address (nes/ppu--current-tile-low-byte-address ppu))) 574 | (setf (nes/ppu->tile-low-byte ppu) (nes/ppu--bus-read ppu (+ address 0))))) 575 | 576 | (defun nes/ppu--fetch-current-tile-hi-byte (ppu) 577 | (let ((address (nes/ppu--current-tile-low-byte-address ppu))) 578 | (setf (nes/ppu->tile-hi-byte ppu) (nes/ppu--bus-read ppu (+ address 8))))) 579 | 580 | ;; 581 | ;; PPU pattern tables 582 | ;; https://wiki.nesdev.com/w/index.php/PPU_pattern_tables 583 | ;; 584 | (defun nes/ppu--store-current-tile-data (ppu) 585 | (let ((attribute (nes/ppu->attribute-table-byte ppu)) 586 | (low (nes/ppu->tile-low-byte ppu)) 587 | (hi (nes/ppu->tile-hi-byte ppu)) 588 | (data 0)) 589 | (dotimes (i nes/ppu:TILE-WIDTH) 590 | (let* ((bit (- nes/ppu:TILE-WIDTH i 1)) 591 | (p1 (if (nes--logbitp bit hi) #b10 #b00)) 592 | (p2 (if (nes--logbitp bit low) #b01 #b00))) 593 | (setq data (logior (lsh data 4) 594 | (logand #xFF (logior attribute p1 p2)))))) 595 | (setf (nes/ppu->tile-data1 ppu) (logand #xFFFFFFFF data)))) 596 | 597 | (defun nes/ppu--evaluate-sprites (ppu) 598 | (let ((h (nes/ppu--sprite-height ppu)) 599 | (count 0)) 600 | (dotimes (i 64) 601 | (let* ((base-addr (* i 4)) 602 | ;; 603 | ;; https://wiki.nesdev.com/w/index.php/PPU_OAM 604 | ;; 605 | (y (nes/ppu--read-from-sprite-ram ppu (+ base-addr 0))) 606 | (tile (nes/ppu--read-from-sprite-ram ppu (+ base-addr 1))) 607 | (attribute (nes/ppu--read-from-sprite-ram ppu (+ base-addr 2))) 608 | (x (nes/ppu--read-from-sprite-ram ppu (+ base-addr 3))) 609 | 610 | ;; 611 | ;; line number in currnet sprite 612 | ;; 613 | (row (- (nes/ppu--current-y ppu) y)) 614 | 615 | ;; 616 | ;; Flags determined by attribute 617 | ;; 618 | (sprite-priority-p (not (nes--logbitp 5 attribute))) 619 | (reversed-vertically-p (nes--logbitp 7 attribute)) 620 | ) 621 | (when (<= 0 row (1- h)) 622 | (when reversed-vertically-p 623 | (setq row (- h 1 row))) 624 | (when (< count 8) 625 | (aset (nes/ppu->sprite-patterns ppu) count (nes/ppu--fetch-sprite-pattern ppu row tile attribute (eq h 8))) 626 | (aset (nes/ppu->sprite-positions ppu) count x) 627 | (aset (nes/ppu->sprite-priorities ppu) count sprite-priority-p) 628 | (aset (nes/ppu->sprite-indexes ppu) count i)) 629 | (incf count) 630 | ))) 631 | 632 | (when (> count 8) 633 | (nes/ppu--set-sprite-overflow ppu) 634 | (setq count 8)) 635 | (setf (nes/ppu->sprite-count ppu) count) 636 | )) 637 | 638 | (defun nes/ppu--fetch-sprite-pattern (ppu row tile attribute sprite-height-8-p) 639 | (let ((palette-id (lsh (logand attribute #b11) 2)) 640 | (reversed-horizontally-p (nes--logbitp 6 attribute)) 641 | (base-addr 0) 642 | (addr 0) 643 | (low 0) 644 | (hi 0) 645 | (patterns 0)) 646 | ;; 647 | ;; https://wiki.nesdev.com/w/index.php/PPU_OAM#Byte_1 648 | ;; 649 | (if sprite-height-8-p 650 | (setq base-addr (nes/ppu--sprite-pattern-table-address-offset ppu)) 651 | (progn 652 | (setq base-addr (* #x1000 (logand tile #b00000001))) 653 | (setq tile (logand tile #b11111110)) 654 | ;; 655 | ;; if bottom half 656 | ;; 657 | (when (> row 7) 658 | (incf tile) 659 | (decf row 8))) 660 | ) 661 | 662 | ;; 663 | ;; ram[addr] has 16 bit sprite data (low + hi) 664 | ;; 665 | (setq addr (+ base-addr (* nes/ppu:TILE-HEIGHT 2 (logand #xFF tile)) row)) 666 | (setq low (nes/ppu--bus-read ppu addr)) 667 | (setq hi (nes/ppu--bus-read ppu (+ addr 8))) 668 | (dotimes (i nes/ppu:TILE-WIDTH) 669 | (let ((idx (if reversed-horizontally-p i (- nes/ppu:TILE-WIDTH 1 i)))) 670 | (setq patterns (logior (lsh patterns 4) 671 | palette-id 672 | (if (nes--logbitp idx low) #b01 #b00) 673 | (if (nes--logbitp idx hi) #b10 #b00))))) 674 | patterns)) 675 | 676 | (defun nes/ppu--render-pixel (ppu) 677 | (let* ((x (nes/ppu--current-x ppu)) 678 | (y (nes/ppu--current-y ppu)) 679 | 680 | ;; 681 | ;; bg-color-index == 0 means `Universal background color` 682 | ;; 683 | (bg-color-index (if (or (>= x 8) (nes/ppu--left-background-enabled-p ppu)) 684 | (nes/ppu--get-current-background-palette-index ppu) 685 | 0)) 686 | 687 | ;; 688 | ;; sp-color-index == 0 means `transparent` 689 | ;; 690 | (sp (nes/ppu--get-current-sprite-pixel ppu)) 691 | (sp-index (elt sp 0)) 692 | (sp-color-index (if (or (>= x 8) (nes/ppu--left-sprite-enabled-p ppu)) 693 | (elt sp 1) 0)) 694 | 695 | ;; 696 | ;; https://wiki.nesdev.com/w/index.php/PPU_palettes#Memory_Map 697 | ;; 698 | (bg-p (/= (% bg-color-index 4) 0)) 699 | (sp-p (/= (% sp-color-index 4) 0)) 700 | 701 | (color-index (cond 702 | ((and (not bg-p) (not sp-p)) 703 | 0) 704 | 705 | ((and (not bg-p) sp-p) 706 | (logior sp-color-index #x10)) 707 | 708 | ((and bg-p (not sp-p)) 709 | bg-color-index) 710 | 711 | (t 712 | (when (and (zerop (aref (nes/ppu->sprite-indexes ppu) sp-index)) 713 | (< x 255)) 714 | (nes/ppu--set-sprite-zero-hit ppu)) 715 | (if (aref (nes/ppu->sprite-priorities ppu) sp-index) 716 | (logior sp-color-index #x10) 717 | bg-color-index))) 718 | ) 719 | 720 | ) 721 | (nes/ppu--image-write x y (nes/ppu--read-from-palette-table ppu color-index)) 722 | )) 723 | 724 | (defun nes/ppu--image-write (x y color) 725 | (nes/ppu--render-set-cell x y color)) 726 | 727 | (defun nes/ppu--get-current-background-palette-index (ppu) 728 | (if (nes/ppu--background-enabled-p ppu) 729 | (let ((index (- nes/ppu:TILE-WIDTH 1 (nes/ppu->x ppu)))) 730 | (logand (lsh (nes/ppu->tile-data2 ppu) (- (* index 4))) #b1111)) 731 | 0)) 732 | 733 | (defun* nes/ppu--get-current-sprite-pixel (ppu) 734 | (if (nes/ppu--sprite-enabled-p ppu) 735 | (let ((offset 0) 736 | (color 0)) 737 | (dotimes (i (nes/ppu->sprite-count ppu)) 738 | (setq offset (- (nes/ppu--current-x ppu) (aref (nes/ppu->sprite-positions ppu) i))) 739 | (when (<= 0 offset 7) 740 | (setq color (logand #xF (lsh (aref (nes/ppu->sprite-patterns ppu) i) (- (* (- 7 offset) 4))))) 741 | (when (/= (% color 4) 0) 742 | (return-from nes/ppu--get-current-sprite-pixel (vector i color))))) 743 | [0 0]) 744 | [0 0])) 745 | 746 | (defun nes/ppu--vram-address-increment-offset (ppu) 747 | (if (nes--logbitp 2 (nes/ppu->ppuctrl ppu)) 32 1)) 748 | 749 | (defun nes/ppu--sprite-pattern-table-address-offset (ppu) 750 | (if (nes--logbitp 3 (nes/ppu->ppuctrl ppu)) #x1000 #x0000)) 751 | 752 | (defun nes/ppu--background-pattern-table-address-offset (ppu) 753 | (if (nes--logbitp 4 (nes/ppu->ppuctrl ppu)) #x1000 #x0000)) 754 | 755 | (defun nes/ppu--sprite-height (ppu) 756 | (if (nes--logbitp 5 (nes/ppu->ppuctrl ppu)) 16 8)) 757 | 758 | (defun nes/ppu--generate-nmi-p (ppu) 759 | (nes--logbitp 7 (nes/ppu->ppuctrl ppu))) 760 | 761 | (defun nes/ppu--set-vblank (ppu) 762 | (setf (nes/ppu->ppustatus ppu) (logior (nes/ppu->ppustatus ppu) #x80)) 763 | (nes/ppu--nmi-change ppu) 764 | ) 765 | 766 | (defun nes/ppu--clear-vblank (ppu) 767 | (setf (nes/ppu->ppustatus ppu) (logand (nes/ppu->ppustatus ppu) #x7F)) 768 | (nes/ppu--nmi-change ppu) 769 | ) 770 | 771 | (defun nes/ppu--vblank-p (ppu) 772 | (nes--logbitp 7 (nes/ppu->ppustatus ppu))) 773 | 774 | (defun nes/ppu--set-sprite-zero-hit (ppu) 775 | (setf (nes/ppu->ppustatus ppu) (logior (nes/ppu->ppustatus ppu) #x40))) 776 | 777 | (defun nes/ppu--clear-sprite-zero-hit (ppu) 778 | (setf (nes/ppu->ppustatus ppu) (logand (nes/ppu->ppustatus ppu) #xBF))) 779 | 780 | (defun nes/ppu--set-sprite-overflow (ppu) 781 | (setf (nes/ppu->ppustatus ppu) (logior (nes/ppu->ppustatus ppu) #x20))) 782 | 783 | (defun nes/ppu--clear-sprite-overflow (ppu) 784 | (setf (nes/ppu->ppustatus ppu) (logand (nes/ppu->ppustatus ppu) #xDF))) 785 | 786 | (defun nes/ppu--left-background-enabled-p (ppu) 787 | (nes--logbitp 1 (nes/ppu->ppumask ppu))) 788 | 789 | (defun nes/ppu--left-sprite-enabled-p (ppu) 790 | (nes--logbitp 2 (nes/ppu->ppumask ppu))) 791 | 792 | (defun nes/ppu--background-enabled-p (ppu) 793 | (nes--logbitp 3 (nes/ppu->ppumask ppu))) 794 | 795 | (defun nes/ppu--sprite-enabled-p (ppu) 796 | (nes--logbitp 4 (nes/ppu->ppumask ppu))) 797 | 798 | (defsubst nes/ppu--current-x (p) 799 | (1- (nes/ppu->cycle p))) 800 | 801 | (defsubst nes/ppu--current-y (p) 802 | (nes/ppu->line p)) 803 | 804 | (defsubst nes/ppu--coarse-x-scroll (ppu) 805 | (logand (nes/ppu->v ppu) #b0000000000011111)) 806 | 807 | (defsubst nes/ppu--increment-coarse-x-scroll (ppu) 808 | (incf (nes/ppu->v ppu))) 809 | 810 | (defsubst nes/ppu--clear-coarse-x-scroll (ppu) 811 | (setf (nes/ppu->v ppu) (logand (nes/ppu->v ppu) #b1111111111100000))) 812 | 813 | (defsubst nes/ppu--coarse-y-scroll (ppu) 814 | (lsh (logand (nes/ppu->v ppu) #b0000001111100000) -5)) 815 | 816 | (defsubst nes/ppu--set-coarse-y-scroll (ppu y) 817 | (setf (nes/ppu->v ppu) (logior (logand (nes/ppu->v ppu) #b01111110000011111) 818 | (lsh y 5)))) 819 | 820 | (defsubst nes/ppu--fine-y-scroll (ppu) 821 | (lsh (logand (nes/ppu->v ppu) #b0111000000000000) -12)) 822 | 823 | (defsubst nes/ppu--clear-fine-y-scroll (ppu) 824 | (setf (nes/ppu->v ppu) (logand (nes/ppu->v ppu) #b1000111111111111))) 825 | 826 | (defsubst nes/ppu--increment-fine-y-scroll (ppu) 827 | (incf (nes/ppu->v ppu) #b0001000000000000)) 828 | 829 | (defsubst nes/ppu--toggle-horizontal-name-table-select (ppu) 830 | (setf (nes/ppu->v ppu) (logxor (nes/ppu->v ppu) #b0000010000000000))) 831 | 832 | (defsubst nes/ppu--toggle-vertical-name-table-select (ppu) 833 | (setf (nes/ppu->v ppu) (logxor (nes/ppu->v ppu) #b0000100000000000))) 834 | 835 | (defun nes/ppu--display-options () 836 | (let ((options (make-vector 256 '(nil nil nil)))) 837 | (dotimes (i (length nes/colors)) 838 | (aset options (+ i nes/ppu:COLOR-START-OFFSET) (aref nes/colors i))) 839 | options)) 840 | 841 | (defun nes/ppu--render-set-cell (x y c) 842 | "see `gamegrid-set-cell' 843 | 844 | In this library, `insert-char' and `delete-char' are not necessary in set-cell. 845 | Because all grid uses only `? ' 846 | " 847 | (save-excursion 848 | (let ((buffer-read-only nil)) 849 | (goto-char (1+ (gamegrid-cell-offset x y))) 850 | (gamegrid-set-face (+ c nes/ppu:COLOR-START-OFFSET))))) 851 | 852 | (defun nes/ppu-init (buffer-name) 853 | (select-window (or (get-buffer-window buffer-name) 854 | (selected-window))) 855 | (switch-to-buffer buffer-name) 856 | (setq show-trailing-whitespace nil) 857 | (setq gamegrid-use-color t) 858 | (setq gamegrid-use-glyphs nil) 859 | (gamegrid-init-buffer nes/ppu:SCREEN-WIDTH 860 | nes/ppu:SCREEN-HEIGHT 861 | ? ) 862 | (gamegrid-init (nes/ppu--display-options))) 863 | 864 | (defun nes/ppu-set-character-ram (ppu ram) 865 | (setf (nes/ppu-bus->character-ram (nes/ppu->bus ppu)) ram)) 866 | 867 | (defun nes/ppu-transfer-sprite-data (ppu index value) 868 | (let ((address (% (+ (nes/ppu->sprite-ram-addr ppu) index) nes/ppu:SPRITE-RAM-BYTESIZE))) 869 | (aset (nes/ppu->sprite-ram ppu) address value))) 870 | 871 | (provide 'nes-ppu) 872 | -------------------------------------------------------------------------------- /nes-util.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (defsubst nes--logbitp (index byte) 4 | (declare (pure t) (side-effect-free t)) 5 | (/= (logand byte (lsh 1 index)) 0)) 6 | 7 | (provide 'nes-util) 8 | 9 | -------------------------------------------------------------------------------- /nes.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t -*- 2 | 3 | (eval-when-compile (require 'cl)) 4 | 5 | (require 'nes-cartridge) 6 | (require 'nes-cpu) 7 | (require 'nes-ppu) 8 | (require 'nes-dma) 9 | (require 'nes-keypad) 10 | (require 'nes-instruction) 11 | (require 'nes-interrupt) 12 | (require 'nes-color) 13 | (require 'nes-util) 14 | 15 | (defconst nes-buffer-name "*NES-EMULATOR*") 16 | 17 | (defvar nes--current-cartridge-filename nil) 18 | (defvar nes--current-game nil) 19 | 20 | (setq nes-mode-map 21 | (let ((map (make-sparse-keymap 'nes-mode-map))) 22 | (define-key map (kbd "q") #'nes-quit) 23 | map)) 24 | 25 | (defstruct nes 26 | (cpu nil) 27 | (ppu nil) 28 | (dma nil) 29 | (keypad nil) 30 | (cart nil) 31 | (interrupt nil)) 32 | 33 | (defun nes-setup (filename) 34 | (let ((cart (nes/cartridge-load filename)) 35 | (keypad (make-nes/keypad)) 36 | (interrupt (make-nes/interrupt)) 37 | (ram (make-vector #x0800 0)) 38 | (cpu) 39 | (ppu) 40 | (dma) 41 | ) 42 | ;; ppu 43 | (setq ppu (make-nes/ppu :interrupt interrupt)) 44 | (nes/ppu-set-character-ram ppu (copy-sequence (nes/cartridge->chr-rom cart))) 45 | (nes/ppu-init nes-buffer-name) 46 | 47 | ;; dma 48 | (setq dma (make-nes/dma :ppu ppu :ram ram)) 49 | 50 | ;; keypad 51 | (nes/keypad-init keypad nes-mode-map) 52 | 53 | ;; cpu 54 | (setq cpu (make-nes/cpu :ppu ppu :keypad keypad :interrupt interrupt :dma dma)) 55 | (nes/cpu-set-working-ram cpu ram) 56 | (nes/cpu-set-program-rom cpu (lexical-let ((cart cart)) 57 | (lambda (addr) 58 | (nes/cartridge-read-from-prg-rom cart addr)))) 59 | (nes/cpu-init cpu) 60 | 61 | (make-nes :cpu cpu :ppu ppu :dma dma :keypad keypad :cart cart :interrupt interrupt))) 62 | 63 | (defun nes-update () 64 | (let ((buffer (get-buffer nes-buffer-name))) 65 | (when (eq (current-buffer) buffer) 66 | (let ((c (nes-cpu nes--current-game)) 67 | (p (nes-ppu nes--current-game)) 68 | (d (nes-dma nes--current-game))) 69 | (nes/dma-transfer d) 70 | (dotimes (_ 1000) 71 | (dotimes (_ (* (nes/cpu-step c) 3)) 72 | (nes/ppu-step p)) 73 | ) 74 | )) 75 | (when buffer 76 | (run-at-time 0.001 nil 'nes-update)))) 77 | 78 | (defun nes-quit () 79 | (interactive) 80 | (setq nes--current-game nil) 81 | (gamegrid-kill-timer) 82 | (kill-buffer nes-buffer-name)) 83 | 84 | (define-derived-mode nes-mode nil "NES Emulator" 85 | (use-local-map nes-mode-map) 86 | (add-hook 'kill-buffer-hook 'gamegrid-kill-timer nil t) 87 | (gamegrid-kill-timer) 88 | (setq nes--current-game (nes-setup nes--current-cartridge-filename)) 89 | (run-at-time 0.001 nil 'nes-update) 90 | ) 91 | 92 | (defun nes (filename) 93 | "nes-mode keybindings: 94 | \\{nes-mode-map}" 95 | (interactive "ffilename: ") 96 | (select-window (or (get-buffer-window nes-buffer-name) 97 | (selected-window))) 98 | (switch-to-buffer nes-buffer-name) 99 | (setq nes--current-cartridge-filename filename) 100 | (nes-mode)) 101 | --------------------------------------------------------------------------------