# HG changeset patch # User Steve Losh # Date 1483370911 0 # Node ID d5779be0dd59820c7aea8cb3dc1953ba516110c6 # Parent 668bb0e9c534a838014c33cbf9d861beb1e2adf1 Proofread diff -r 668bb0e9c534 -r d5779be0dd59 content/blog/2017/01/chip8-disassembly.markdown --- a/content/blog/2017/01/chip8-disassembly.markdown Mon Jan 02 15:11:10 2017 +0000 +++ b/content/blog/2017/01/chip8-disassembly.markdown Mon Jan 02 15:28:31 2017 +0000 @@ -10,11 +10,11 @@ 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 +I've been guiding you through the code step by step and it might look 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. +debug the execution of code. 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: @@ -91,7 +91,7 @@ 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, +many instructions I think this is reasonable. Now we can pass in a raw, two-byte instruction and get out something readable: ``` @@ -139,15 +139,16 @@ 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 -``` +
+    200: 8055 (SUB V0 V5)
+     ^    ^   ^
+     |    |   |
+     |    |  Disassembly
+     |    |
+     |   Raw instruction
+     |
+    Address
+
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: @@ -174,10 +175,9 @@ (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. +These functions *could* be combined into a single bigger function, but I'm +a strong believer in having each function do exactly one thing. And as we'll +see, each of these "simple" tasks is going to get more complicated later. Now we can dump the disassembly for a ROM to see how it works: @@ -224,18 +224,18 @@ 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. +program code (instructions) and data. Data 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. +a way to make a particular sequence of instructions perform double duty 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 @@ -247,7 +247,6 @@ 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 @@ -280,22 +279,19 @@ *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) █ -``` +
                                       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: +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 @@ -305,11 +301,10 @@ (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)) + (collect (cond ((and hi lo) #\full_block) + (hi #\upper_half_block) + (lo #\lower_half_block) + (t #\space)) :result-type 'string))) ```