I’ve come up with a definitive version for the programming challenge for Haskell. It is as follows:
Write an LC_RLE1 decompressor. If that works out, attempt to write an LC_LZ2 decompressor. The keyword is “attempt” as I’m pretty sure I won’t be able to finish it in time due to the difficulty. I do have a rough idea of how to attempt the latter though:
Write a function for each command. In pseudo code, it would roughly look something like this:
byte directCopy(byte input, int length)
byte directFill(byte data, int length)
byte wordFill(uint16 word, int length)
byte increasingFill(byte data, int length)
byte repeat(uint16 index, int length)
And then somehow put them together as one big “decompressLZ2” command. As for LC_RLE1 I suppose it’s just the first two functions.
The idea is that I work my way up from a simple format to a more complex format. LZ2 is quite complex though and it’s possible that I might actually get stuck solely due to my inexperience with Haskell rather than the format itself.
The biggest challenge would probably be reading out the individual bits out of a byte, as well as loading/saving a file in Haskell (it might sound trivial but I have absolutely no clue how this is done).
I presented my idea to the instructor and got his approval. Hopefully, I can finish at least the RLE1 challenge tomorrow already as I’m quite short on time.
The namesake is inspired by “Snes9x” (I never knew what the 9x really meant). I didn’t want to simply call it Super Chip-8 because there’s a variant of the Chip-8 called SCHIP (Super Chip) and it could cause confusion, and conveniently enough the name already had a number so I just stuck an “x” at the end.
How the idea came to be
Writing a (crude) SNES emulator has always been on my wishlist – so I started doing research on the SNES as well as emulation in general. Google seems to have taken notice of my emulation-related searches. During my daily routine of checking the Google app for stories to read, it suggested to me this certain article:
Upon seeing the title alone, I had three questions in mind: “What is Chip-8”, “what is Rust” and “what is WebAssembly”? I looked into the latter two and they didn’t really interest me. Looking into Chip-8 was another story, though.
I seem to have a soft spot for emulation, assembly and old/retro consoles in general. So when I read about the Chip-8 I noticed how simple it was (it’s considered the “hello world” of emulation) and I got really into the idea of writing an emulator for this. Then I found out there’s already like a thousand emulators for this. It was then that I had this impulsive thought: The Chip-8 is so simple, it could probably run even on the SNES. Amused by my own idea, I set up the project directory.
Setting up the project
To work on this project, all I needed are three tools:
And… that’s about it. The rest was up to my coding and problem-solving skills.
Prior to this project, I had never finished a SNES homebrew ROM before. The greatest extent of my homebrewing is activating the screen and displaying things, but in terms of gameplay or controller inputs, I did nothing. This was going to be a whole new experience. I decided to approach this project in my own way – take a really safe approach and define every RAM address, and (almost) every magical number. I also decided to not optimize the code, because I think I would’ve lost control over my code really fast if I did that from the very beginning already.
The display of the Chip-8 is 64×32. The display of the (NTSC) SNES is 256×224 by default. p4plus2 gave me the idea to make the screen mode 7. It has a very simple graphics format (1 byte per pixel basically) and you can scale the screen, so I could make the Chip-8 display 256×128. People won’t have to squint their eyes when using the emulator, at least. Because the (scaled up) horizontal resolution is a perfect 256 pixels wide, I decided to use HDMA to color the screen boundaries.
In order to emulate the Chip-8, I had to allocate some RAM for its registers.
These are all in the SNES direct page (except for the memory), which would allow for slightly faster access to the registers.
I also allocated some RAM for opcode parameters. Each opcode could be ‘dissected’ into 6 variables: The opcode itself and 5 parameters. All of these variables are filled in regardless of the opcode currently being processed.
Inside the main game loop, I added a subroutine call to an opcode parser. Because every opcode is exactly two bytes, it was a matter of reading an opcode, then increasing the program counter by 2 to get to the next instruction.
I made use of a pointer table which is used by !Opcode. Each opcode will have its own function, but there are certain opcodes which act as a ‘container’ for another group of opcodes. The greatest example is opcode $08 – “Arithmetic”. The SNES – in my opinion – is pretty okay with pointer tables, and I had no problems with making yet another pointer table for those specific container opcodes.
My biggest problem was thinking of a proper solution for the Chip-8 input. Officially, the system has sixteen keys. The SNES only has twelve and you can’t use all 12 for conventional input. Start and select are in the middle of the controller and you’ll have to reach them with your thumb, forcing you to stop using the D-pad or the ABXY buttons. This leaves you with 10 buttons, and there’s no real way to divide 16 buttons over 10 buttons.
Then I got the idea of using button combinations. You could map the 16 keys to the ABXY buttons by using button combinations with the L+R buttons:
However, using this controller scheme for every single ROM would be very as awkward and uncomfortable. So I got the idea to make a custom controller scheme for every playable ROM.
dw CDefault : db CDefault_end-CDefault ;boot
dw CDefault : db CDefault_end-CDefault ; fifteenpuzzle
dw CBlinky : db CBlinky_end-CBlinky
!CUP = $0800
!CDOWN = $0400
!CLEFT = $0200
!CRIGHT = $0100
!CY = $4000
!CX = $0040
!CB = $8000
!CA = $0080
!CL = $0020
!CR = $0010
dw !CY : db $01 ; Y
dw !CX : db $02 ; X
dw !CB : db $03 ; B
dw !CA : db $0C ; A
dw !CY|!CL : db $04 ; Y + L
dw !CX|!CL : db $05 ; X + L
dw !CB|!CL : db $06 ; B + L
dw !CA|!CL : db $0d ; A + L
dw !CY|!CR : db $07 ; Y + R
dw !CX|!CR : db $08 ; X + R
dw !CB|!CR : db $09 ; B + R
dw !CA|!CR : db $0e ; A + R
dw !CY|!CL|!CR : db $0a ; Y + LR
dw !CX|!CL|!CR : db $00 ; X + LR
dw !CB|!CL|!CR : db $0b ; B + LR
dw !CA|!CL|!CR : db $0f ; A + LR
;generally accepted directional keys
dw !CUP : db $02 ; up
dw !CLEFT : db $04 ; left
dw !CRIGHT : db $06 ; right
dw !CDOWN : db $08 ; down
dw !CUP : db $03
dw !CDOWN : db $06
dw !CLEFT : db $07
dw !CRIGHT : db $08
dw !CA : db $0F
dw !CB : db $0F
dw !CY : db $0F
dw !CX : db $0F
It involved extra work, but in the end, it was worth it. Technically the emulator supports all 16 keys, but at the same time, you can set intuitive controls for each playable ROM.
In the end, I am surprised at myself for being able to complete this project at all. I think the very idea of coding an emulator for the SNES kept me going on. Personally, I think it’s a pretty crazy idea.
SMWCentral’s C3 event was also nearing so I thought it was the perfect opportunity to finish a project and show it off to everyone.
The fact that I can tell people that I coded an emulator for the SNES, no matter how simple the Chip-8 system may be, is something that I can be proud of.