# HG changeset patch # User Steve Losh # Date 1483361779 0 # Node ID e7a83e2baf5277cf40d220d43602c112247e7e17 # Parent ef47c27c647091f29f7f0f0741370b1ee4181139 Happy New Year diff -r ef47c27c6470 -r e7a83e2baf52 content/blog/2016/12/chip8-debugging-infrastructure.markdown --- a/content/blog/2016/12/chip8-debugging-infrastructure.markdown Mon Jan 02 12:55:34 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -+++ -title = "CHIP-8 in Common Lisp: Debugging Infrastructure" -snip = "What's happening inside this computer?" -date = 2016-12-31T18:50:00Z -draft = true - -+++ - -Our [CHIP-8][] emulator in Common Lisp is coming along nicely. It can play -games, and in the last post we added a disassembler so we can dump the code of -ROMs. - -In this post we'll add some low-level debugging infrastructure so we can set -breakpoints and step through code. - -The full series of posts so far: - -1. [CHIP-8 in Common Lisp: The CPU](http://stevelosh.com/blog/2016/12/chip8-cpu/) -2. [CHIP-8 in Common Lisp: Graphics](http://stevelosh.com/blog/2016/12/chip8-graphics/) -3. [CHIP-8 in Common Lisp: Input](http://stevelosh.com/blog/2016/12/chip8-input/) -4. [CHIP-8 in Common Lisp: Sound](http://stevelosh.com/blog/2016/12/chip8-sound/) - -The full emulator source is on [BitBucket][] and [GitHub][]. - -[CHIP-8]: https://en.wikipedia.org/wiki/CHIP-8 -[BitBucket]: https://bitbucket.org/sjl/cl-chip8 -[GitHub]: https://github.com/sjl/cl-chip8 - -
- -## Architecture - -The overall goal will be to keep the debugging infrastructure as separate from -possible the rest of the emulation code. Unfortunately the nature of debugging -will require us to weave it into some of the normal emulator code, but we'll try -to keep the pollution to a minimum. - -All information about the state of the debugger (e.g. breakpoints, pause status, -etc) will be stored in a separate debugger data structure. We'll define a small -API for interacting with this structure. The `chip` struct will have -a `debugger` slot and will use the debugger API to interact with it. Later on, -the graphical debugger UI will also use this API. - -## The Debugger Data Structure - -We'll start by creating a `debugger` struct and a `with-debugger` macro for it. -We'll be adding fields to this struct as we build the debugging infrastructure. - -```lisp -(defstruct debugger - ; ... - ) - -(define-with-macro debugger - ; ... - ) -``` - -We'll then add a `debugger` slot to our `chip` struct: - -```lisp -(defstruct chip - ; ... - (debugger (make-debugger) :type debugger :read-only t)) -``` - -## Pausing - -The first thing we'll add is support for pausing and unpausing execution. We'll -add a `paused` slot to the debugger: - -```lisp -(defstruct debugger - (paused nil :type boolean) - ; ... - ) -``` - -Then we'll add some simple API functions so the emulator won't have to directly -work with the debugger's slots: - -```lisp -(defun debugger-pause (debugger) - (with-debugger (debugger) - (setf paused t print-needed t))) - -(defun debugger-unpause (debugger) - (with-debugger (debugger) - (setf paused nil print-needed nil))) - -(defun debugger-toggle-pause (debugger) - (if (debugger-paused debugger) - (debugger-unpause debugger) - (debugger-pause debugger))) - -(defun debugger-paused-p (debugger) - (debugger-paused debugger)) -``` - -Now we'll modify `emulate-cycle` to check if the debugger is paused before -actually running an instruction: - -```lisp -(defun emulate-cycle (chip) - (with-chip (chip) - (if (debugger-paused-p debugger) ; NEW - (sleep 10/1000) ; NEW - (let ((instruction (cat-bytes - (aref memory program-counter) - (aref memory (1+ program-counter))))) - (zapf program-counter (chop 12 (+ % 2))) - (dispatch-instruction chip instruction))) - nil)) -``` - -The timer thread will need to skip the timer decrements whenever the debugger is -paused: - -```lisp -(defun run-timers (chip) - (with-chip (chip) - (iterate - (while running) - (when (not (debugger-paused-p debugger)) ; NEW - (decrement-timers chip)) - (sleep 1/60)))) -``` - -Next we'll modify the sound thread to be silent when paused, otherwise pausing -during a buzz would result in perpetual buzzing: - -```lisp -(defun run-sound (chip) - (portaudio:with-audio - (portaudio:with-default-audio-stream - (audio-stream 0 1 - :sample-format :float - :sample-rate +sample-rate+ - :frames-per-buffer +audio-buffer-size+) - (with-chip (chip) - (iterate (with buffer = (make-audio-buffer)) - (with angle = 0.0) - (with rate = (audio-rate 440)) - (while running) - (if (and (plusp sound-timer) - (not (debugger-paused-p debugger))) ; NEW - (progn - (setf angle (funcall (audio-buffer-filler chip) - buffer rate angle)) - (portaudio:write-stream audio-stream buffer)) - (sleep +audio-buffer-time+)))))) - nil) -``` - -We'll also need a way to actually pause and unpause the debugger. We could do -it through NREPL or SLIME, but it'll be much easier if we just add a key for it -over on the Qt side of things: - -```lisp -(define-override (screen key-release-event) (ev) - (let* ((key (q+:key ev)) - (pad-key (pad-key-for key))) - (if pad-key - (chip8::keyup chip pad-key) - (qtenumcase key - ((q+:qt.key_escape) (die screen)) - ((q+:qt.key_f1) (chip8::reset chip)) - ((q+:qt.key_space) ; NEW - (chip8::debugger-toggle-pause (chip8::debugger chip)) ; NEW - (t (pr :unknown-key (format nil "~X" key)))))) - (stop-overriding)) -``` - -Now we can pause and unpause the emulator with the space bar, which is a handy -feature to have all on its own. - -## Stepping - -## Printing - -## Breakpoints - -## UI - -## Result - -## Future - - -```lisp -``` - -```lisp -``` - -```lisp -``` - -```lisp -``` - -```lisp -``` - -```lisp -``` diff -r ef47c27c6470 -r e7a83e2baf52 content/blog/2016/12/chip8-disassembly.markdown --- a/content/blog/2016/12/chip8-disassembly.markdown Mon Jan 02 12:55:34 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,405 +0,0 @@ -+++ -title = "CHIP-8 in Common Lisp: Disassembly" -snip = "What's in a ROM?" -date = 2016-12-31T14:50:00Z -draft = true - -+++ - -In the previous posts we looked at how to emulate a [CHIP-8][] CPU with Common -Lisp. After adding a screen, input, and sound the core of the emulator is -essentially complete. - -I've been guiding you through the code step by step and it might look pretty -simple, but that's only because I went down all the dead ends myself first. In -practice, when you're writing an emulator for a system you'll need a way to -debug the execution of code, and the first step is to be able to *read* the -code, so let's look at how to add a disassembler to our simple CHIP-8 emulator. - -The full series of posts so far: - -1. [CHIP-8 in Common Lisp: The CPU](http://stevelosh.com/blog/2016/12/chip8-cpu/) -2. [CHIP-8 in Common Lisp: Graphics](http://stevelosh.com/blog/2016/12/chip8-graphics/) -3. [CHIP-8 in Common Lisp: Input](http://stevelosh.com/blog/2016/12/chip8-input/) -4. [CHIP-8 in Common Lisp: Sound](http://stevelosh.com/blog/2016/12/chip8-sound/) - -The full emulator source is on [BitBucket][] and [GitHub][]. - -[CHIP-8]: https://en.wikipedia.org/wiki/CHIP-8 -[BitBucket]: https://bitbucket.org/sjl/cl-chip8 -[GitHub]: https://github.com/sjl/cl-chip8 - -
- -## Disassembling Single Instructions - -The first thing we'll need is a way to take a single instruction like `#x8055` -and turn it into something we can read. The easiest way to do this seemed to be -to copy the dispatch loop from the CPU emulator and turn it into a disassembly -function: - -```lisp -(defun disassemble-instruction (instruction) - (flet ((v (n) (symb 'v (format nil "~X" n)))) - (let ((_x__ (ldb (byte 4 8) instruction)) - (__x_ (ldb (byte 4 4) instruction)) - (___x (ldb (byte 4 0) instruction)) - (__xx (ldb (byte 8 0) instruction)) - (_xxx (ldb (byte 12 0) instruction))) - (case (logand #xF000 instruction) - (#x0000 (case instruction - (#x00E0 '(cls)) - (#x00EE '(ret)))) - (#x1000 `(jp ,_xxx)) - (#x2000 `(call ,_xxx)) - (#x3000 `(se ,(v _x__) ,__xx)) - (#x4000 `(sne ,(v _x__) ,__xx)) - (#x5000 (case (logand #x000F instruction) - (#x0 `(se ,(v _x__) ,(v __x_))))) - (#x6000 `(ld ,(v _x__) ,__xx)) - (#x7000 `(add ,(v _x__) ,__xx)) - (#x8000 (case (logand #x000F instruction) - (#x0 `(ld ,(v _x__) ,(v __x_))) - (#x1 `(or ,(v _x__) ,(v __x_))) - (#x2 `(and ,(v _x__) ,(v __x_))) - (#x3 `(xor ,(v _x__) ,(v __x_))) - (#x4 `(add ,(v _x__) ,(v __x_))) - (#x5 `(sub ,(v _x__) ,(v __x_))) - (#x6 `(shr ,(v _x__) ,(v __x_))) - (#x7 `(subn ,(v _x__) ,(v __x_))) - (#xE `(shl ,(v _x__) ,(v __x_))))) - (#x9000 (case (logand #x000F instruction) - (#x0 `(sne ,(v _x__) ,(v __x_))))) - (#xA000 `(ld i ,_xxx)) - (#xB000 `(jp ,(v 0) ,_xxx)) - (#xC000 `(rnd ,(v _x__) ,__xx)) - (#xD000 `(drw ,(v _x__) ,(v __x_) ,___x)) - (#xE000 (case (logand #x00FF instruction) - (#x9E `(skp ,(v _x__))) - (#xA1 `(sknp ,(v _x__))))) - (#xF000 (case (logand #x00FF instruction) - (#x07 `(ld ,(v _x__) dt)) - (#x0A `(ld ,(v _x__) k)) - (#x15 `(ld dt ,(v _x__))) - (#x18 `(ld st ,(v _x__))) - (#x1E `(add i ,(v _x__))) - (#x29 `(ld f ,(v _x__))) - (#x33 `(ld b ,(v _x__))) - (#x55 `(ld (mem i) ,_x__)) - (#x65 `(ld ,_x__ (mem i))))))))) -``` - -There are a lot of other ways we could have done this, like making a proper -parser or adding functionality to `define-opcode`, but since there's not that -many instructions I think this is pretty reasonable. Now we can pass in a raw, -two-byte instruction and get out something readable: - -``` -[SBCL] CHIP8> (disassemble-instruction #x8055) -(SUB V0 V5) - -[SBCL] CHIP8> (disassemble-instruction #x4077) -(SNE V0 119) -``` - -## Disassembling Entire ROMs - -Disassembling a single instruction will be useful, but it would also be nice to -disassemble an entire ROM at once to see what its code looks like. Let's make -a little helper function to handle that: - -```lisp -(defun dump-disassembly (array &optional (start 0) (end (length array))) - (iterate - (for i :from start :below end :by 2) - (print-disassembled-instruction array i) - (sleep 0.001))) -``` - -The `sleep` is there because Neovim's terminal seems to shit the bed if you dump -too much text at it at once. Computers are garbage. - -Other that than, `dump-disassembly` is pretty straightforward: just iterate -through the array of instructions two bytes at a time and print the information. -Let's look at the printing function now: - -```lisp -(defun print-disassembled-instruction (array index) - (destructuring-bind (address instruction disassembly) - (instruction-information array index) - (let ((*print-base* 16)) - (format t "~3,'0X: ~4,'0X ~24A~%" - address - instruction - (or disassembly ""))))) -``` - -Once again we'll delegate to a helper function. -`print-disassembled-instruction` just handles the string formatting to dump an -instruction to the screen. Running it for a single instruction would print -something like: - -``` -Address Disassembly - | | - v v -200: 8055 (SUB V0 V5) - ^ - | - Raw instruction -``` - -The helper function `instruction-information` is simple, but we'll be using it -in the future for something else, so it's nice to have: - -```lisp -(defun instruction-information (array index) - (let ((instruction (retrieve-instruction array index))) - (list index - instruction - (disassemble-instruction instruction)))) -``` - -It just takes an address and memory array and returns a list of: - -* The address -* The raw instruction at the address -* The disassembly for that instruction - -`retrieve-instruction` is simple (for now): - -```lisp -(defun retrieve-instruction (array index) - (cat-bytes (aref array index) - (aref array (1+ index)))) -``` - -These functions *could* be combined into a single, bigger function, but I'm -a strong believer in having each function do exactly one thing only. And as -we'll see, each of these "simple" tasks is going to get more complicated in the -real world. - -Now we can dump the disassembly for a ROM to see how it works: - -``` -(run "roms/ufo.rom") ; stores the current chip struct in *c* - -(dump-disassembly (chip-memory *c*) #x200 #x220) -200: A2CD (LD I 2CD) -202: 6938 (LD V9 38) -204: 6A08 (LD VA 8) -206: D9A3 (DRW V9 VA 3) -208: A2D0 (LD I 2D0) -20A: 6B00 (LD VB 0) -20C: 6C03 (LD VC 3) -20E: DBC3 (DRW VB VC 3) -210: A2D6 (LD I 2D6) -212: 641D (LD V4 1D) -214: 651F (LD V5 1F) -216: D451 (DRW V4 V5 1) -218: 6700 (LD V7 0) -21A: 680F (LD V8 F) -21C: 22A2 (CALL 2A2) -21E: 22AC (CALL 2AC) -``` - -## Sprites - -Take a look at `print-disassembled-instruction` again: - -```lisp -(defun print-disassembled-instruction (array index) - (destructuring-bind (address instruction disassembly) - (instruction-information array index) - (let ((*print-base* 16)) - (format t "~3,'0X: ~4,'0X ~24A~%" - address - instruction - (or disassembly ""))))) -``` - -Notice how we used `(or disassembly "")` instead of just passing in the -disassembly. If you look back at `disassemble-instruction` you'll see it uses -normal `case` statements, not `ecase`, so if the instruction doesn't match any -valid opcodes it will return `nil`. - -The CHIP-8 (like [most computers][neumann]) uses the same memory to hold both -program code (instructions) and data. This includes things like player health, -score, location, and most importantly: the sprites that will be drawn on the -screen. - -[neumann]: https://en.wikipedia.org/wiki/Von_Neumann_architecture - -Unfortunately there's no way to know for sure whether a given memory location -contains an instruction (and thus needs to be disassembled) or is intended to be -a piece of data. Indeed, someone like [Mel][] could conceivably figure out -a way to use a particular sequence of instructions as a sprite! So our -disassembler will just always show the disassembly for anything that *might* be -an instruction. - -[Mel]: http://www.catb.org/jargon/html/story-of-mel.html - -But with that said, we can probably make some educated guesses. If we had some -way to visualize what a hunk of memory would look like *if* it were rendered as -a sprite, we could probably figure out where most of the program's sprites are -kept. It's unlikely that any given sequence of instructions would just *happen* -to look like a ghost from Pac Man or something. - -We could add a separate function to draw out the sprite data, but the CHIP-8's -sprites are so simple that we can just tack it on to the disassembly output. - -[Remember][cowgod-disp] that each byte of memory defines one eight-pixel-wide -row of a sprite, and that `DRW X, Y, Size` will draw `Size` rows of a sprite -using contiguous bytes in memory. So if memory contains something like this (at -the location specified by the index register): - -[cowgod-disp]: http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#2.4 - -``` -Address Data -#x300 #b11110000 -#x301 #b00010000 -#x302 #b11110000 -#x303 #b00010000 -#x304 #b11110000 -``` - -A `DRW X, Y, 5` instruction would draw a `3` sprite to the screen: - -
-    ████
-       █
-    ████
-       █
-    ████
-
- -It would be trivial to simply render the bits of any given instruction as spaces -and some other ASCII character and tack it onto the end of the disassembly, but -there's a snag: instructions are *two* bytes each, but each row in a sprite is -*one* byte long. Our sprites would get pretty mangled if we printed two of -their rows per line of disassembly — for example, `4` would look like this: - -``` - byte 1 byte 2 - 1111111122222222 -064: 9090 (SNE V0 V9) █ █ █ █ -066: F010 ████ █ -068: 10F0 (JP F0) █ -``` - -Not ideal. One option would be to make every instruction of disassembly two -lines long, but that's painful to read when trying to look at the code portions -of the ROM. - -We can get around this with a delightful little hack: using characters from -[Unicode Block Elements][block] to cram two rows of sprite data into a single -line of output. Let's start by defining a `bit-diagram` function that will take -a two-byte-wide integer and return an ASCII diagram of its bits: - -[block]: https://en.wikipedia.org/wiki/Block_Elements - -```lisp -(defun bit-diagram (integer) - (iterate (for high-bit :from 15 :downto 8) - (for low-bit :from 7 :downto 0) - (for hi = (logbitp high-bit integer)) - (for lo = (logbitp low-bit integer)) - (collect (cond - ((and hi lo) #\full_block) - (hi #\upper_half_block) - (lo #\lower_half_block) - (t #\space)) - :result-type 'string))) -``` - -```lisp -; Example rows of sprite data: -; 11110000 -; 11001100 - -(bit-diagram #b1111000011001100) -"██▀▀▄▄ " -``` - -Now that we've got this we can easily add it into our disassembly functions: - -```lisp -(defun instruction-information (array index) - (let ((instruction (retrieve-instruction array index))) - (list index - instruction - (disassemble-instruction instruction) - (bit-diagram instruction)))) ; NEW - -(defun print-disassembled-instruction (array index) - (destructuring-bind (address instruction disassembly bits) - (instruction-information array index) - (let ((*print-base* 16)) - ; NEW - (format t "~3,'0X: ~4,'0X ~24A ~8A~%" - address - instruction - (or disassembly "") - bits)))) ; NEW -``` - -Now when we dump the disassembly of a ROM we'll also see what each instruction -would look like if drawn as a sprite. For program code this will tend to look -like garbage (unless some crazy person has managed to write code that also works -as sprites): - -
-    200: A2CD (LD I 2CD)               █▄▀ ▄▄▀▄
-    202: 6938 (LD V9 38)                ▀█▄█  ▀
-    204: 6A08 (LD VA 8)                 ▀▀ █ ▀
-    206: D9A3 (DRW V9 VA 3)            █▀▄▀▀ ▄█
-    208: A2D0 (LD I 2D0)               █▄▀▄  ▀
-    20A: 6B00 (LD VB 0)                 ▀▀ ▀ ▀▀
-    20C: 6C03 (LD VC 3)                 ▀▀ ▀▀▄▄
-    20E: DBC3 (DRW VB VC 3)            ██ ▀▀ ██
-
- -But when we look at areas of the ROM that *do* contain sprites, they look pretty -recognizable: - -
-    050: F090                          █▀▀█
-    052: 9090 (SNE V0 V9)              █  █
-    054: F020                          ▀▀█▀
-    056: 6020 (LD V0 20)                ▀█
-    058: 2070 (CALL 70)                 ▄█▄
-    05A: F010                          ▀▀▀█
-    05C: F080                          █▀▀▀
-    05E: F0F0                          ████
-    060: 10F0 (JP F0)                  ▄▄▄█
-    062: 10F0 (JP F0)                  ▄▄▄█
-    064: 9090 (SNE V0 V9)              █  █
-    066: F010                          ▀▀▀█
-    068: 10F0 (JP F0)                  ▄▄▄█
-    06A: 80F0 (LD V0 VF)               █▄▄▄
-    06C: 10F0 (JP F0)                  ▄▄▄█
-    06E: F080                          █▀▀▀
-
- -Human eyes are pretty good at picking out patterns, so when you're scrolling -through a disassembled ROM it's pretty easy to tell which sections are sprites -and which are data, even if it's not perfectly rendered. - -## Result - -We've now got a way to dump the disassembly of a ROM to see what its code and -data look like. - -We can also inspect the rest of our emulator's state at runtime with NREPL or -SLIME by running things like `(chip-program-counter *c*)`. - -## Future - -Manually querying for information and dumping the disassembly isn't very -ergonomic, so in the future we'll look at adding: - -* Debugging infrastructure like pausing and breakpoints -* A graphical debugger/disassembly viewer - -As well as a few other niceties like menus for loading ROMs, etc. diff -r ef47c27c6470 -r e7a83e2baf52 content/blog/2017/01/chip8-debugging-infrastructure.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/content/blog/2017/01/chip8-debugging-infrastructure.markdown Mon Jan 02 12:56:19 2017 +0000 @@ -0,0 +1,206 @@ ++++ +title = "CHIP-8 in Common Lisp: Debugging Infrastructure" +snip = "What's happening inside this computer?" +date = 2016-12-31T18:50:00Z +draft = true + ++++ + +Our [CHIP-8][] emulator in Common Lisp is coming along nicely. It can play +games, and in the last post we added a disassembler so we can dump the code of +ROMs. + +In this post we'll add some low-level debugging infrastructure so we can set +breakpoints and step through code. + +The full series of posts so far: + +1. [CHIP-8 in Common Lisp: The CPU](http://stevelosh.com/blog/2016/12/chip8-cpu/) +2. [CHIP-8 in Common Lisp: Graphics](http://stevelosh.com/blog/2016/12/chip8-graphics/) +3. [CHIP-8 in Common Lisp: Input](http://stevelosh.com/blog/2016/12/chip8-input/) +4. [CHIP-8 in Common Lisp: Sound](http://stevelosh.com/blog/2016/12/chip8-sound/) + +The full emulator source is on [BitBucket][] and [GitHub][]. + +[CHIP-8]: https://en.wikipedia.org/wiki/CHIP-8 +[BitBucket]: https://bitbucket.org/sjl/cl-chip8 +[GitHub]: https://github.com/sjl/cl-chip8 + +
+ +## Architecture + +The overall goal will be to keep the debugging infrastructure as separate from +possible the rest of the emulation code. Unfortunately the nature of debugging +will require us to weave it into some of the normal emulator code, but we'll try +to keep the pollution to a minimum. + +All information about the state of the debugger (e.g. breakpoints, pause status, +etc) will be stored in a separate debugger data structure. We'll define a small +API for interacting with this structure. The `chip` struct will have +a `debugger` slot and will use the debugger API to interact with it. Later on, +the graphical debugger UI will also use this API. + +## The Debugger Data Structure + +We'll start by creating a `debugger` struct and a `with-debugger` macro for it. +We'll be adding fields to this struct as we build the debugging infrastructure. + +```lisp +(defstruct debugger + ; ... + ) + +(define-with-macro debugger + ; ... + ) +``` + +We'll then add a `debugger` slot to our `chip` struct: + +```lisp +(defstruct chip + ; ... + (debugger (make-debugger) :type debugger :read-only t)) +``` + +## Pausing + +The first thing we'll add is support for pausing and unpausing execution. We'll +add a `paused` slot to the debugger: + +```lisp +(defstruct debugger + (paused nil :type boolean) + ; ... + ) +``` + +Then we'll add some simple API functions so the emulator won't have to directly +work with the debugger's slots: + +```lisp +(defun debugger-pause (debugger) + (with-debugger (debugger) + (setf paused t print-needed t))) + +(defun debugger-unpause (debugger) + (with-debugger (debugger) + (setf paused nil print-needed nil))) + +(defun debugger-toggle-pause (debugger) + (if (debugger-paused debugger) + (debugger-unpause debugger) + (debugger-pause debugger))) + +(defun debugger-paused-p (debugger) + (debugger-paused debugger)) +``` + +Now we'll modify `emulate-cycle` to check if the debugger is paused before +actually running an instruction: + +```lisp +(defun emulate-cycle (chip) + (with-chip (chip) + (if (debugger-paused-p debugger) ; NEW + (sleep 10/1000) ; NEW + (let ((instruction (cat-bytes + (aref memory program-counter) + (aref memory (1+ program-counter))))) + (zapf program-counter (chop 12 (+ % 2))) + (dispatch-instruction chip instruction))) + nil)) +``` + +The timer thread will need to skip the timer decrements whenever the debugger is +paused: + +```lisp +(defun run-timers (chip) + (with-chip (chip) + (iterate + (while running) + (when (not (debugger-paused-p debugger)) ; NEW + (decrement-timers chip)) + (sleep 1/60)))) +``` + +Next we'll modify the sound thread to be silent when paused, otherwise pausing +during a buzz would result in perpetual buzzing: + +```lisp +(defun run-sound (chip) + (portaudio:with-audio + (portaudio:with-default-audio-stream + (audio-stream 0 1 + :sample-format :float + :sample-rate +sample-rate+ + :frames-per-buffer +audio-buffer-size+) + (with-chip (chip) + (iterate (with buffer = (make-audio-buffer)) + (with angle = 0.0) + (with rate = (audio-rate 440)) + (while running) + (if (and (plusp sound-timer) + (not (debugger-paused-p debugger))) ; NEW + (progn + (setf angle (funcall (audio-buffer-filler chip) + buffer rate angle)) + (portaudio:write-stream audio-stream buffer)) + (sleep +audio-buffer-time+)))))) + nil) +``` + +We'll also need a way to actually pause and unpause the debugger. We could do +it through NREPL or SLIME, but it'll be much easier if we just add a key for it +over on the Qt side of things: + +```lisp +(define-override (screen key-release-event) (ev) + (let* ((key (q+:key ev)) + (pad-key (pad-key-for key))) + (if pad-key + (chip8::keyup chip pad-key) + (qtenumcase key + ((q+:qt.key_escape) (die screen)) + ((q+:qt.key_f1) (chip8::reset chip)) + ((q+:qt.key_space) ; NEW + (chip8::debugger-toggle-pause (chip8::debugger chip)) ; NEW + (t (pr :unknown-key (format nil "~X" key)))))) + (stop-overriding)) +``` + +Now we can pause and unpause the emulator with the space bar, which is a handy +feature to have all on its own. + +## Stepping + +## Printing + +## Breakpoints + +## UI + +## Result + +## Future + + +```lisp +``` + +```lisp +``` + +```lisp +``` + +```lisp +``` + +```lisp +``` + +```lisp +``` diff -r ef47c27c6470 -r e7a83e2baf52 content/blog/2017/01/chip8-disassembly.markdown --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/content/blog/2017/01/chip8-disassembly.markdown Mon Jan 02 12:56:19 2017 +0000 @@ -0,0 +1,405 @@ ++++ +title = "CHIP-8 in Common Lisp: Disassembly" +snip = "What's in a ROM?" +date = 2016-12-31T14:50:00Z +draft = true + ++++ + +In the previous posts we looked at how to emulate a [CHIP-8][] CPU with Common +Lisp. After adding a screen, input, and sound the core of the emulator is +essentially complete. + +I've been guiding you through the code step by step and it might look pretty +simple, but that's only because I went down all the dead ends myself first. In +practice, when you're writing an emulator for a system you'll need a way to +debug the execution of code, and the first step is to be able to *read* the +code, so let's look at how to add a disassembler to our simple CHIP-8 emulator. + +The full series of posts so far: + +1. [CHIP-8 in Common Lisp: The CPU](http://stevelosh.com/blog/2016/12/chip8-cpu/) +2. [CHIP-8 in Common Lisp: Graphics](http://stevelosh.com/blog/2016/12/chip8-graphics/) +3. [CHIP-8 in Common Lisp: Input](http://stevelosh.com/blog/2016/12/chip8-input/) +4. [CHIP-8 in Common Lisp: Sound](http://stevelosh.com/blog/2016/12/chip8-sound/) + +The full emulator source is on [BitBucket][] and [GitHub][]. + +[CHIP-8]: https://en.wikipedia.org/wiki/CHIP-8 +[BitBucket]: https://bitbucket.org/sjl/cl-chip8 +[GitHub]: https://github.com/sjl/cl-chip8 + +
+ +## Disassembling Single Instructions + +The first thing we'll need is a way to take a single instruction like `#x8055` +and turn it into something we can read. The easiest way to do this seemed to be +to copy the dispatch loop from the CPU emulator and turn it into a disassembly +function: + +```lisp +(defun disassemble-instruction (instruction) + (flet ((v (n) (symb 'v (format nil "~X" n)))) + (let ((_x__ (ldb (byte 4 8) instruction)) + (__x_ (ldb (byte 4 4) instruction)) + (___x (ldb (byte 4 0) instruction)) + (__xx (ldb (byte 8 0) instruction)) + (_xxx (ldb (byte 12 0) instruction))) + (case (logand #xF000 instruction) + (#x0000 (case instruction + (#x00E0 '(cls)) + (#x00EE '(ret)))) + (#x1000 `(jp ,_xxx)) + (#x2000 `(call ,_xxx)) + (#x3000 `(se ,(v _x__) ,__xx)) + (#x4000 `(sne ,(v _x__) ,__xx)) + (#x5000 (case (logand #x000F instruction) + (#x0 `(se ,(v _x__) ,(v __x_))))) + (#x6000 `(ld ,(v _x__) ,__xx)) + (#x7000 `(add ,(v _x__) ,__xx)) + (#x8000 (case (logand #x000F instruction) + (#x0 `(ld ,(v _x__) ,(v __x_))) + (#x1 `(or ,(v _x__) ,(v __x_))) + (#x2 `(and ,(v _x__) ,(v __x_))) + (#x3 `(xor ,(v _x__) ,(v __x_))) + (#x4 `(add ,(v _x__) ,(v __x_))) + (#x5 `(sub ,(v _x__) ,(v __x_))) + (#x6 `(shr ,(v _x__) ,(v __x_))) + (#x7 `(subn ,(v _x__) ,(v __x_))) + (#xE `(shl ,(v _x__) ,(v __x_))))) + (#x9000 (case (logand #x000F instruction) + (#x0 `(sne ,(v _x__) ,(v __x_))))) + (#xA000 `(ld i ,_xxx)) + (#xB000 `(jp ,(v 0) ,_xxx)) + (#xC000 `(rnd ,(v _x__) ,__xx)) + (#xD000 `(drw ,(v _x__) ,(v __x_) ,___x)) + (#xE000 (case (logand #x00FF instruction) + (#x9E `(skp ,(v _x__))) + (#xA1 `(sknp ,(v _x__))))) + (#xF000 (case (logand #x00FF instruction) + (#x07 `(ld ,(v _x__) dt)) + (#x0A `(ld ,(v _x__) k)) + (#x15 `(ld dt ,(v _x__))) + (#x18 `(ld st ,(v _x__))) + (#x1E `(add i ,(v _x__))) + (#x29 `(ld f ,(v _x__))) + (#x33 `(ld b ,(v _x__))) + (#x55 `(ld (mem i) ,_x__)) + (#x65 `(ld ,_x__ (mem i))))))))) +``` + +There are a lot of other ways we could have done this, like making a proper +parser or adding functionality to `define-opcode`, but since there's not that +many instructions I think this is pretty reasonable. Now we can pass in a raw, +two-byte instruction and get out something readable: + +``` +[SBCL] CHIP8> (disassemble-instruction #x8055) +(SUB V0 V5) + +[SBCL] CHIP8> (disassemble-instruction #x4077) +(SNE V0 119) +``` + +## Disassembling Entire ROMs + +Disassembling a single instruction will be useful, but it would also be nice to +disassemble an entire ROM at once to see what its code looks like. Let's make +a little helper function to handle that: + +```lisp +(defun dump-disassembly (array &optional (start 0) (end (length array))) + (iterate + (for i :from start :below end :by 2) + (print-disassembled-instruction array i) + (sleep 0.001))) +``` + +The `sleep` is there because Neovim's terminal seems to shit the bed if you dump +too much text at it at once. Computers are garbage. + +Other that than, `dump-disassembly` is pretty straightforward: just iterate +through the array of instructions two bytes at a time and print the information. +Let's look at the printing function now: + +```lisp +(defun print-disassembled-instruction (array index) + (destructuring-bind (address instruction disassembly) + (instruction-information array index) + (let ((*print-base* 16)) + (format t "~3,'0X: ~4,'0X ~24A~%" + address + instruction + (or disassembly ""))))) +``` + +Once again we'll delegate to a helper function. +`print-disassembled-instruction` just handles the string formatting to dump an +instruction to the screen. Running it for a single instruction would print +something like: + +``` +Address Disassembly + | | + v v +200: 8055 (SUB V0 V5) + ^ + | + Raw instruction +``` + +The helper function `instruction-information` is simple, but we'll be using it +in the future for something else, so it's nice to have: + +```lisp +(defun instruction-information (array index) + (let ((instruction (retrieve-instruction array index))) + (list index + instruction + (disassemble-instruction instruction)))) +``` + +It just takes an address and memory array and returns a list of: + +* The address +* The raw instruction at the address +* The disassembly for that instruction + +`retrieve-instruction` is simple (for now): + +```lisp +(defun retrieve-instruction (array index) + (cat-bytes (aref array index) + (aref array (1+ index)))) +``` + +These functions *could* be combined into a single, bigger function, but I'm +a strong believer in having each function do exactly one thing only. And as +we'll see, each of these "simple" tasks is going to get more complicated in the +real world. + +Now we can dump the disassembly for a ROM to see how it works: + +``` +(run "roms/ufo.rom") ; stores the current chip struct in *c* + +(dump-disassembly (chip-memory *c*) #x200 #x220) +200: A2CD (LD I 2CD) +202: 6938 (LD V9 38) +204: 6A08 (LD VA 8) +206: D9A3 (DRW V9 VA 3) +208: A2D0 (LD I 2D0) +20A: 6B00 (LD VB 0) +20C: 6C03 (LD VC 3) +20E: DBC3 (DRW VB VC 3) +210: A2D6 (LD I 2D6) +212: 641D (LD V4 1D) +214: 651F (LD V5 1F) +216: D451 (DRW V4 V5 1) +218: 6700 (LD V7 0) +21A: 680F (LD V8 F) +21C: 22A2 (CALL 2A2) +21E: 22AC (CALL 2AC) +``` + +## Sprites + +Take a look at `print-disassembled-instruction` again: + +```lisp +(defun print-disassembled-instruction (array index) + (destructuring-bind (address instruction disassembly) + (instruction-information array index) + (let ((*print-base* 16)) + (format t "~3,'0X: ~4,'0X ~24A~%" + address + instruction + (or disassembly ""))))) +``` + +Notice how we used `(or disassembly "")` instead of just passing in the +disassembly. If you look back at `disassemble-instruction` you'll see it uses +normal `case` statements, not `ecase`, so if the instruction doesn't match any +valid opcodes it will return `nil`. + +The CHIP-8 (like [most computers][neumann]) uses the same memory to hold both +program code (instructions) and data. This includes things like player health, +score, location, and most importantly: the sprites that will be drawn on the +screen. + +[neumann]: https://en.wikipedia.org/wiki/Von_Neumann_architecture + +Unfortunately there's no way to know for sure whether a given memory location +contains an instruction (and thus needs to be disassembled) or is intended to be +a piece of data. Indeed, someone like [Mel][] could conceivably figure out +a way to use a particular sequence of instructions as a sprite! So our +disassembler will just always show the disassembly for anything that *might* be +an instruction. + +[Mel]: http://www.catb.org/jargon/html/story-of-mel.html + +But with that said, we can probably make some educated guesses. If we had some +way to visualize what a hunk of memory would look like *if* it were rendered as +a sprite, we could probably figure out where most of the program's sprites are +kept. It's unlikely that any given sequence of instructions would just *happen* +to look like a ghost from Pac Man or something. + +We could add a separate function to draw out the sprite data, but the CHIP-8's +sprites are so simple that we can just tack it on to the disassembly output. + +[Remember][cowgod-disp] that each byte of memory defines one eight-pixel-wide +row of a sprite, and that `DRW X, Y, Size` will draw `Size` rows of a sprite +using contiguous bytes in memory. So if memory contains something like this (at +the location specified by the index register): + +[cowgod-disp]: http://devernay.free.fr/hacks/chip8/C8TECH10.HTM#2.4 + +``` +Address Data +#x300 #b11110000 +#x301 #b00010000 +#x302 #b11110000 +#x303 #b00010000 +#x304 #b11110000 +``` + +A `DRW X, Y, 5` instruction would draw a `3` sprite to the screen: + +
+    ████
+       █
+    ████
+       █
+    ████
+
+ +It would be trivial to simply render the bits of any given instruction as spaces +and some other ASCII character and tack it onto the end of the disassembly, but +there's a snag: instructions are *two* bytes each, but each row in a sprite is +*one* byte long. Our sprites would get pretty mangled if we printed two of +their rows per line of disassembly — for example, `4` would look like this: + +``` + byte 1 byte 2 + 1111111122222222 +064: 9090 (SNE V0 V9) █ █ █ █ +066: F010 ████ █ +068: 10F0 (JP F0) █ +``` + +Not ideal. One option would be to make every instruction of disassembly two +lines long, but that's painful to read when trying to look at the code portions +of the ROM. + +We can get around this with a delightful little hack: using characters from +[Unicode Block Elements][block] to cram two rows of sprite data into a single +line of output. Let's start by defining a `bit-diagram` function that will take +a two-byte-wide integer and return an ASCII diagram of its bits: + +[block]: https://en.wikipedia.org/wiki/Block_Elements + +```lisp +(defun bit-diagram (integer) + (iterate (for high-bit :from 15 :downto 8) + (for low-bit :from 7 :downto 0) + (for hi = (logbitp high-bit integer)) + (for lo = (logbitp low-bit integer)) + (collect (cond + ((and hi lo) #\full_block) + (hi #\upper_half_block) + (lo #\lower_half_block) + (t #\space)) + :result-type 'string))) +``` + +```lisp +; Example rows of sprite data: +; 11110000 +; 11001100 + +(bit-diagram #b1111000011001100) +"██▀▀▄▄ " +``` + +Now that we've got this we can easily add it into our disassembly functions: + +```lisp +(defun instruction-information (array index) + (let ((instruction (retrieve-instruction array index))) + (list index + instruction + (disassemble-instruction instruction) + (bit-diagram instruction)))) ; NEW + +(defun print-disassembled-instruction (array index) + (destructuring-bind (address instruction disassembly bits) + (instruction-information array index) + (let ((*print-base* 16)) + ; NEW + (format t "~3,'0X: ~4,'0X ~24A ~8A~%" + address + instruction + (or disassembly "") + bits)))) ; NEW +``` + +Now when we dump the disassembly of a ROM we'll also see what each instruction +would look like if drawn as a sprite. For program code this will tend to look +like garbage (unless some crazy person has managed to write code that also works +as sprites): + +
+    200: A2CD (LD I 2CD)               █▄▀ ▄▄▀▄
+    202: 6938 (LD V9 38)                ▀█▄█  ▀
+    204: 6A08 (LD VA 8)                 ▀▀ █ ▀
+    206: D9A3 (DRW V9 VA 3)            █▀▄▀▀ ▄█
+    208: A2D0 (LD I 2D0)               █▄▀▄  ▀
+    20A: 6B00 (LD VB 0)                 ▀▀ ▀ ▀▀
+    20C: 6C03 (LD VC 3)                 ▀▀ ▀▀▄▄
+    20E: DBC3 (DRW VB VC 3)            ██ ▀▀ ██
+
+ +But when we look at areas of the ROM that *do* contain sprites, they look pretty +recognizable: + +
+    050: F090                          █▀▀█
+    052: 9090 (SNE V0 V9)              █  █
+    054: F020                          ▀▀█▀
+    056: 6020 (LD V0 20)                ▀█
+    058: 2070 (CALL 70)                 ▄█▄
+    05A: F010                          ▀▀▀█
+    05C: F080                          █▀▀▀
+    05E: F0F0                          ████
+    060: 10F0 (JP F0)                  ▄▄▄█
+    062: 10F0 (JP F0)                  ▄▄▄█
+    064: 9090 (SNE V0 V9)              █  █
+    066: F010                          ▀▀▀█
+    068: 10F0 (JP F0)                  ▄▄▄█
+    06A: 80F0 (LD V0 VF)               █▄▄▄
+    06C: 10F0 (JP F0)                  ▄▄▄█
+    06E: F080                          █▀▀▀
+
+ +Human eyes are pretty good at picking out patterns, so when you're scrolling +through a disassembled ROM it's pretty easy to tell which sections are sprites +and which are data, even if it's not perfectly rendered. + +## Result + +We've now got a way to dump the disassembly of a ROM to see what its code and +data look like. + +We can also inspect the rest of our emulator's state at runtime with NREPL or +SLIME by running things like `(chip-program-counter *c*)`. + +## Future + +Manually querying for information and dumping the disassembly isn't very +ergonomic, so in the future we'll look at adding: + +* Debugging infrastructure like pausing and breakpoints +* A graphical debugger/disassembly viewer + +As well as a few other niceties like menus for loading ROMs, etc.