To make it so you don't need to water your plants in Harvest Moon for Super Nintendo, use the PAR codes
82A8ACEA
82A8ADEA
Explanation below.
The game 'Harvest Moon' for Super Nintendo has fun elements, but also repetitive ones. One of them that wore on me was having to water your plants. If you water at night, it's not even a skill-based action mechanic since you can restore your energy and night lasts forever. I wanted to experience the game and see its content, but not have the repetitive action of watering.
Idea: patch the game so that watering is not needed
First approach was to take memory dumps before and after watering a tile, and diff them. Seems ok. But, the diffs were too noisy and I didn't know what I was looking for. How were the farm tiles stored? Was it even one byte per tile, one byte per pair of tiles, or something else entirely? I didn't even know that part yet, so I gave up on it.
Instead, I tried something way dumber- keep watering a tile with a view of live memory open, and watch for changes. I had an idea of what the storage could be- one byte per tile, arranged sequentially, so I looked for that. And I planted some test plants in an observable pattern. This plus exploiting timing of changes made it way easier to discern signal from noise and I found it.
There were not one, but two places the tile data lived. For the tile I'm standing near, the first was at 7E1100 and that one I found first. When I left the farm and came back I saw that the data got wiped and restored, so that meant it's a cache and not the primary copy.
Looking for a dupe of it in memory, I found that at 7EAC30, more likely to be the primary copy.
From experimenting I found this out about the tile storage:
- It is one byte per tile
- Tiles are stored sequentially. Nothing too crazy.
And as for what tile value means what thing, I found
Tile Value | What It Means |
0x00 | Untilled, unwatered ground |
0x01 | Untilled, unwatered ground (different graphic from above) |
0x07 | Tilled, unwatered ground |
0x08 | Tilled, watered ground |
0x58 | Unwatered potato seed |
0x59 | Watered potato seed |
0x68 | Unwatered turnip seed |
0x69 | Watered turnip seed |
(Not exhaustive.)
From the data points there are, watering a tile simply increments its value by 1. So when it rains, something must iterate through all the tiles and increment their value by 1.
To find out what that is, I set a break-on-write of 0x7EAC32, the location of the top-left plant, looking for the value to change from 0x58 to 0x59. This took me right into this code
(Paste below is multiple debugging transcripts spliced together so don't put too much stock in the reg values.)
void ProcessWeather()
...
$82/8390 8F 1E 1F 7F STA $7F1F1E[$7F:1F1E] A:0400 X:0400 Y:0400 P:eNvMxdizc
$82/8394 22 D6 89 82 JSL $8289D6[$82:89D6] A:0400 X:0400 Y:0400 P:eNvMxdizc
// Call UpdateFarmTiles, transcribed below
$82/8398 22 11 A8 82 JSL $82A811[$82:A811] A:0400 X:0400 Y:0400 P:eNvMxdizc
$82/839C 22 09 82 82 JSL $828209[$82:8209] A:0400 X:0400 Y:0400 P:eNvMxdizc
...
void UpdateFarmTiles() - $82/A811
// Preconditions:
// Weather is stored at $7E0196. 0x0 means sunny, 0x2 means rainy.
// Farm data is stored around $7EAC30.
// This function is called when you sleep, no matter the weather or if you save.
$82/A811 E2 20 SEP #$20 A:0007 X:000E Y:0002 P:envmxdizC
$82/A813 C2 10 REP #$10 A:0007 X:000E Y:0002 P:envMxdizC
$82/A815 A9 04 LDA #$04 A:0007 X:000E Y:0002 P:envMxdizC
$82/A817 8D 81 01 STA $0181 [$00:0181] A:0004 X:000E Y:0002 P:envMxdizC
$82/A81A C2 20 REP #$20 A:0004 X:000E Y:0002 P:envMxdizC
$82/A81C A0 00 00 LDY #$0000 A:0004 X:000E Y:0002 P:envmxdizC
$82/A81F A2 00 00 LDX #$0000 A:01E0 X:0400 Y:01E0 P:eNvmxdizc
StartProcessingTile:
$82/A822 5A PHY A:00D0 X:00D0 Y:01D0 P:eNvmxdizc
$82/A823 DA PHX A:00D0 X:00D0 Y:01D0 P:eNvmxdizc
$82/A824 86 82 STX $82 [$00:0082] A:00D0 X:00D0 Y:01D0 P:eNvmxdizc
$82/A826 84 84 STY $84 [$00:0084] A:00D0 X:00D0 Y:01D0 P:eNvmxdizc
$82/A828 20 3C B1 JSR $B13C [$82:B13C] A:00D0 X:00D0 Y:01D0 P:eNvmxdizc
$82/A82B E2 20 SEP #$20 A:074D X:074D Y:01D0 P:envmxdizc
// Load the state for a tile in your farm. We look at just 1 byte
$82/A82D BF E6 A4 7E LDA $7EA4E6,x[$7E:AC33] A:074D X:074D Y:01D0 P:envMxdizc
; Do various things for the different tile types.
$82/A831 D0 03 BNE $03 [$A836] A:0758 X:074D Y:01D0 P:envMxdizc
$82/A836 C9 03 CMP #$03 A:0758 X:074D Y:01D0 P:envMxdizc
$82/A838 B0 03 BCS $03 [$A83D] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A83A 4C 1B A9 JMP $A91B [$82:A91B] A:0701 X:074F Y:01D0 P:eNvMxdizc
$82/A83D C9 A0 CMP #$A0 A:0758 X:074D Y:01D0 P:envMxdizC
$82/A83F 90 03 BCC $03 [$A844] A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A844 C9 06 CMP #$06 A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A846 D0 03 BNE $03 [$A84B] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A84B C9 07 CMP #$07 A:0758 X:074D Y:01D0 P:envMxdizC ; Is tilled soil?
$82/A84D F0 55 BEQ $55 [$A8A4] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A84F C9 08 CMP #$08 A:0758 X:074D Y:01D0 P:envMxdizC
$82/A851 D0 03 BNE $03 [$A856] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A856 C9 1E CMP #$1E A:0758 X:074D Y:01D0 P:envMxdizC
$82/A858 F0 4A BEQ $4A [$A8A4] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A85A C9 1F CMP #$1F A:0758 X:074D Y:01D0 P:envMxdizC
$82/A85C D0 03 BNE $03 [$A861] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A861 C9 1D CMP #$1D A:0758 X:074D Y:01D0 P:envMxdizC
$82/A863 D0 03 BNE $03 [$A868] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A868 C9 20 CMP #$20 A:0758 X:074D Y:01D0 P:envMxdizC
$82/A86A B0 03 BCS $03 [$A86F] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A86F C9 39 CMP #$39 A:0758 X:074D Y:01D0 P:envMxdizC
$82/A871 D0 03 BNE $03 [$A876] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A876 C9 53 CMP #$53 A:0758 X:074D Y:01D0 P:envMxdizC
$82/A878 D0 03 BNE $03 [$A87D] A:0758 X:074D Y:01D0 P:envMxdizC
$82/A87D C9 61 CMP #$61 A:0758 X:074D Y:01D0 P:envMxdizC
$82/A87F D0 03 BNE $03 [$A884] A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A884 C9 6F CMP #$6F A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A886 D0 03 BNE $03 [$A88B] A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A88B C9 79 CMP #$79 A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A88D D0 03 BNE $03 [$A892] A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A892 C9 7C CMP #$7C A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A894 D0 03 BNE $03 [$A899] A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A899 C9 70 CMP #$70 A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A89B B0 69 BCS $69 [$A906] A:0758 X:074D Y:01D0 P:eNvMxdizc
$82/A89D 29 01 AND #$01 A:0758 X:074D Y:01D0 P:eNvMxdizc ; Mask
$82/A89F F0 03 BEQ $03 [$A8A4] A:0700 X:074D Y:01D0 P:envMxdiZc
...
$82/A8A4 C2 20 REP #$20 A:0700 X:074D Y:01D0 P:envMxdiZc
$82/A8A6 AD 96 01 LDA $0196 [$00:0196] A:0700 X:074D Y:01D0 P:envmxdiZc ; Load weather
; 0x0 means sunny.
; 0x2 means rainy.
$82/A8A9 29 02 00 AND #$0002 A:0002 X:074D Y:01D0 P:envmxdizc
; If not rainy, skip ahead--
$82/A8AC F0 03 BEQ $03 [$A8B1] A:0002 X:074D Y:01D0 P:envmxdizc
; If it is rainy, goto IncrementTileValue to mark the tile as watered.
$82/A8AE 4C 06 A9 JMP $A906 [$82:A906] A:0002 X:074D Y:01D0 P:envmxdizc
...
IncrementTileValue:
; This is a common path for all kinds of tile incrementing, it's not just for rain.
$82/A906 E2 20 SEP #$20 A:0002 X:074D Y:01D0 P:envmxdizc
; Load early-out cond
$82/A908 AF 19 1F 7F LDA $7F1F19[$7F:1F19] A:0002 X:074D Y:01D0 P:envMxdizc
$82/A90C C9 03 CMP #$03 A:0000 X:074D Y:01D0 P:envMxdiZc ;
$82/A90E F0 59 BEQ $59 [$A969] A:0000 X:074D Y:01D0 P:eNvMxdizc ;
; Load tile value
$82/A910 BF E6 A4 7E LDA $7EA4E6,x[$7E:AC33] A:0000 X:074D Y:01D0 P:eNvMxdizc
; Apply 'watered' status
$82/A914 1A INC A A:0058 X:074D Y:01D0 P:envMxdizc
WriteRainEffect:
; X=0x74C means just to the right of shipping bin.
; Value of 0x59 means 'watered'.
$82/A915 9F E6 A4 7E STA $7EA4E6,x[$7E:AC32] A:0059 X:074C Y:01D0 P:envMxdizc
; Goto DoneProcessingTile.
$82/A919 80 4E BRA $4E [$A969] A:0059 X:074C Y:01D0 P:envMxdizc
...
OnUntilledSoil:
$82/A91B E2 20 SEP #$20 A:0701 X:074F Y:01D0 P:eNvMxdizc
$82/A91D AF 19 1F 7F LDA $7F1F19[$7F:1F19] A:0701 X:074F Y:01D0 P:eNvMxdizc
$82/A921 C9 02 CMP #$02 A:0700 X:074F Y:01D0 P:envMxdiZc
$82/A923 F0 44 BEQ $44 [$A969] A:0700 X:074F Y:01D0 P:eNvMxdizc
$82/A925 C9 03 CMP #$03 A:0700 X:074F Y:01D0 P:eNvMxdizc
$82/A927 F0 40 BEQ $40 [$A969] A:0700 X:074F Y:01D0 P:eNvMxdizc
$82/A929 AF 1B 1F 7F LDA $7F1F1B[$7F:1F1B] A:0700 X:074F Y:01D0 P:eNvMxdizc
$82/A92D 29 03 AND #$03 A:0706 X:074F Y:01D0 P:envMxdizc
$82/A92F D0 38 BNE $38 [$A969] A:0702 X:074F Y:01D0 P:envMxdizc ; Goto DoneProcessingTile
...
DoneProcessingTile:
$82/A969 C2 30 REP #$30 A:0059 X:074C Y:01D0 P:envMxdizc
$82/A96B FA PLX A:0059 X:074C Y:01D0 P:envmxdizc
$82/A96C 7A PLY A:0059 X:00C0 Y:01D0 P:envmxdizc
$82/A96D 8A TXA A:0059 X:00C0 Y:01D0 P:envmxdizc
$82/A96E 18 CLC A:00C0 X:00C0 Y:01D0 P:envmxdizc
$82/A96F 69 10 00 ADC #$0010 A:00C0 X:00C0 Y:01D0 P:envmxdizc
$82/A972 AA TAX A:00D0 X:00C0 Y:01D0 P:envmxdizc
$82/A973 E0 00 04 CPX #$0400 A:00D0 X:00D0 Y:01D0 P:envmxdizc
$82/A976 F0 03 BEQ $03 [$A97B] A:00D0 X:00D0 Y:01D0 P:eNvmxdizc
$82/A978 4C 22 A8 JMP $A822 [$82:A822] A:00D0 X:00D0 Y:01D0 P:eNvmxdizc
DoneProcessingField:
$82/A97B 98 TYA A:0400 X:0400 Y:01D0 P:envmxdiZC
$82/A97C 18 CLC A:01D0 X:0400 Y:01D0 P:envmxdizC
$82/A97D 69 10 00 ADC #$0010 A:01D0 X:0400 Y:01D0 P:envmxdizc
$82/A980 A8 TAY A:01E0 X:0400 Y:01D0 P:envmxdizc
$82/A981 C0 00 04 CPY #$0400 A:01E0 X:0400 Y:01E0 P:envmxdizc
$82/A984 F0 03 BEQ $03 [$A989] A:01E0 X:0400 Y:01E0 P:eNvmxdizc
$82/A986 4C 1F A8 JMP $A81F [$82:A81F] A:01E0 X:0400 Y:01E0 P:eNvmxdizc
...
This provides enough information to understand how the 'watered' status gets applied. So to apply watered status irrespective of rain, you can just change the branch below
$82/A8A9 29 02 00 AND #$0002 A:0002 X:074D Y:01D0 P:envmxdizc
; If not rainy, skip ahead--
$82/A8AC F0 03 BEQ $03 [$A8B1] A:0002 X:074D Y:01D0 P:envmxdizc
; If it is rainy, goto IncrementTileValue to mark the tile as watered.
$82/A8AE 4C 06 A9 JMP $A906 [$82:A906] A:0002 X:074D Y:01D0 P:envmxdizc
to no ops. In other words, change
82A8AC: F0
82A8AD: 03
to
82A8AC: EA
82A8AD: EA
Expressing this as a PAR code, it looks like
82A8ACEA
82A8ADEA
See a demo of the change
It's a bit more fun this way. Enjoy