From: Ruby Quiz Date: 2006-07-29T02:50:25+09:00 Subject: [QUIZ] Chip-8 (#88) The three rules of Ruby Quiz: 1. Please do not post any solutions or spoiler discussion for this quiz until 48 hours have passed from the time on this message. 2. Support Ruby Quiz by submitting ideas as often as you can: http://www.rubyquiz.com/ 3. Enjoy! Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone on Ruby Talk follow the discussion. Please reply to the original quiz message, if you can. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= by Ethan Price CHIP-8 was an interpreted language used in the 1970's for basic games like Pong. While it is technically an interpreted language, it defines many components a real computer has, such as registers and a processor. Today, like many other gaming consoles, it is kept alive by emulators allowing it to be played on most any modern computer. Emulation is where raw operation codes (the most low-level way of feeding instructions to a computer) are translated by another computer, giving a totally different system the ability to run programs not designed for it. Your job is to make an emulator. It will only cover some basic parts of it, but all emulators have to start somewhere. I have written a simple program, and will give you a list of all the opcodes I used and what they are supposed to do. We won't worry about graphics right now, just simple operations. Four things need to be done: 1. Make the emulator read the file. All opcodes are four digit hex numbers, but those will need to be split. 2. Set up the registers and other assorted things so they can be modified. 3. Interpret the function of the opcode in Ruby. 4. Dump all the registers so you can check and make sure everything went well. Like I said, all opcodes are four digit long hex numbers. So you read four hex digits worth of data, process it, then move on to the next four digits. There are 16 registers (areas where data can be stored). They are named V0 through VF, and are all eight bits wide. VF is usually used as a carry for math operations. Here is the list of opcodes you will need to interpret: NNN is an address KK is an 8 bit constant X and Y are two 4 bits constants 0000 This is nothing. If you see this, it should mean that you are at the end of the file and have nothing left to read, so you therefore need to exit gracefully. 1NNN Jump to the address NNN of the file 3XKK Skip next instruction if VX == KK 6XKK VX = KK 7XKK VX = VX + KK (Note: The documentation does not mention this, but I would assume that VF would act as a carry if needed. The included program will not need a carry for this function, so providing for one is purely optional.) 8XY0 VX = VY 8XY1 VX = VX OR VY 8XY2 VX = VX AND VY 8XY3 VX = VX XOR VY 8XY4 VX = VX + VY, Sets VF to 1 if there is a carry, 0 if there isn't 8XY5 VX = VX - VY, VF is set to 0 if there is a borrow, 1 if there isn't. 8X06 VX = VX SHIFT RIGHT 1 (VX=VX/2), VF is set to the value of the least significant bit of VX before the shift. 8XY7 VX = VY - VX, VF is set to 0 if there is a borrow, 1 if there isn't. 8X0E VX = VX SHIFT LEFT 1 (VX=VX*2), VF is set to the value of the most significant bit of VX before the shift. CXKK VX = Random number AND KK Lets explain how to read these opcodes. Take opcode 1NNN for example. Since the 1NNN starts with 1, we know that no matter what comes after it we will be doing a jump instruction. Same with the other codes. For the opcode 8XY2, we know if an opcode starts with 8 and ends in 2 it will be the operation "VX = VX AND VY". The variables NNN, KK, X and Y are a little different. They can represent any hex number (0-F), and also are used to note how big of chunks to read them in. For NNN, you will want to read those as a three digit hex number. Lets do an example opcode (in hex): 1234. Since it starts with 1, we know it is a "Jump to address NNN" command. The three numbers after that (the NNN part), are 234, so we know to jump to location 234 in the file (this is a hex number, not decimal). KK and X/Y are similar, just for different sized chunks. While I'm on the subject, let's talk about addresses in detail. Your current address in the file is how many bytes you are from the beginning of the file. So if I am at address 008 in the file, I am eight bytes away from the beginning of the file, and therefore the next byte I read will be byte nine. Say we had the opcode 100F, that means we would jump to after byte 16 (it is a hex remember). If your wondering how the carries work, here are some examples. First off, addition. Say we add these two numbers (opcode 8xy4): 11111111 <--This is VX + 00000001 <--This is VY Obviously this would equal 100000000, but that is too big for one register. So what we do is make VF equal 00000001, and then roll over to 00000000. Sort of like an old car odometer. When it hits 99,999 miles, it goes back to 00,000 miles, except we are keeping track of the fact that it rolled over. Subtraction is similar (opcode 8xy5): 00000000 <-- This is VX - 00000001 <-- This is VY Since the registers can only deal with positive numbers, we have to keep it positive. So what we do is "borrow", making VX equal to 100000000. All of a sudden our subtraction works: 100000000 <--VX - 00000001 <--VY = 11111111 If we do this though, we have to make sure VF is zero to indicate that a borrow was needed. If a borrow was not needed, VF gets set to 00000001, since you didn't need the extra. Now for shifts. To sum it up, if you shift left, the far left number gets taken away, and a zero added to the far right. So 10101111 would become 01011110, because we got rid of the far left number and added a zero to the far right. Shift right is the same except going the other way (get rid of far right number, add 0 to the left). As you can see, a number disappears when you shift. VF is set to the number that disappears (obviously it can only be a 1 or 0). A full list of opcodes can be had at David Winter's page, under section 1.5: http://www.pdc.kth.se/%7Elfo/chip8/CHIP8.htm He also has a full explanation of everything, so if check there for more details. If you want to write code for an opcode that I didn't list, by all means go for it. Be sure to start reading the attached program from the beginning. To my understanding in a real Chip-8 game up to address 200 was reserved for machine code which we wont worry about. The register values you should get are: V1:01000101 V2:10111011 V3:11101100 V4:this number should be random, so do multiple runs to make sure it changes VF:00000000 A note: If everything is working as it is supposed to, you should run straight through the file once. If you are looping, I would start with double checking all your math operations. Good Luck!