Well, got a little curious my self when trying to help somebody in the right direction on NintendoAge with how to hack/change R.B.I. Baseball. Well, they got everything done basically but the title screen. I tried to do this part my self, just find a little routine and edit it a bit. But what I found wasn't as easy to explain:
I have no idea where to even start on that routine just to put the title screen up. And there's even more code after the JSR! Anyone else run into this stuff [Bad code, anything of that matter.] trying to change something in a game?
short srcAddress at $002B
short dstAddress at $002D
char numRowsLeft at $002F
# AF9F
srcAddress = b012Table[0]
dstAddress = $2067
for numRowsLeft in range(10, 0, -1):
# AFC1
for Y in range(0, $13):
write srcAddress[Y] to PPUDATA
# AFCD
srcAddress += $13
dstAddress += $20 # move to next row
# AFE7
$AFEB looks like it uses the strategy of putting arguments to a subroutine after the JSR. MLI calls in ProDOS use this strategy; so do switch statements of Super Mario Bros. (Refer to SMBDis by doppelganger for an explanation of JumpEngine in SMB1.)
As I understand it, the subroutine at $B44A works like this (again, pseudocode):
short srcAddress at $002B
def writeStrings():
# B44A
Pop return address into srcAddress
Read PPUSTATUS to reset the PPUADDR latch
do:
# B453
Copy 2-byte address to PPUADDR
# B463
Copy bytes to PPUDATA until $FE or $FF value
while terminator was $FE
# B474
Add the total number of bytes processed to srcAddress
# B47E
Push srcAddress back on the stack
jsr writeStrings
.addr $2104
.byt "Hello World", $FE
.addr $2124
.byt "This is line 2", $FE
.addr $2124
.byt "This is the last line", $FF
; control returns here
Doh: Tepples beat me, but I'll still post this, I suppose. Here's my thought process.
The goal is simply to hack the title screen? I don't see any bad code here. Sometimes you don't hack code. Sometimes you hack data.
This routine puts the address of the titlescreen's data into $2B and $2C. It then loops loading values from that address using indexed indirect mode.
Since I don't know anything about bank switching, I set a breakpoint at AFC3, and stepped through it looking for the values loaded to A. (Which of course are then stored to $2007 which writes a tile.)
I got these: 70 70 70 70 70 7D 7E 80 C4
I then ran a search for those hex values in my hex editor, and changed them to 01 02 03 04 05 7D 7E 80 C4, and the title screen displayed those tiles, which happened to display 12345 at the very top of the B.
This lead me to believe that data for this title screen is uncompressed, as $70 is repeated many times and changing it changes exactly 1 tile.
The start of the data in the rom is at $3024 in the actual rom file with header. Changing values here chooses which tiles are drawn to the titlescreen.
The title screen that is not blank is $12 tiles long. (Which is why cmp #$13 ends the loop) So each set of $12 tiles starting at the address affects 1 row, starting 8 tiles from the right. This saves at least some space since the blank tiles before each row (24 24 24 24 24 24 24 24) don't have to be stored. That gets you as far as the main logo.
I have no idea what the second thing does, and definitely don't want to find out since there's no context, but hopefully this will get you started.
1P PLAY, 2P PLAY and WATCH, plus the copyright info seem to be affected by another routine. But... you don't even need to FIND that routine. You didn't need to look at the code at all. I just searched for the tile numbers the display 1 PLAY (01 19 24 19 15 0A 22) And changed them to whatever. I could have done exactly that for the main logo as well. Because the data is uncompressed, you don't really need a debugger at all. Is that all you want, or is there another goal in mind?
Nope, I was just trying to help the guy find the data in the ROM and since I didn't want to get into any guess and check algorithms and data types, I just waited for it to write to the name table. This was what I stumbled upon. For the purpose it is used by, it seems just a bit overly uneededly complex compared to most other code. Thanks for explaining how it works, now I'll relay this to the guy on NA. And before setting PPU Read/Write breaks, I found about 3 different subroutines that cleared the screen and other weird stuff, heh.
I don't see why it needs so much zero page and complexity to it. Like said above, per line, if you put it into RLE compression, the end blocks take up less space in the ROM and you get a shorter program. It's just.....not my choice I guess. Thanks for reading it through for me, it was at 5AM when I posted that just trying to find a simple fix for the guy.
3gengames wrote:For the purpose it is used by, it seems just a bit overly uneededly complex compared to most other code.
The first half looks like the most straightforward way to write a rectangular chunk of nametable to the screen. The second half, which I called writeStrings, appears to have come from a library used by multiple games and/or multiple places within a game, and its complexity follows from its generality.
3gengames wrote:I don't see why it needs so much zero page
On Atari 2600, five bytes is "a lot of zero page" because zero page is shared with the stack, the game state, and even the I/O registers. On Apple II, five bytes is "a lot of zero page" because ROM BASIC eats up most of it. On NES, neither is the case. In fact, my standard calling convention on the NES reserves 16 bytes for a subroutine's local variables.
3gengames wrote:Eh, okay then. Not the way I would have done it, but if you think it's fine then so be it.
I don't think this is a matter of whether people think it's fine, it's just that this is what the game used. You could make a simpler routine if you wanted, but hacking the routine itself would be a lot more trouble than just hacking the data, don't you think?
I hacked RBI to contain all the baseball teams for someone at dee-nee.
Not sure what you are trying to do with the code but $AF9F is the code to draw the Baseball at the beginning of the game and $B44A is one of RBI's codes to write data to the PPU. RBI tends to have PPU data inside the code or uses a pointer at $3C with routines, like at $B44A, to write the data.
Yeah, guys, I am not hacking the routine. I used it to find the data in the ROM [Well, was going to use it for that.] I just thought this one was strange and bloated and would of thought it would have made a good topic. I guess not.