A Documenting of my Rule 30 CHIP-8 Program This is documentation for the Rule 30 program I've written. Wanting to use my MMC more and waiting no longer for the sixth OctoJam event, I decided it was past time to write another program and I'll simply improve this program for the event. As with my other programs, only the fully annotated view will be shown here, and so follows is a view of the program when it is first loaded into the tool: 200-201 0512-0513 ________ 00FF 00255 Enable extended mode 202-203 0514-0515 # ^ _#_ A287 41607 begin I <- from 204-205 0516-0517 ^##^^#^_ FE65 65125 bar Load V0->VE; I <- I + 15 206-207 0518-0519 ^ ^ _^ A204 41476 I <- bar 208-209 0520-0521 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR 20A-20B 0522-0523 ^^^^#^_^ FD0A 64778 input VD <- key 20C-20D 0524-0525 ^ ^ _^ A204 41476 I <- bar 20E-20F 0526-0527 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR 210-211 0528-0529 ^ ^^_^ 4D02 19714 Skip next if VD <> 002 212-213 0530-0531 _######_ 7EFF 32511 VE <- VE + 255 214-215 0532-0533 ^ #^ ^ 4D08 19720 Skip next if VD <> 008 216-217 0534-0535 ^^^^^^_ 7E01 32257 VE <- VE + 001 218-219 0536-0537 ^ _^^# 8E12 36370 VE <- VE AND V1 21A-21B 0538-0539 ^ ##_# 4D0F 19727 Skip next if VD <> 015 21C-21D 0540-0541 _#__^ 123C 04668 Jump to automata 21E-21F 0542-0543 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR 220-221 0544-0545 ^^^# # 3D05 15621 Skip next if VD = 005 222-223 0546-0547 ^_ # 120A 04618 Jump to input 224-225 0548-0549 # #_#### AFBF 44991 I <- 4031 226-227 0550-0551 ^^^#### FE1E 65054 I <- I + VE 228-229 0552-0553 ^##^ _ _ F065 61541 Load V0->V0; I <- I + 01 22A-22B 0554-0555 # #_#### AFBF 44991 I <- 4031 22C-22D 0556-0557 ^^^#### FE1E 65054 I <- I + VE 22E-22F 0558-0559 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR 230-231 0560-0561 ^_ __ 8043 32835 V0 <- V0 XOR V4 232-233 0562-0563 ^#^# _ _ F055 61525 Save V0->V0; I <- I + 01 234-235 0564-0565 # #_#### AFBF 44991 I <- 4031 236-237 0566-0567 ^^^#### FE1E 65054 I <- I + VE 238-239 0568-0569 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR 23A-23B 0570-0571 ^_ # 120A 04618 Jump to input 23C-23D 0572-0573 ^^^# _^# F315 62229 automata delay <- V3 23E-23F 0574-0575 _##_###_ 6EFF 28415 VE <- 255 240-241 0576-0577 ^^ ^^ 6C00 27648 VC <- 000 242-243 0578-0579 ^^^ ^_ 7201 29185 V2 <- V2 + 001 244-245 0580-0581 #___ ^ 4278 17016 Skip next if V2 <> 120 246-247 0582-0583 _###__^ 72FC 29436 V2 <- V2 + 252 248-249 0584-0585 ^^^^^^_ 7E01 32257 prime VE <- VE + 001 24A-24B 0586-0587 # #_#### AFBF 44991 I <- 4031 24C-24D 0588-0589 ^^^#### FE1E 65054 I <- I + VE 24E-24F 0590-0591 ^##^ _ # F165 61797 Load V0->V1; I <- I + 02 250-251 0592-0593 ^ ^^^^ 8F00 36608 rule 30 begins VF <- V0 252-253 0594-0595 ^ _ _ 8011 32785 V0 <- V0 OR V1 254-255 0596-0597 #_ __ 80C3 32963 V0 <- V0 XOR VC 256-257 0598-0599 #___^^ 8CF0 36080 rule 30 ends VC <- VF 258-259 0600-0601 # #_#### AFBF 44991 I <- 4031 25A-25B 0602-0603 ^^^#### FE1E 65054 I <- I + VE 25C-25D 0604-0605 ^#^# _ _ F055 61525 Save V0->V0; I <- I + 01 25E-25F 0606-0607 _^^^^^ 3E40 15936 Skip next if VE = 064 260-261 0608-0609 _ ^_ ^ 1248 04680 Jump to prime 262-263 0610-0611 ^^ ^^^ 6E00 28160 VE <- 000 264-265 0612-0613 # #_#### AFBF 44991 draw I <- 4031 266-267 0614-0615 ^^^#### FE1E 65054 I <- I + VE 268-269 0616-0617 ##_^__#_ D2EF 53999 Draw 08x15 at V2,VE; VF <- XOR 26A-26B 0618-0619 ^^^###_ 7E0F 32271 VE <- VE + 015 26C-26D 0620-0621 ####^ 3E3C 15932 Skip next if VE = 060 26E-26F 0622-0623 __^ _^ 1264 04708 Jump to draw 270-271 0624-0625 #_#_#^## AFFB 45051 I <- 4091 272-273 0626-0627 ##_^ _^ D2E4 53988 Draw 08x04 at V2,VE; VF <- XOR 274-275 0628-0629 ^^^^^### FF07 65287 delay VF <- delay 276-277 0630-0631 #^# ^ _ E4A1 58529 Skip next if V4 <> key 278-279 0632-0633 ______ _ 00FD 00253 Exit program 27A-27B 0634-0635 ^^^^^^ 3F00 16128 Skip next if VF = 000 27C-27D 0636-0637 __# _^ 1274 04724 Jump to delay 27E-27F 0638-0639 #__ _#_ 4277 17015 Skip next if V2 <> 119 280-281 0640-0641 ______ 00FC 00252 Scroll <- by 04 282-283 0642-0643 _#__^ 123C 04668 Jump to automata 284-285 0644-0645 ___ 00E0 00224 restart Clear the screen 286-287 0646-0647 ^ # 1202 04610! from Jump to begin 288 0648 ###### 3F 063 289 0649 ### # 74 116 28A 0650 # # 0A 010 28B 0651 # 01 001 The register usage is as follows: V0 Cell manipulation. V1 Cell manipulation; hold a boundary. V2 Horizontal positioning of all drawing. V3 Hold the delay register delay value. V4 Hold a constant for cell manipulation and a key code. V5 Unused. V6 Unused. V7 Unused. V8 Unused. V9 Unused. VA Unused. VB Unused. VC Store a previous cell. VD Store a key. VE Vertical positioning of all drawing. VF Store a temporary result. As this program was written by me, I will discuss the history of the program to a point, as opposed to being a more bare examination. This is the only Rule 30 program written in CHIP-8 I'm aware of. One goal of this program was being small and making novel use of some CHIP-8 particulars. The registers were chosen for the following reasons: V0 Ease of access. V1 Ease of access. V2 Ease of access. V3 Ease of access. V4 Ease of access. V5 Unused. V6 Unused. V7 Unused. V8 Unused. V9 Unused. VA Unused. VB Unused. VC Distance from lower registers. VD Distance from lower registers. VE Distance from lower registers. VF Unimportance and distance from lower registers. The program begins by instating Super CHIP-8 mode and initializing all registers to their decided values or zero. Note that register fifteen isn't initialized purely because the constant FE was to be used as part of the interface and it was recognized early that modifying this instruction would result in the desired constant; by using FE for the interface, the drawing code is simplified: 200-201 0512-0513 ________ 00FF 00255 Enable extended mode 202-203 0514-0515 # ^ _#_ A287 41607 begin I <- from 204-205 0516-0517 ^##^^#^_ FE65 65125 bar Load V0->VE; I <- I + 15 Following that, the bar is drawn at the top of the screen and in the same position that will be used for all drawing in the pattern entry routine: 206-207 0518-0519 ^ ^ _^ A204 41476 I <- bar 208-209 0520-0521 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR Pattern entry involves accepting a key, moving up for two, moving down for eight, and changing the value of a cell for five; the initial value of register one is used for wrapping bounds protection; observe that the bar is erased even when nothing else takes place: 20A-20B 0522-0523 ^^^^#^_^ FD0A 64778 input VD <- key 20C-20D 0524-0525 ^ ^ _^ A204 41476 I <- bar 20E-20F 0526-0527 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR 210-211 0528-0529 ^ ^^_^ 4D02 19714 Skip next if VD <> 002 212-213 0530-0531 _######_ 7EFF 32511 VE <- VE + 255 214-215 0532-0533 ^ #^ ^ 4D08 19720 Skip next if VD <> 008 216-217 0534-0535 ^^^^^^_ 7E01 32257 VE <- VE + 001 218-219 0536-0537 ^ _^^# 8E12 36370 VE <- VE AND V1 Key fifteen is used to end pattern entry and I believe this is a nice key for it: 21A-21B 0538-0539 ^ ##_# 4D0F 19727 Skip next if VD <> 015 21C-21D 0540-0541 _#__^ 123C 04668 Jump to automata The bar is then redrawn in its new position, which may be the same position, and what remains of the input routine is dedicated to key five, cell modification: 21E-21F 0542-0543 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR 220-221 0544-0545 ^^^# # 3D05 15621 Skip next if VD = 005 222-223 0546-0547 ^_ # 120A 04618 Jump to input The proper cell is loaded into register zero by initializing I to the base and then adding what is the current vertical coordinate; due to the implicit movement of I by loading, it must be set again. The location of 4031 was chosen in the same way I choose the last three locations for the decimal representation storage; it is chosen because I can't run out of space by using this location and it requires no adjustment based on other program changes: 224-225 0548-0549 # #_#### AFBF 44991 I <- 4031 226-227 0550-0551 ^^^#### FE1E 65054 I <- I + VE 228-229 0552-0553 ^##^ _ _ F065 61541 Load V0->V0; I <- I + 01 22A-22B 0554-0555 # #_#### AFBF 44991 I <- 4031 22C-22D 0556-0557 ^^^#### FE1E 65054 I <- I + VE The current value of the cell is then drawn, in order to erase any live cells; it has no effect on cells already dead. The current value is then flipped by the one stored in register four and it is stored back: 22E-22F 0558-0559 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR 230-231 0560-0561 ^_ __ 8043 32835 V0 <- V0 XOR V4 232-233 0562-0563 ^#^# _ _ F055 61525 Save V0->V0; I <- I + 01 The I is then set again, as before, and used to draw the new value properly, again only having an effect if the cell is living. The input routine then loops to the beginning: 234-235 0564-0565 # #_#### AFBF 44991 I <- 4031 236-237 0566-0567 ^^^#### FE1E 65054 I <- I + VE 238-239 0568-0569 ##_^ ^_ D2E1 53985 Draw 08x01 at V2,VE; VF <- XOR 23A-23B 0570-0571 ^_ # 120A 04618 Jump to input The automata routine is the next and final stage of the program. It begins by setting a delay and initializing the higher registers to appropriate values; note that register two was previously used for the horizontal position of the initial pattern and so can simply be incremented; this is part of the simplification that comes about from using the FE bar pattern for the pattern entry interface: 23C-23D 0572-0573 ^^^# _^# F315 62229 automata delay <- V3 23E-23F 0574-0575 _##_###_ 6EFF 28415 VE <- 255 240-241 0576-0577 ^^ ^^ 6C00 27648 VC <- 000 242-243 0578-0579 ^^^ ^_ 7201 29185 V2 <- V2 + 001 If the horizontal position has reached its end, it is decremented by four: 244-245 0580-0581 #___ ^ 4278 17016 Skip next if V2 <> 120 246-247 0582-0583 _###__^ 72FC 29436 V2 <- V2 + 252 The prime routine implements the core of the cellular automata, with the automata routine merely being the initialization and looping around it. The vertical position is incremented and again used to collect the appropriate cells. Note that location 4031 is used instead of location 4032 so that the last memory address accessed is 4095, the end of memory: 248-249 0584-0585 ^^^^^^_ 7E01 32257 prime VE <- VE + 001 24A-24B 0586-0587 # #_#### AFBF 44991 I <- 4031 24C-24D 0588-0589 ^^^#### FE1E 65054 I <- I + VE 24E-24F 0590-0591 ^##^ _ # F165 61797 Load V0->V1; I <- I + 02 These four instructions form the core of the program and can be modified to change the particular cellular automata implemented. The current cell value is stored in register fifteen as a temporary place; the current cell is compared with the right cell and register twelve stores the left cell; after this, the old current cell is stored as the new left cell, for the next iteration. In doing this, the pattern can be manipulated in-place, yet as if it was modified all at once: 250-251 0592-0593 ^ ^^^^ 8F00 36608 rule 30 begins VF <- V0 252-253 0594-0595 ^ _ _ 8011 32785 V0 <- V0 OR V1 254-255 0596-0597 #_ __ 80C3 32963 V0 <- V0 XOR VC 256-257 0598-0599 #___^^ 8CF0 36080 rule 30 ends VC <- VF The new current cell value is then stored and the prime routine is entered again if not all cells have been updated: 258-259 0600-0601 # #_#### AFBF 44991 I <- 4031 25A-25B 0602-0603 ^^^#### FE1E 65054 I <- I + VE 25C-25D 0604-0605 ^#^# _ _ F055 61525 Save V0->V0; I <- I + 01 25E-25F 0606-0607 _^^^^^ 3E40 15936 Skip next if VE = 064 260-261 0608-0609 _ ^_ ^ 1248 04680 Jump to prime When all cells have been updated, drawing is performed. Register fourteen is set to zero and loops quatrice, displaying the tallest sprite, fifteen high, in order to display the first sixty cells of the new pattern. Note that eight loopings of eight high would've resulted in shorter code, however this is maximally efficient and it is so rare to be able to draw an entire column of the screen in the most efficient five drawings that I opted to do so: 262-263 0610-0611 ^^ ^^^ 6E00 28160 VE <- 000 264-265 0612-0613 # #_#### AFBF 44991 draw I <- 4031 266-267 0614-0615 ^^^#### FE1E 65054 I <- I + VE 268-269 0616-0617 ##_^__#_ D2EF 53999 Draw 08x15 at V2,VE; VF <- XOR 26A-26B 0618-0619 ^^^###_ 7E0F 32271 VE <- VE + 015 26C-26D 0620-0621 ####^ 3E3C 15932 Skip next if VE = 060 26E-26F 0622-0623 __^ _^ 1264 04708 Jump to draw As it's a constant which cells are those last four, I is set explicitly rather than indirectly by register fourteen and those last four are drawn: 270-271 0624-0625 #_#_#^## AFFB 45051 I <- 4091 272-273 0626-0627 ##_^ _^ D2E4 53988 Draw 08x04 at V2,VE; VF <- XOR The remaining delay is then exhausted in turn with checking for key one, as that's a good value that was readily available in register four; pressing that key ends the program: 274-275 0628-0629 ^^^^^### FF07 65287 delay VF <- delay 276-277 0630-0631 #^# ^ _ E4A1 58529 Skip next if V4 <> key 278-279 0632-0633 ______ _ 00FD 00253 Exit program 27A-27B 0634-0635 ^^^^^^ 3F00 16128 Skip next if VF = 000 27C-27D 0636-0637 __# _^ 1274 04724 Jump to delay The screen is scrolled if the horizontal position is at its end and the automata routine loops; it's noteworthy that this scrolling behavior is what influenced the idea of implementing such a cellular automata program initially: 27E-27F 0638-0639 #__ _#_ 4277 17015 Skip next if V2 <> 119 280-281 0640-0641 ______ 00FC 00252 Scroll <- by 04 282-283 0642-0643 _#__^ 123C 04668 Jump to automata These instructions are not currently used, but will be later; the remainder of the program are those initial values for the lower registers. Note here that the from is overlapped with this ending jump and this case warrants a special display mechanism, indicating that from is not of location 0646 but 0647; orange is the chosen color to contrast nicely with yellow and I noticed a pleasant orange form of decreasing powers of two with one subtracted, RGB 255 127 63: 284-285 0644-0645 ___ 00E0 00224 restart Clear the screen 286-287 0646-0647 ^ # 1202 04610! from Jump to begin 288 0648 ###### 3F 063 289 0649 ### # 74 116 28A 0650 # # 0A 010 28B 0651 # 01 001 Those reading this with color terminal codes note that the code for orange is subtly incorrect, to appease those terminal emulators, all I know of, which implement these extended colors incorrectly. This appeasement is disgusting to me, but expected from incompetent programmers who don't read the standards they claim to implement and then insist on incorrect behavior to be compatible. I was initially wanting to wait to write this, to write it for the sixth OctoJam, but decided I'd already waited too long to write another program with my MMC, so long that I'd needed to relearn my own key tables. Instead, this is an initial implementation and I will improve it for that event. It will be noticed that the I adjustment could be made a routine, that the speed could be made able to be changed interactively, that the interface could have a notice, that the cellular automata does not draw on the rightmost column, and that a discovered peculiarity results in an unpleasant pattern eventually overtaking the screen and that I intend to correct this by experimenting with behavior in having the edges of the pattern both be living or only one of which would be living. These changes and perhaps others will be made during that event. It was refreshing to write this program over the time that I did, making a few changes or advancements over many days instead of writing it all in a lone sitting. It was nice to finally implement the hidden address name display in my MMC for this, also. .