Revision (current)

Last Updated by FractalFusion on 11/10/2013 6:00 AM

Last Updated by FractalFusion on 11/10/2013 6:00 AM

NES Wheel of Fortune game mechanics and assembly documentation. See also [Game Resources/NES/Wheel Of Fortune/Puzzle List] ! Addresses related to puzzle selection and RNG *0x0A-0x0C: RNG *0x20-0x22: Key inputs (used for randomization) **0x20: 1P key input status **0x21: 1P key input change status (a bit is 1 if and only if key is pressed, and not pressed on previous frame) **0x22: 2P key input status *0x780-0x7FC: Puzzle availability bitfield (0=available, 1=not available) Key inputs (NES standard): ||Key||Hex value|| |R|01| |L|02| |D|04| |U|08| |Start|10| |Select|20| |B|40| |A|80| ! RNG What the below does: Random stuff. Note that, as far as is known, addresses 0x341, 0x381, 0x3A1, 0x3A2 are always 0 when this is executed. Not all values are possible, even if all possible inputs (0x20-0x22) are entered. When on the screen showing the round, carry is always set at execution of 00:87F8, and the game polls all inputs 0x20-0x22. When on the spin screen, carry is always clear at execution of 00:87F8, and the game only polls input from 0x22 (0x20 and 0x21 are set to zero). %%SRC_EMBED snescom 00:8C4C:E6 0A INC $000A ... ;(carry is set on screen showing round; carry is clear when on spin screen) 00:87F8:A5 0B LDA $000B 00:87FA:65 20 ADC $0020 00:87FC:65 22 ADC $0022 00:87FE:65 21 ADC $0021 00:8800:65 0A ADC $000A 00:8802:2A ROL 00:8803:2A ROL 00:8804:2A ROL 00:8805:6D A2 03 ADC $03A2 00:8808:4D 41 03 EOR $0341 00:880B:85 0B STA $000B 00:880D:69 09 ADC #$09 00:880F:65 0C ADC $000C 00:8811:6D 81 03 ADC $0381 00:8814:49 07 EOR #$07 00:8816:65 0A ADC $000A 00:8818:65 20 ADC $0020 00:881A:ED A1 03 SBC $03A1 00:881D:85 0C STA $000C 00:881F:60 RTS %%END_EMBED ! Puzzle selection routines What the below does: A puzzle selection subroutine, which also messes with the RNG. It is as follows: Let X be a random multiple of 4 from 0 to 0x7C, and add a random number from 0 to 3 if it is not 0x74, 0x78, or 0x7C. Let Y be a random number from 0 to 7. Then take the (Y+1)th high-end bit of address 0x780+X. If it is 0, turn the 0 bit to 1 (this selects a puzzle) and return success (0). If it is 1, return failure (1). Address 0x17 starts at 8. Each time failure is returned, 0x17 is decremented and the subroutine is called again. This subroutine executes up to 8 times. %%SRC_EMBED snescom 04:8006:A5 0B LDA $000B 04:8008:0A ASL 04:8009:0A ASL 04:800A:45 0C EOR $000C 04:800C:85 0B STA $000B 04:800E:45 0A EOR $000A 04:8010:65 0B ADC $000B 04:8012:85 0C STA $000C 04:8014:A5 0B LDA $000B 04:8016:29 7C AND #$7C 04:8018:AA TAX 04:8019:C9 74 CMP #$74 04:801B:B0 0E BCS $802B 04:801D:A5 0C LDA $000C 04:801F:45 0B EOR $000B 04:8021:4A LSR 04:8022:4A LSR 04:8023:29 03 AND #$03 04:8025:85 1A STA $001A 04:8027:8A TXA 04:8028:05 1A ORA $001A 04:802A:AA TAX 04:802B:A5 0C LDA $000C 04:802D:45 17 EOR $0017 04:802F:29 07 AND #$07 04:8031:85 1A STA $001A 04:8033:A8 TAY 04:8034:BD 80 07 LDA $0780,X 04:8037:39 5D 80 AND $805D,Y 04:803A:F0 03 BEQ $803F 04:803C:A9 01 LDA #$01 04:803E:60 RTS ----- 04:803F:BD 80 07 LDA $0780,X 04:8042:19 5D 80 ORA $805D,Y 04:8045:9D 80 07 STA $0780,X 04:8048:8A TXA 04:8049:4A LSR 04:804A:4A LSR 04:804B:4A LSR 04:804C:4A LSR 04:804D:4A LSR 04:804E:85 19 STA $0019 04:8050:E6 19 INC $0019 04:8052:8A TXA 04:8053:0A ASL 04:8054:0A ASL 04:8055:0A ASL 04:8056:05 1A ORA $001A 04:8058:85 18 STA $0018 04:805A:A9 00 LDA #$00 04:805C:60 RTS -------- ;(data table used above) 04:805D:80 40 20 10 08 04 02 01 %%END_EMBED Puzzle selection routine: What the below does: If all bytes of 0x780-0x7FC are 0xFF, turn them all to 0. %%SRC_EMBED snescom 04:8065:A2 7C LDX #$7C 04:8067:BD 80 07 LDA $0780,X 04:806A:C9 FF CMP #$FF 04:806C:D0 0E BNE $807C 04:806E:CA DEX 04:806F:10 F6 BPL $8067 04:8071:A9 00 LDA #$00 04:8073:E8 INX 04:8074:9D 80 07 STA $0780,X 04:8077:E8 INX 04:8078:E0 7E CPX #$7E 04:807A:D0 F8 BNE $8074 %%END_EMBED What the below does: Address 0x17 starts at 8 and the subroutine at 04:8006 executed. Each time failure is returned, 0x17 is decremented and the subroutine is called again. This subroutine executes up to 8 times. If at any time the subroutine returns success, then a bit in the range 0x780-0x7FC has been turned from 0 to 1 (a puzzle has been selected). %%SRC_EMBED snescom 04:807C:A9 08 LDA #$08 04:807E:85 17 STA $0017 04:8080:20 06 80 JSR $8006 04:8083:F0 42 BEQ $80C7 04:8085:C6 17 DEC $0017 04:8087:D0 F7 BNE $8080 %%END_EMBED What the below does: If it still returns failure after 8 times, then take the last value of X (from description above), and let Z be the largest multiple of 32 less than X. Starting from address 0x780+Z, take the first byte that is not 0xFF (that means a puzzle is available there). If it reaches address 0x7FC and that byte is 0xFF, then go back to 0x700 and keep trying. %%SRC_EMBED snescom 04:8089:A5 0B LDA $000B 04:808B:29 7C AND #$7C 04:808D:AA TAX 04:808E:BD 80 07 LDA $0780,X 04:8091:C9 FF CMP #$FF 04:8093:D0 09 BNE $809E 04:8095:E8 INX 04:8096:E0 7D CPX #$7D 04:8098:D0 F4 BNE $808E 04:809A:A2 00 LDX #$00 04:809C:F0 F0 BEQ $808E 04:809E:85 14 STA $0014 04:80A0:8A TXA 04:80A1:4A LSR 04:80A2:4A LSR 04:80A3:4A LSR 04:80A4:4A LSR 04:80A5:4A LSR 04:80A6:85 19 STA $0019 04:80A8:E6 19 INC $0019 04:80AA:8A TXA 04:80AB:0A ASL 04:80AC:0A ASL 04:80AD:0A ASL 04:80AE:85 18 STA $0018 %%END_EMBED What the below does: From the above byte which is not 0xFF, take the first high-end bit that is 0. Turn the 0 to 1 (this selects a puzzle). %%SRC_EMBED snescom 04:80B0:A9 80 LDA #$80 04:80B2:85 15 STA $0015 04:80B4:06 14 ASL $0014 04:80B6:90 07 BCC $80BF 04:80B8:E6 18 INC $0018 04:80BA:46 15 LSR $0015 04:80BC:4C B4 80 JMP $80B4 04:80BF:BD 80 07 LDA $0780,X 04:80C2:05 15 ORA $0015 04:80C4:9D 80 07 STA $0780,X %%END_EMBED ! ROM bank of puzzles All pointers are 2-byte little-endian values to NES addresses (04:xxxx range). For example, the hex string "EC 81" points to the address 04:81EC. *04:81D8-81E9 (ROM 101E8-101FC): pointers to 81EC-89BD range (marking categories?) *04:81EA-81EB (ROM 101FA-101FB): pointer to wheel of fortune fake puzzle (title screen) *04:81EC-89BD (ROM 101FC-109CD): pointers to puzzles in 89CE-C5F4 range *04:89BE-89CD (ROM 109CE-109DD): wheel of fortune fake puzzle *04:89CE-C607 (ROM 109DE-14617): puzzle set Note: The last puzzle (1001st puzzle/puzzle #1000) at 04:C5F5-C607 (ROM 14605-14617) can never be called by the game's puzzle selection routine. Puzzles are stored in a modified ASCII form: ||Symbol||Hex value|| |space|20| |'|27| |-|2D| |A|41| |B|42| |C|43| |D|44| |E|45| |F|46| |G|47| |H|48| |I|49| |J|4A| |K|4B| |L|4C| |M|4D| |N|4E| |O|4F| |P|50| |Q|51| |R|52| |S|53| |T|54| |U|55| |V|56| |W|57| |X|58| |Y|59| |Z|5A| As well, the following rules apply to indicate new line or end of puzzle: * The last symbol before a new line has 0x80 added to its value. * The last two symbols of the puzzle both have 0x80 added to their values. For example, the following string is WHEEL@OF@FORTUNE (@ meaning newline): 57 48 45 45 CC 4F C6 46 4F 52 54 55 CE C5 W H E E L@ O F@ F O R T U N E ! Addresses related to spin mechanics *0x5A: Delay after spin stops. *0x73-0x74: Spin counter while wheel is spinning. *0x8A: Spin strength. *0xC5: Spin delay. ! Spin mechanics The spin for speed-up round uses the following randomization rules, in this order: * Set address 0x0A to 0 and 0x8A to 0. * Take address 0x0B mod 128. Add 20 to this value and store in address 0xC5 (spin wait). * From now on, run RNG each frame using spin RNG behavior (see above section on RNG). Each frame, decrement address 0xC5. If 0xC5 is not decremented to 0, do the following to 0x8A: ** If 0x8A (spin strength) is equal to 0x39, add 0x80=128 to it. If 0x8A is equal to 0x80=128, set it to 0. ** Otherwise, increment 0x8A if it is less than 0x80 and decrement 0x8A otherwise. * On the frame when 0xC5 is decremented to 0, after running RNG, take 0x0B mod 16 (this is random fudge factor). Add 0x8A mod 128 (spin strength) to this value. Add 100 to this value. Multiply this value by 4. Store this value as two-byte big-endian value in 0x73-74. * On each frame, if, after running RNG, 0x0A is even, then decrement 0x73-0x74. Otherwise, subtract two from 0x73-0x74. * On the frame when 0x73-0x74 becomes negative, assuming the spin lands on a price panel, start incrementing 0x5A (which starts at 0). * On the frame when 0x5A becomes 0x39, stop running the RNG. These values will be used for the next puzzle.