A Documenting of my Rule 30 Improved CHIP-8 Program This is a documenting of the Rule 30, Improved, program I've written. If you've not read my article documenting the Rule 30 program, 2019-08-18, then I suggest you do so. This program was written for the Octo Jam VI event and is a rewrite and heavy improvement of the previous program. As with those other programs of mine, 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: [32m200-201 [31m0512-0513 [34m________ 00FF 00255 [39m Enable extended mode [32m202-203 [31m0514-0515 [34m___ 00E0 00224 [39m Clear the screen [32m204-205 [31m0516-0517 [34m# # #### AFAF 44975 [39m I <- 4015 [32m206-207 [31m0518-0519 [34m^##^^#^# FF65 65381 [39m Load V0->VF; I <- I + 16 [32m208-209 [31m0520-0521 [34m^#^#^#^# FF55 65365 [39m Save V0->VF; I <- I + 16 [32m20A-20B [31m0522-0523 [34m^#^#^#^# FF55 65365 [39m Save V0->VF; I <- I + 16 [32m20C-20D [31m0524-0525 [34m^#^#^#^# FF55 65365 [39m Save V0->VF; I <- I + 16 [32m20E-20F [31m0526-0527 [34m^#^#^#^# FF55 65365 [39m Save V0->VF; I <- I + 16 [32m210-211 [31m0528-0529 [34m ^^ 6000 24576 [39m V0 <- 000 [32m212-213 [31m0530-0531 [34m^#^# _ _ F055 61525 [39m Save V0->V0; I <- I + 01 [32m214-215 [31m0532-0533 [34m# ^_ _^_ A295 41621 [39m I <- from [32m216-217 [31m0534-0535 [34m^##^^#^# FF65 65381 [39m Load V0->VF; I <- I + 16 [32m218-219 [31m0536-0537 [34m^ # _ ^ A228 41512 [33m entry[39m I <- bar [32m21A-21B [31m0538-0539 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR [32m21C-21D [31m0540-0541 [34m^^^^_ _ F00A 61450 [33m key[39m V0 <- key [32m21E-21F [31m0542-0543 [34m^ # _ ^ A228 41512 [39m I <- bar [32m220-221 [31m0544-0545 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR [32m222-223 [31m0546-0547 [34m ^ ____ 400F 16399 [39m Skip next if V0 <> 015 [32m224-225 [31m0548-0549 [34m _ # _^ 1254 04692 [39m Jump to phase [32m226-227 [31m0550-0551 [34m ^ _ 4008 16392 [39m Skip next if V0 <> 008 [32m228-229 [31m0552-0553 [34m ^^^^^^_ 7E01 32257 [33m bar[39m VE <- VE + 001 [32m22A-22B [31m0554-0555 [34m ^ _ 4002 16386 [39m Skip next if V0 <> 002 [32m22C-22D [31m0556-0557 [34m_######_ 7EFF 32511 [39m VE <- VE + 255 [32m22E-22F [31m0558-0559 [34m^ _^^# 8E12 36370 [39m VE <- VE AND V1 [32m230-231 [31m0560-0561 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR [32m232-233 [31m0562-0563 [34m ^^ _ _ 3005 12293 [39m Skip next if V0 = 005 [32m234-235 [31m0564-0565 [34m #__^ 121C 04636 [39m Jump to key [32m236-237 [31m0566-0567 [34m _^ __# 224E 08782 [39m Call index [32m238-239 [31m0568-0569 [34m^##^ _ _ F065 61541 [39m Load V0->V0; I <- I + 01 [32m23A-23B [31m0570-0571 [34m _^ __# 224E 08782 [39m Call index [32m23C-23D [31m0572-0573 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR [32m23E-23F [31m0574-0575 [34m^ _ __ 8023 32803 [39m V0 <- V0 XOR V2 [32m240-241 [31m0576-0577 [34m _^ _ ^ 2248 08776 [39m Call save [32m242-243 [31m0578-0579 [34m _^ __# 224E 08782 [39m Call index [32m244-245 [31m0580-0581 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR [32m246-247 [31m0582-0583 [34m #__^ 121C 04636 [39m Jump to key [32m248-249 [31m0584-0585 [34m _^ __# 224E 08782 [33m save[39m Call index [32m24A-24B [31m0586-0587 [34m^#^# _ _ F055 61525 [39m Save V0->V0; I <- I + 01 [32m24C-24D [31m0588-0589 [34m___ ___ 00EE 00238 [39m Return [32m24E-24F [31m0590-0591 [34m# #_#### AFBF 44991 [33m index[39m I <- 4031 [32m250-251 [31m0592-0593 [34m^^^#### FE1E 65054 [39m I <- I + VE [32m252-253 [31m0594-0595 [34m___ ___ 00EE 00238 [39m Return [32m254-255 [31m0596-0597 [34m#__ ^^ ^ 8DE0 36320 [33m phase[39m VD <- VE [32m256-257 [31m0598-0599 [34m_____ __ 00FB 00251 [33m scroll[39m Scroll -> by 04 [32m258-259 [31m0600-0601 [34m^^^#^# # FD15 64789 [33m automata[39m delay <- VD [32m25A-25B [31m0602-0603 [34m ^^ ^^ 6C00 27648 [39m VC <- 000 [32m25C-25D [31m0604-0605 [34m ^^ ^^^ 6E00 28160 [39m VE <- 000 [32m25E-25F [31m0606-0607 [34m_###__#_ 72FF 29439 [39m V2 <- V2 + 255 [32m260-261 [31m0608-0609 [34m^ __ # 8232 33330 [39m V2 <- V2 AND V3 [32m262-263 [31m0610-0611 [34m _^ __# 224E 08782 [33m prime[39m Call index [32m264-265 [31m0612-0613 [34m^##^ _ # F165 61797 [39m Load V0->V1; I <- I + 02 [32m266-267 [31m0614-0615 [34m^ ^^^^ 8F00 36608 [33m begin rule 30[39m VF <- V0 [32m268-269 [31m0616-0617 [34m^ _ _ 8011 32785 [39m V0 <- V0 OR V1 [32m26A-26B [31m0618-0619 [34m#_ __ 80C3 32963 [39m V0 <- V0 XOR VC [32m26C-26D [31m0620-0621 [34m#___^^ 8CF0 36080 [33m end rule 30[39m VC <- VF [32m26E-26F [31m0622-0623 [34m _^ _ ^ 2248 08776 [39m Call save [32m270-271 [31m0624-0625 [34m ^^^^^^_ 7E01 32257 [39m VE <- VE + 001 [32m272-273 [31m0626-0627 [34m _^^^^^ 3E40 15936 [39m Skip next if VE = 064 [32m274-275 [31m0628-0629 [34m __^ # 1262 04706 [39m Jump to prime [32m276-277 [31m0630-0631 [34m ^^ ^^^ 6E00 28160 [39m VE <- 000 [32m278-279 [31m0632-0633 [34m _^ __# 224E 08782 [33m draw[39m Call index [32m27A-27B [31m0634-0635 [34m##_^__#_ D2EF 53999 [39m Draw 08x15 at V2,VE; VF <- XOR [32m27C-27D [31m0636-0637 [34m ^^^###_ 7E0F 32271 [39m VE <- VE + 015 [32m27E-27F [31m0638-0639 [34m ####^ 3E3C 15932 [39m Skip next if VE = 060 [32m280-281 [31m0640-0641 [34m __#_ ^ 1278 04728 [39m Jump to draw [32m282-283 [31m0642-0643 [34m _^ __# 224E 08782 [39m Call index [32m284-285 [31m0644-0645 [34m##_^ _^ D2E4 53988 [39m Draw 08x04 at V2,VE; VF <- XOR [32m286-287 [31m0646-0647 [34m^^^^^### FF07 65287 [33m delay[39m VF <- delay [32m288-289 [31m0648-0649 [34m#^# ^ _ E4A1 58529 [39m Skip next if V4 <> key [32m28A-28B [31m0650-0651 [34m ^ ^ 1200 04608 [39m Jump to 0512 [32m28C-28D [31m0652-0653 [34m ^^^^^^ 3F00 16128 [39m Skip next if VF = 000 [32m28E-28F [31m0654-0655 [34m_ ^ _# 1286 04742 [39m Jump to delay [32m290-291 [31m0656-0657 [34m ^^ ^ 3200 12800 [39m Skip next if V2 = 000 [32m292-293 [31m0658-0659 [34m _ #_ ^ 1258 04696 [39m Jump to automata [32m294-295 [31m0660-0661 [34m _ # _# 1256 04694[38;2;255;127;63m! from[39m Jump to scroll [32m296 [31m0662 [34m ###### 3F 063 [32m297 [31m0663 [34m# 80 128 [32m298 [31m0664 [34m ## 03 003 [32m299 [31m0665 [34m ### 0E 014[39m The register usage is as follows; this omits usage of all registers at the beginning of the program: V0 Set the right pattern boundary; store a key; cell manipulation. V1 Cell manipulation; hold a boundary. V2 Hold a constant for cell manipulation; horizontal positioning of most drawing. V3 Hold a boundary V4 Hold a key code. V5 Unused. V6 Unused. V7 Unused. V8 Unused. V9 Unused. VA Unused. VB A zero source for horizontal positioning of some drawing. VC Store a previous cell. VD Manipulating the delay register VE Vertical positioning of all drawing. VF Store a temporary result; manipulating the delay register. As I'm the author of this program, I will discuss the history of it to a point, as opposed to a more bare examination. This and the previous are the only Rule 30 programs in CHIP-8 I'm aware of. This program was written with concerns other than a minimal size, although it's still rather small; to be efficient in drawing, as the opportunity for this is rare; and to make modification of those pattern boundaries simple. 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 Unimportance and distance from lower registers. VC Unimportance and distance from lower registers. VD Unimportance and distance from lower registers. VE Unimportance and distance from lower registers; sprite happenstance. VF Unimportance and distance from lower registers. This program begins by entering Super CHIP-8 mode, clearing the screen, overwriting those sixty-five final bytes of memory with zeroes by loading all registers with memory obtained directly beforehand, and then initializing all registers from the end of the program. Note that the 6000 instruction may be modified to affect the right boundary of the pattern space; note that this adds four bytes to the program's size, as that memory can safely remain untouched, otherwise: [32m200-201 [31m0512-0513 [34m________ 00FF 00255 [39m Enable extended mode [32m202-203 [31m0514-0515 [34m___ 00E0 00224 [39m Clear the screen [32m204-205 [31m0516-0517 [34m# # #### AFAF 44975 [39m I <- 4015 [32m206-207 [31m0518-0519 [34m^##^^#^# FF65 65381 [39m Load V0->VF; I <- I + 16 [32m208-209 [31m0520-0521 [34m^#^#^#^# FF55 65365 [39m Save V0->VF; I <- I + 16 [32m20A-20B [31m0522-0523 [34m^#^#^#^# FF55 65365 [39m Save V0->VF; I <- I + 16 [32m20C-20D [31m0524-0525 [34m^#^#^#^# FF55 65365 [39m Save V0->VF; I <- I + 16 [32m20E-20F [31m0526-0527 [34m^#^#^#^# FF55 65365 [39m Save V0->VF; I <- I + 16 [32m210-211 [31m0528-0529 [34m ^^ 6000 24576 [39m V0 <- 000 [32m212-213 [31m0530-0531 [34m^#^# _ _ F055 61525 [39m Save V0->V0; I <- I + 01 [32m214-215 [31m0532-0533 [34m# ^_ _^_ A295 41621 [39m I <- from [32m216-217 [31m0534-0535 [34m^##^^#^# FF65 65381 [39m Load V0->VF; I <- I + 16 The program displays the bar sprite at location 0,0; waits for a key; and afterwards erases the bar: [32m218-219 [31m0536-0537 [34m^ # _ ^ A228 41512 [33m entry[39m I <- bar [32m21A-21B [31m0538-0539 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR [32m21C-21D [31m0540-0541 [34m^^^^_ _ F00A 61450 [33m key[39m V0 <- key [32m21E-21F [31m0542-0543 [34m^ # _ ^ A228 41512 [39m I <- bar [32m220-221 [31m0544-0545 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR The next phase of the program is entered, if key fifteen has been pressed; otherwise, the bar sprite is moved up or down for keys two and eight, respectively, and the screen boundary is respected by an AND which provides wrap-around behavior. The bar is then redrawn; note that it is drawn and redrawn even in cases where it's not been moved; note that the bar sprite is taken from an instruction here, which is part of the reason register fourteen was used for this: [32m222-223 [31m0546-0547 [34m ^ ____ 400F 16399 [39m Skip next if V0 <> 015 [32m224-225 [31m0548-0549 [34m _ # _^ 1254 04692 [39m Jump to phase [32m226-227 [31m0550-0551 [34m ^ _ 4008 16392 [39m Skip next if V0 <> 008 [32m228-229 [31m0552-0553 [34m ^^^^^^_ 7E01 32257 [33m bar[39m VE <- VE + 001 [32m22A-22B [31m0554-0555 [34m ^ _ 4002 16386 [39m Skip next if V0 <> 002 [32m22C-22D [31m0556-0557 [34m_######_ 7EFF 32511 [39m VE <- VE + 255 [32m22E-22F [31m0558-0559 [34m^ _^^# 8E12 36370 [39m VE <- VE AND V1 [32m230-231 [31m0560-0561 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR The pattern toggle code is entered when key five has been pressed; otherwise, control returns to key collection. The index routine is called; the contents of the pattern array are loaded into register zero; the index routine is called again, due to implicit I movement; the pattern contents, which are zero or 2**7, are displayed once to erase them if necessary. The pattern is then toggled by XOR and the save routine is entered; index is called once more; and then the current pattern value is shown. After this, key collection resumes. Note that the pattern is displayed twice, as this is the method to ensure a live sprite is erased by itself before nothing is drawn; the other case poses no issues: [32m232-233 [31m0562-0563 [34m ^^ _ _ 3005 12293 [39m Skip next if V0 = 005 [32m234-235 [31m0564-0565 [34m #__^ 121C 04636 [39m Jump to key [32m236-237 [31m0566-0567 [34m _^ __# 224E 08782 [39m Call index [32m238-239 [31m0568-0569 [34m^##^ _ _ F065 61541 [39m Load V0->V0; I <- I + 01 [32m23A-23B [31m0570-0571 [34m _^ __# 224E 08782 [39m Call index [32m23C-23D [31m0572-0573 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR [32m23E-23F [31m0574-0575 [34m^ _ __ 8023 32803 [39m V0 <- V0 XOR V2 [32m240-241 [31m0576-0577 [34m _^ _ ^ 2248 08776 [39m Call save [32m242-243 [31m0578-0579 [34m _^ __# 224E 08782 [39m Call index [32m244-245 [31m0580-0581 [34m##_^^ ^# DBE1 56289 [39m Draw 08x01 at VB,VE; VF <- XOR [32m246-247 [31m0582-0583 [34m #__^ 121C 04636 [39m Jump to key For size efficiency, the save and index routines exist. The save routine calls index and then saves register zero to the resulting location. The index routine merely positions I according to register fourteen; there is no name for address 4031, as this is the last valid location for it to begin, and so there will never be a need to move it: [32m248-249 [31m0584-0585 [34m _^ __# 224E 08782 [33m save[39m Call index [32m24A-24B [31m0586-0587 [34m^#^# _ _ F055 61525 [39m Save V0->V0; I <- I + 01 [32m24C-24D [31m0588-0589 [34m___ ___ 00EE 00238 [39m Return [32m24E-24F [31m0590-0591 [34m# #_#### AFBF 44991 [33m index[39m I <- 4031 [32m250-251 [31m0592-0593 [34m^^^#### FE1E 65054 [39m I <- I + VE [32m252-253 [31m0594-0595 [34m___ ___ 00EE 00238 [39m Return The next phase of the program assigns the vertical coordinate of the bar sprite to register thirteen to act as the delay; the bar isn't displayed at this point in the program and the speed maps cleanly to height due to larger vertical coordinates being lower being longer delays. The screen is shifted to the right by four and the next main routine is entered by beginning the delay; register twelve is used to represent the left boundary of the pattern space and then later the leftmost cell. Register fourteen is initialized to zero; register two is set to the next horizontal coordinate; the usage of register two is interesting, as I wanted to take advantage of the binary cycling of zero to three and so sought to avoid a conditional setting of the horizontal coordinate register; mistakenly I used register two, at first thinking I would need initialize a register to four for the purpose; I noticed that 2**7-1 AND 3 would provide the desired cycle of three, two, one, and zero and so gladly I used this register for the purpose and avoided storing another byte: [32m254-255 [31m0596-0597 [34m#__ ^^ ^ 8DE0 36320 [33m phase[39m VD <- VE [32m256-257 [31m0598-0599 [34m_____ __ 00FB 00251 [33m scroll[39m Scroll -> by 04 [32m258-259 [31m0600-0601 [34m^^^#^# # FD15 64789 [33m automata[39m delay <- VD [32m25A-25B [31m0602-0603 [34m ^^ ^^ 6C00 27648 [39m VC <- 000 [32m25C-25D [31m0604-0605 [34m ^^ ^^^ 6E00 28160 [39m VE <- 000 [32m25E-25F [31m0606-0607 [34m_###__#_ 72FF 29439 [39m V2 <- V2 + 255 [32m260-261 [31m0608-0609 [34m^ __ # 8232 33330 [39m V2 <- V2 AND V3 Index is called, the rightmost two cells loaded, and the Rule 30 algorithm begins. Register fifteen stores the future leftmost cell, the new cell is calculated by V0 OR V1 XOR VC, and the new leftmost cell is stored for the next iteration. The new cell is then saved: [32m262-263 [31m0610-0611 [34m _^ __# 224E 08782 [33m prime[39m Call index [32m264-265 [31m0612-0613 [34m^##^ _ # F165 61797 [39m Load V0->V1; I <- I + 02 [32m266-267 [31m0614-0615 [34m^ ^^^^ 8F00 36608 [33m begin rule 30[39m VF <- V0 [32m268-269 [31m0616-0617 [34m^ _ _ 8011 32785 [39m V0 <- V0 OR V1 [32m26A-26B [31m0618-0619 [34m#_ __ 80C3 32963 [39m V0 <- V0 XOR VC [32m26C-26D [31m0620-0621 [34m#___^^ 8CF0 36080 [33m end rule 30[39m VC <- VF [32m26E-26F [31m0622-0623 [34m _^ _ ^ 2248 08776 [39m Call save Register fourteen is incremented and the program loops until all new cells are calculated: [32m270-271 [31m0624-0625 [34m ^^^^^^_ 7E01 32257 [39m VE <- VE + 001 [32m272-273 [31m0626-0627 [34m _^^^^^ 3E40 15936 [39m Skip next if VE = 064 [32m274-275 [31m0628-0629 [34m __^ # 1262 04706 [39m Jump to prime Drawing begins; register fourteen is reinitialized to zero, index is called, and an eight-by-fifteen sprite is drawn four times, in a loop which draws the firsty sixty cells of the pattern. Drawing is more efficient with eight eight-by-eight sprites, rather than this four eight-by-fifteen followed by one eight-by-four, but this is more efficient and such an opportunity is rarely applicable. This is another set of four bytes that is unnecessary but was chosen due to size not being the only concern: [32m276-277 [31m0630-0631 [34m ^^ ^^^ 6E00 28160 [39m VE <- 000 [32m278-279 [31m0632-0633 [34m _^ __# 224E 08782 [33m draw[39m Call index [32m27A-27B [31m0634-0635 [34m##_^__#_ D2EF 53999 [39m Draw 08x15 at V2,VE; VF <- XOR [32m27C-27D [31m0636-0637 [34m ^^^###_ 7E0F 32271 [39m VE <- VE + 015 [32m27E-27F [31m0638-0639 [34m ####^ 3E3C 15932 [39m Skip next if VE = 060 [32m280-281 [31m0640-0641 [34m __#_ ^ 1278 04728 [39m Jump to draw [32m282-283 [31m0642-0643 [34m _^ __# 224E 08782 [39m Call index [32m284-285 [31m0644-0645 [34m##_^ _^ D2E4 53988 [39m Draw 08x04 at V2,VE; VF <- XOR Finally, the delay is exhuasted and interleaved with checking four key fourteen to end the automata; key fourteen is unnecessary as I was going to use register three's value of three, but was chosen to pad to an even size and originally to use key fifteen, to be similar to the pattern entry, but I had troubles with a CHIP-8 implementation and how it registered key presses, so using a different key is the best way to solve that issue and avoid unintended multiple presses. An advantage of my desiring to avoid depending on initial register values of zero is that the program is more easily re-entered. When the delay is exhausted, the automata routine is entered again or, after four iterations, scroll is entered: [32m286-287 [31m0646-0647 [34m^^^^^### FF07 65287 [33m delay[39m VF <- delay [32m288-289 [31m0648-0649 [34m#^# ^ _ E4A1 58529 [39m Skip next if V4 <> key [32m28A-28B [31m0650-0651 [34m ^ ^ 1200 04608 [39m Jump to 0512 [32m28C-28D [31m0652-0653 [34m ^^^^^^ 3F00 16128 [39m Skip next if VF = 000 [32m28E-28F [31m0654-0655 [34m_ ^ _# 1286 04742 [39m Jump to delay [32m290-291 [31m0656-0657 [34m ^^ ^ 3200 12800 [39m Skip next if V2 = 000 [32m292-293 [31m0658-0659 [34m _ #_ ^ 1258 04696 [39m Jump to automata [32m294-295 [31m0660-0661 [34m _ # _# 1256 04694[38;2;255;127;63m! from[39m Jump to scroll Those reading this in a terminal with color note that the code for orange is subtly incorrect. Many terminal emulators don't properly parse these extended colors and this failure of such a basic thing is disgusting to me, but one should expect this from incompetent and foolish programmers; they don't read even the standards they claim to implement, yet then insist on continued incorrect behavior for backwards compatibility. I read and follow relevant standards, and yet I may be less of a bother in some cases, as such programmers tend to have pet standards they will ironically defend to no ending. This program ends with the continuation of the from name, 661, for those initial values of registers one through four: [32m296 [31m0662 [34m ###### 3F 063 [32m297 [31m0663 [34m# 80 128 [32m298 [31m0664 [34m ## 03 003 [32m299 [31m0665 [34m ### 0E 014[39m