‹ go back
Racket and Chip-8.
If all goes well, I will hopefully be attending the University of British Columbia this coming September - probably for computer science. As a first year, I’m going to end up taking CPSC 110. Unfortunately, CPSC 110 happens to be firmly entrenched in the language of Racket. Because I’m also plan on challenging (credit by exam, they call it apparently) the course, and was thinking of getting some preliminary practise with Racket. It also seems like an interesting language overall, and I was a bit bored after adding an editor to diveintoht.ml. So, I decided to try something in Racket.
That something is currently the skeleton of a Chip-8 emulator. Chip-8 was apparently historically used on the COSMAC VIP and run on the Chip-8 virtual machine, a sentence which I am not entirely sure I understand myself. I’ve had about zero experience with trying to emulate or do anything lower level than CS50’s C sections. So, I thought this would be a fun challenge.
Was trying to juggle learning a new programming language, the syntax of which was completely unlike anything I’d worked with before, and wrapping my brain around hexadecimal and opcoes at the same time a good idea? Probably not, but I’ve managed to do a decent chunk of work, and I thought I’d write about it today.
(printf “So, Racket.”)
After blundering through some install instructions, I had Racket. And just because I decided to, I didn’t install DrRacket either. I originally had the contents of above printf as ‘Hello World’. I don’t think I’ve ever done a proper Hello World program1, and hey, there was no time like the present. So began my stumbling into Racket.
The Racket language docs are rather complicated for a complete beginner, which I was. I also had the misfortune of only finding Teach Yourself Racket and The Racket Guide just after muddling my way through too many Stack Overflow pages.
Racket is apparently ‘functional programming’ (oh look - a buzzword!), so that was another challenge I had to deal with. Most lines are something like
([function] a b). This is extremely different from what I’ve worked with before. This means it took me a solid couple minutes while trying my first Pythagorean’s project to even realize that I was unconsciously doing
(a + b). The proper
(+ a b). The constant parenthesis matching is something that coc-pairs really helped with, so I’m happy I didn’t have to deal with constantly reaching around. Also, something else that was interesting - no semicolons! The whole wrapping expressions and definitions concept wasn’t too bad either.
Another unique issue I encountered while trying to grasp the basics was the fact that Racket is a subset of Scheme, which is itself a dialect of Lisp. Racket seems to be a very niche language that seems to only have resources from university course websites. It’s therefore sometimes difficult to find the answer to a question. I have to first look for something like ‘print variable in racket’ (very basic, I know). Then, when that doesn’t come up with many results that aren’t just the Racket docs (which at that point weren’t very comprehensible). I then have to google for Scheme, which seems a little more widely used, so that’s something to keep in mind.
I find that having such a unique syntax with the functions and parentheses completely different from something like Python was probably helpful. I didn’t end up confusing functions with each other - as I did when doing CS50 with C and constantly forgetting that the print function was actually printf - and that I actually needed semicolons (or when I switched back to Python and found myself accidentally writing C). I’m still very much a beginner, and I have very far to go, but Racket is certainly a lot less painful than it was the first few days.
I think in decimal, and I can attempt to convert with bytes, but oh boy, dealing with hex and trying to manipulate the data read in for each opcode was a pain.
Part of the problem was simply that I didn’t have much (or any, really) experience with hexadecimal or assembly. Most of this was also down to the fact that I forgot that I had to divide by hexadecimal 100 to get the correct digit I wanted, or that I needed to check if the variable was equal to a hex equivalent, instead of a decimal number.
The most challenging part of understanding Chip-8 so far, I find, was definitely masking. All the binary operations that go into decoding just one opcode are rather tricky. It also didn’t entirely help that I was unfamiliar with what the Racket syntax was when looking at example emulators. This ended up causing documentation tangents every time I found a new function. Just yesterday, I figured out a problem I’d been having for two days where apparently I was reading bytes from out of bounds. The issue? I had masked the value properly and gotten the correct digit, but it was stored in the hundredths place of the entire hex number. That meant that I was reading at the position 768 (or #x300 in hex) instead of at the correct register, 3. This was because I’d ignored an implementation’s division by #x100 (256 in decimal) and misguidedly tried to use the value itself.
Speaking of registers, I also had to properly learn how to access things from places in memory in a way that I didn’t have to do with lists in Python. Well, for one, I didn’t make giant byte strings with entire ROMs in Python, but I’d have slightly more of a clue what to do in Python as opposed to Racket. Oh well.
In the end, with the help of several resources that I’ll acknowledge a bit later so they’re all in once place, I managed to figure out what the example was doing. I somehow also figured out why it was doing it2, and how I could do so in my own program. It got to the point where my parents were questioning why I was trying to divide seemingly random numbers by 16 on pieces of scrap paper while I was taking a break, but hey, I figured it out.
At the moment, I’m feeling a bit more acquainted with Racket. Nowhere near the point that I’d have needed to challenge CPSC 110, but somewhere, at least. Interestingly enough, I apparently went in the opposite direction that 110 would have gone. While 110’s past papers reveal a lot of data structures, I instead focused on some lower level applications. A mental note to review data structures later when I finish this emulator.
I should also point out that most of my emulator has been based, or at least somewhat referenced from Austin Bourgerie’s emulator. It seemed to be the only emulator in Racket I was able to find that wasn’t too incomplete. [edit: also found theo-lw’s recently. Was helpful.] It pointed me in the right direction in several tricky places. My approaches for getting specific bytes and positions for specific opcodes and the general program was heavily inspired by it. I’d also like to recommend Thomas Greene’s, aka Cowgod’s, reference on Chip-8. Clear and concise, and was helpful when I encountered new concepts.
I’m nowhere near done with my emulator. As of writing this, I have the reading of the ROM and the beginnings of the opcode processing for codes starting with characters 0 through 7. I hope to finish the opcodes sometime this week and see where I can get myself with graphics and input. From preliminary research, graphics doesn’t seem too bad - I can probably just do those in terminal with half blocks ASCII characters for each row. Input, though, looks a bit tricky. Apparently, Chip-8 itself has a bunch of inconsistencies with how implementations handle input. Besides, I’m not entirely sure how to tackle input in the terminal without messing up display at the same time.3
It’s been a fun detour from web and tinkering with NPM though, and while I’ve been trying to learn the basics of Racket and Chip-8, I’m also learning a lot.
At this point, I feel like Hello World programs are kind of the baseline of programming one needs to know. Similar to the ‘vast knowledge of web design’ lines people slap on their resume, maybe Hello Worlds are also part of that extremely limited but perhaps resume-able set of things people first learn to program. ↩︎
Instead of just copy-pasting code, which would have been the easiest way to get a working emulator up, but also the most useless. ↩︎
Though I was looking at Curses for Python, and that seems relatively painless. Doesn’t seem to be a proper Curses library for Racket that I’ve found ye. However, I’m also entirely unsure how package management in Racket even works. A problem for later. [edit: I found racket-ncurses, which looks like it should work!] ↩︎
‹ go back