Ikrananka Posted June 27, 2021 Share Posted June 27, 2021 I've identified a Frogger cart that I own whose ROM is one byte different to all of the others that I have. I've examined the ROM to see if it's a difference in any of the graphics but that doesn't appear to be the case (the graphics appear to all be stored much earlier in the rom). So, I can only assume that the one byte difference is a tiny change in the code/data but it is well beyond my skill set to be able to disassemble the roms and figure out what the difference is and why. So, I'm looking for a kind volunteer who'd be willing to help. Thanks in advance. Quote Link to comment Share on other sites More sharing options...
cdoty Posted June 28, 2021 Share Posted June 28, 2021 (edited) I can look at the disassembly. A single byte would most likely be a different value loaded into a register. Changing an opcode would involve a larger change. The easiest way would be to load it into MAME with the debugger enabled, and disassemble memory at the location, at the location - 1, at the location - 2, and at the location - 3. On Mame, click on Debug->New Disassembly Window. Enter the address, in hexadecimal, into the top level box and press Enter. You will see the disassembly below. The bytes, to the right, will show you the values involved in the disassembly. If you find what looks like a valid disassembly, compare it to the other versions. Edited June 28, 2021 by cdoty Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 28, 2021 Share Posted June 28, 2021 I can also breakpoint on address access on my personal emulator, if you want to throw it at me. Quote Link to comment Share on other sites More sharing options...
ChildOfCv Posted June 28, 2021 Share Posted June 28, 2021 And I have IDA Pro... It's also possible that the single-byte difference fixes a wrong note. But I hadn't heard of making fixes for cartridge games before. Quote Link to comment Share on other sites More sharing options...
Ikrananka Posted June 28, 2021 Author Share Posted June 28, 2021 You guys are amazing - thanks for all your offers of help. Attached are the two binary rom files in question. "Frogger (1983)(Parker Brothers).bin" is the one that matches three of my carts and based on other analysis is the only one I thought existed. "Frogger US 2-piece 12K.bin" is my strange cart with the one byte difference. This occurs at byte 2BA3h with a value of 07h in the former and 47h in the latter. I played around with the debugger in blueMSX and I believe the following is where the disassembly is different. As cdoty suspected, looks like register bc is being loaded with a different value: Frogger.zip Quote Link to comment Share on other sites More sharing options...
Ikrananka Posted June 28, 2021 Author Share Posted June 28, 2021 5 hours ago, ChildOfCv said: But I hadn't heard of making fixes for cartridge games before. Understandably there weren't many carts that were later released with fixes, but there were a few for the ColecoVision. Regional variations aside, I guess the most well known is Donkey Kong where the original 24K version was superseded by a "bug fixed" 16K version. Other carts that were later release with changes are: Miner 2049er - changed RAM location for hardware compatibility Pitstop - fixed bug when in the pits Victory - where the later EU release includes the correct rom while the rom in the original US release was corrupted And now possibly Frogger......... 1 Quote Link to comment Share on other sites More sharing options...
ChildOfCv Posted June 28, 2021 Share Posted June 28, 2021 I knew about different sizes of DK. The main difference seems to be that the 24K version was written in Pascal while the 16K version was probably rewritten in assembler or maybe C. Whichever, it doesn't make use of the time-consuming Pascal API. I guess they decided that the rewrite would be cheaper than the 24K ROM set. 1 Quote Link to comment Share on other sites More sharing options...
Ikrananka Posted June 28, 2021 Author Share Posted June 28, 2021 19 minutes ago, ChildOfCv said: I knew about different sizes of DK. The main difference seems to be that the 24K version was written in Pascal while the 16K version was probably rewritten in assembler or maybe C. Whichever, it doesn't make use of the time-consuming Pascal API. I guess they decided that the rewrite would be cheaper than the 24K ROM set. Yeah, I guess there must have been a decent cost saving using only 2x8K PROMs as opposed to the previous 3x8K. Particularly with this being shipped with every (almost) CV sold. Must have been quite a motivation to achieve this with the CBS Electronics launch of the CV in Europe and Australia in the summer of '83. Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 28, 2021 Share Posted June 28, 2021 The address is data, not code, It's accessed from this block of code: AB3B 23 inc hl (6) AB3C 66 ld h,(hl) (7) Where hl was 0xABA3 during the load (and so, it reads either 47 or 07). It triggered when I jumped into a log on the last row, then oddly every jump after that... maybe my emu missed the first few... AB3D dd77 ld (ix+04h),a 04 AB40 dd74 ld (ix+05h),h 05 AB43 79 ld a,c AB44 cb3f srl a AB46 dd6e ld l,(ix+06h) 06 AB49 dd66 ld h,(ix+07h) The next block just stores it at ix+5, which is 0x737F. h is overwritten shortly after. This code uses that value. This is a general purpose function often called with other values, but when it reads 737F, ix is set to 737A again. But even 737F often has other data, only at the end of the jump does it contain our mysterious changing byte. AAD7 dd7e ld a,(ix+05h) * read value (47 or 07) 05 AADA 4f ld c,a * make copy AADB e630 and 30h * mask bits 4 and 5 - gives us 0 in either case, so we don't care about a any further AADD b0 or b AADE 07 rlca AADF 07 rlca AAE0 07 rlca AAE1 07 rlca AAE2 fd77 ld (iy+04h),a 04 AAE5 79 ld a,c * get original value (47 or 07) AAE6 cb27 sla a * shift up by four (gives us 70, so we don't care about the rest) AAE8 cb27 sla a AAEA cb27 sla a AAEC cb27 sla a c is overwritten before it's used again. I don't see any other accesses to that byte, and that code suggests that the upper two bits of that byte (values 80 and 40) are never used. As a test, I patched it to be C7. I played far enough to clear the first board and didn't notice any differences. The data gets stored to RAM again after that bit shuffling, and I didn't dig further to see what it was used for, since the bit that changes is always discarded. Why are they different? My suspicion is not intentional. It's a single bit, and single bit errors can happen. There's no checksum or other mechanism on the ROM to verify integrity. 1 Quote Link to comment Share on other sites More sharing options...
ChildOfCv Posted June 29, 2021 Share Posted June 29, 2021 (edited) It's part of the song data. It seems to be a list of frequencies for each note. There are two lists of 16 words, one at AB64 and the second immediately following at AB84. The code seems to augment the standard PLAY_SONGS function by unpacking its own values into the data areas used by PLAY_SONGS before making the call to the function on each frame. It does an odd save of the area followed by the processing I just mentioned, and finally the call to PLAY_SONGS and then it restores the data saved. I haven't fully comprehended what it's doing yet, but my guess is that the value fixes a wrong note. Edit: It's a SFX frequency value, and it seems to be for the "hop" sound. And since it's a single bit difference, I think I'd attribute this to a ROM defect. Edited June 29, 2021 by ChildOfCv 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 29, 2021 Share Posted June 29, 2021 Where do you see it being used without the masking I called out? Quote Link to comment Share on other sites More sharing options...
ChildOfCv Posted June 29, 2021 Share Posted June 29, 2021 Oh, I see what you're asking. The data itself is used, of course. But yeah, the high 2 bits are ignored for that case. Detailed explanation: Starting with a "hop": FROGGER:A6FB ld hl, hop_sfx_table FROGGER:A6FE jp play_sfx_no_override FROGGER:A9F8 play_sfx_no_override: ; CODE XREF: sub_1A6D7+27↑j FROGGER:A9F8 ld a, (channel2_flag) FROGGER:A9FB or a FROGGER:A9FC FROGGER:A9FC FROGGER:A9FC jr z, set_sfx_data ; hl = soundfx data FROGGER:AA12 set_sfx_data: ; CODE XREF: play_sfx_no_override+4↑j FROGGER:AA12 ld ix, channel2_song ; hl = soundfx data FROGGER:AA12 ; End of function play_sfx FROGGER:AA12 FROGGER:AA16 FROGGER:AA16 ; The channel table is a list of pointers for each FROGGER:AA16 ; channel. Each pointer has the data for only that FROGGER:AA16 ; channel. This function is either called once for FROGGER:AA16 ; channel 2 (the dedicated SFX channel) or 3 times for the FROGGER:AA16 ; background music (which typically silences the SFX FROGGER:AA16 ; channel). Each call will point to a location within the FROGGER:AA16 ; channel table. FROGGER:AA16 ; FROGGER:AA16 ; The channel data consists of a pointer to a frequency FROGGER:AA16 ; list, a pointer to a duration list, and finally the notes FROGGER:AA16 ; list. The final note is marked by a 0. Each note FROGGER:AA16 ; specifies both frequency and duration: Frequency in the FROGGER:AA16 ; lower nibble and duration in the upper. Both reference FROGGER:AA16 ; their associated tables. This means that both tables are FROGGER:AA16 ; 16 values maximum. Many tables are shorter. FROGGER:AA16 ; FROGGER:AA16 ; The destination points to a structure representing the FROGGER:AA16 ; song for that channel. FROGGER:AA16 FROGGER:AA16 ; =============== S U B R O U T I N E ======================================= FROGGER:AA16 FROGGER:AA16 ; Creates a song struct for one channel. FROGGER:AA16 ; FROGGER:AA16 ; hl = channel table pointer FROGGER:AA16 ; ix = dest ptr FROGGER:AA16 ; FROGGER:AA16 FROGGER:AA16 create_song_struct: ; CODE XREF: set_all_song_structs+4↑p FROGGER:AA16 ; set_all_song_structs+B↑p FROGGER:AA16 ld e, (hl) ; Dereference the table pointer FROGGER:AA17 inc hl FROGGER:AA18 ld d, (hl) FROGGER:AA19 inc hl FROGGER:AA1A FROGGER:AA1A FROGGER:AA1A ld a, d ; Check if NULL FROGGER:AA1B or e FROGGER:AA1C jr z, empty_song FROGGER:AA1E FROGGER:AA1E FROGGER:AA1E ld a, (de) ; Copy in the frequency table pointer FROGGER:AA1F ld (ix+song_t.freq_values_ptr), a FROGGER:AA22 inc de FROGGER:AA23 ld a, (de) FROGGER:AA24 ld (ix+ song_t.freq_values_ptr+1 ), a FROGGER:AA27 inc de FROGGER:AA28 FROGGER:AA28 FROGGER:AA28 ld a, (de) ; Copy in the duration table pointer FROGGER:AA29 ld (ix+song_t.duration_values_ptr), a FROGGER:AA2C inc de FROGGER:AA2D ld a, (de) FROGGER:AA2E ld (ix+ song_t.duration_values_ptr+1 ), a FROGGER:AA31 FROGGER:AA31 FROGGER:AA31 ld (ix+song_t.duration), 0 ; Initialize the note data FROGGER:AA35 ld (ix+song_t.attenuation), 0 FROGGER:AA39 inc de FROGGER:AA3A FROGGER:AA3A empty_song: ; CODE XREF: create_song_struct+6↑j FROGGER:AA3A ld (ix+song_t), e ; Set the note data pointer to the start of the note data FROGGER:AA3A ; (or NULL if there is no data) FROGGER:AA3D ld (ix+ song_t.note_list_ptr+1 ), d FROGGER:AA40 ret Now we go to the sound driver: FROGGER:AA41 song_driver: ; CODE XREF: sub_199F9-28E↑p FROGGER:AA41 ld a, (byte_7394) FROGGER:AA44 ld b, a FROGGER:AA45 ld a, (channel0_song.note_list_ptr) ; Does channel 0 have a song loaded? FROGGER:AA48 or b FROGGER:AA49 ld b, a FROGGER:AA4A ld a, (channel2_flag) ; Is there a SFX playing? FROGGER:AA4D or b FROGGER:AA4E jr nz, save_song_ptrs ; If any of the above, don't reinitialize the song FROGGER:AA50 FROGGER:AA50 FROGGER:AA50 ld a, (byte_7393) FROGGER:AA53 bit 2, a FROGGER:AA55 jr nz, save_song_ptrs FROGGER:AA57 FROGGER:AA57 FROGGER:AA57 ld hl, main_song_toc ; When whatever song is done playing, go back to the default FROGGER:AA5A call set_all_song_structs ; hl = structure source data FROGGER:AA5D FROGGER:AA5D save_song_ptrs: ; CODE XREF: song_driver+D↑j FROGGER:AA5D ; song_driver+14↑j FROGGER:AA5D ld hl, ptr_to_s_on_0 ; This is the OS7 list of song pointers FROGGER:AA60 ld de, saved_song_ptrs ; Save the current copy FROGGER:AA63 ld bc, 8 FROGGER:AA66 ldir FROGGER:AA68 FROGGER:AA68 FROGGER:AA68 ld ix, channel0_song ; Advance the song state for each channel FROGGER:AA6C call update_song_state FROGGER:AA6F FROGGER:AA6F FROGGER:AA6F ld ix, channel1_song FROGGER:AA73 call update_song_state FROGGER:AA76 FROGGER:AA76 FROGGER:AA76 ld ix, channel2_song FROGGER:AA7A call update_song_state FROGGER:AA7D FROGGER:AA7D FROGGER:AA7D ld a, (ix+song_t.note_list_ptr) ; Check if there is a channel 2 SFX pending FROGGER:AA80 or (ix+ song_t.note_list_ptr+1 ) FROGGER:AA83 jr nz, loc_1AA88 FROGGER:AA85 FROGGER:AA85 FROGGER:AA85 ld (channel2_flag), a ; If not, clear the flag FROGGER:AA88 FROGGER:AA88 loc_1AA88: ; CODE XREF: song_driver+42↑j FROGGER:AA88 ld ix, channel0_song ; Now overwrite the OS7 song data with the current state of our FROGGER:AA88 ; song driver FROGGER:AA8C ld iy, sxdata_channel_0 FROGGER:AA90 ld (ptr_to_s_on_1), iy FROGGER:AA94 call set_play_songs_data ; ix = Frogger song data ptr FROGGER:AA94 ; iy = OS7 song data ptr FROGGER:AA97 FROGGER:AA97 FROGGER:AA97 ld iy, sxdata_channel_1 FROGGER:AA9B ld ix, channel1_song FROGGER:AA9F ld (ptr_to_s_on_2), iy FROGGER:AAA3 call set_play_songs_data ; ix = Frogger song data ptr FROGGER:AAA3 ; iy = OS7 song data ptr FROGGER:AAA6 FROGGER:AAA6 FROGGER:AAA6 ld ix, channel2_song FROGGER:AAAA ld iy, sxdata_channel_2 FROGGER:AAAE ld (ptr_to_s_on_3), iy FROGGER:AAB2 call set_play_songs_data ; ix = Frogger song data ptr FROGGER:AAB2 ; iy = OS7 song data ptr FROGGER:AAB5 FROGGER:AAB5 FROGGER:AAB5 ld a, 0FFh ; This makes clever use of the note data pointer in the FROGGER:AAB5 ; channel 2 song struct. The intent is to make sure the FROGGER:AAB5 ; noise channel is not active. But "inc iy" only advances FROGGER:AAB5 ; us to byte 1 of channel 2. The calls above do not disturb FROGGER:AAB5 ; iy, so it is still as set before the last call. So this FROGGER:AAB5 ; makes use of channel 2's note ptr as a single byte FROGGER:AAB5 ; terminator for channel 0. FROGGER:AAB7 inc iy FROGGER:AAB9 ld (iy+sxdata_t.ch_songnum), a FROGGER:AABC ld (ptr_to_s_on_0), iy FROGGER:AAC0 FROGGER:AAC0 FROGGER:AAC0 call play_songs ; Let OS7 handle programming the sound chip FROGGER:AAC3 FROGGER:AAC3 FROGGER:AAC3 ld de, ptr_to_s_on_0 ; Restore the OS7 sound data pointers FROGGER:AAC6 ld hl, saved_song_ptrs FROGGER:AAC9 ld bc, 8 FROGGER:AACC ldir FROGGER:AACE ret FROGGER:AACE ; End of function song_driver So it makes 2 calls related to the song for each channel: update_song_state and set_play_songs_data. FROGGER:AB01 ; ix = song struct for the channel FROGGER:AB01 FROGGER:AB01 update_song_state: ; CODE XREF: song_driver+2B↑p FROGGER:AB01 ; song_driver+32↑p ... FROGGER:AB01 ld a, (ix+song_t.duration) ; See if the current note is still playing FROGGER:AB04 or a FROGGER:AB05 jr z, next_note FROGGER:AB07 FROGGER:AB07 FROGGER:AB07 dec (ix+song_t.duration) ; Tick a value off FROGGER:AB0A FROGGER:AB0A FROGGER:AB0A ld b, 1 ; Is it an even count? FROGGER:AB0C and b FROGGER:AB0D ret nz FROGGER:AB0E FROGGER:AB0E FROGGER:AB0E ld a, (ix+song_t.attenuation) ; If not, see if we should lower the volume. If it's already FROGGER:AB0E ; silent, then no FROGGER:AB11 cp 0Fh FROGGER:AB13 ret z FROGGER:AB14 FROGGER:AB14 FROGGER:AB14 inc (ix+song_t.attenuation) ; Okay, decay that note FROGGER:AB17 ret FROGGER:AB18 ; --------------------------------------------------------------------------- FROGGER:AB18 FROGGER:AB18 next_note: ; CODE XREF: update_song_state+4↑j FROGGER:AB18 ld l, (ix+song_t.note_list_ptr) ; Load up the pointer to the next note FROGGER:AB1B ld h, (ix+ song_t.note_list_ptr+1 ) FROGGER:AB1E ld a, h FROGGER:AB1F or l FROGGER:AB20 jr z, dead_channel FROGGER:AB22 FROGGER:AB22 FROGGER:AB22 ld a, (hl) ; Get the next note FROGGER:AB23 or a FROGGER:AB24 jr z, dead_channel FROGGER:AB26 FROGGER:AB26 FROGGER:AB26 inc hl ; Advance the pointer FROGGER:AB27 ld (ix+song_t.note_list_ptr), l FROGGER:AB2A ld (ix+ song_t.note_list_ptr+1 ), h FROGGER:AB2D FROGGER:AB2D FROGGER:AB2D ld c, a ; Use the lower half to index into the frequency list FROGGER:AB2E and 0Fh FROGGER:AB30 sla a FROGGER:AB32 ld l, (ix+song_t.freq_values_ptr) FROGGER:AB35 ld h, (ix+ song_t.freq_values_ptr+1 ) FROGGER:AB38 call index_to_hl ; a = index value FROGGER:AB38 ; hl = accumulator FROGGER:AB38 ; FROGGER:AB38 ; On return: FROGGER:AB38 ; FROGGER:AB38 ; hl = hl + index FROGGER:AB38 ; a = value at hl FROGGER:AB3B inc hl FROGGER:AB3C ld h, (hl) FROGGER:AB3D ld (ix+song_t.current_freq), a ; Store the frequency value FROGGER:AB40 ld (ix+ song_t.current_freq+1 ), h FROGGER:AB43 FROGGER:AB43 FROGGER:AB43 ld a, c ; Index the upper half into the durations list FROGGER:AB44 srl a FROGGER:AB46 ld l, (ix+song_t.duration_values_ptr) FROGGER:AB49 ld h, (ix+ song_t.duration_values_ptr+1 ) FROGGER:AB4C call index8_to_hl ; a = index value FROGGER:AB4C ; hl = accumulator FROGGER:AB4C ; FROGGER:AB4C ; On return: FROGGER:AB4C ; FROGGER:AB4C ; hl = hl + index/8 FROGGER:AB4C ; a = value at hl FROGGER:AB4F ld (ix+song_t.duration), a ; Store the duration and reset the attenuation FROGGER:AB52 ld (ix+song_t.attenuation), 0 FROGGER:AB56 ret FROGGER:AB57 ; --------------------------------------------------------------------------- FROGGER:AB57 FROGGER:AB57 dead_channel: ; CODE XREF: update_song_state+1F↑j FROGGER:AB57 ; update_song_state+23↑j FROGGER:AB57 ld (ix+ song_t.note_list_ptr+1 ), 0 FROGGER:AB5B ld (ix+song_t), 0 FROGGER:AB5F ld (ix+song_t.attenuation), 0Fh FROGGER:AB63 ret FROGGER:AB63 ; End of function update_song_state And the OS7 translator: FROGGER:AACF ; ix = Frogger song data ptr FROGGER:AACF ; iy = OS7 song data ptr FROGGER:AACF FROGGER:AACF set_play_songs_data: ; CODE XREF: song_driver+53↑p FROGGER:AACF ; song_driver+62↑p ... FROGGER:AACF ld a, (ix+song_t.attenuation) FROGGER:AAD2 cp 0Fh FROGGER:AAD4 jr z, silent FROGGER:AAD6 FROGGER:AAD6 FROGGER:AAD6 ld b, a FROGGER:AAD7 FROGGER:AAD7 FROGGER:AAD7 ld a, (ix+ song_t.current_freq+1 ) FROGGER:AADA FROGGER:AADA ; FROGGER:AADA ; Take only the 8th and 9th bits of frequency value and FROGGER:AADA ; combine with attenuation for byte 4 of song data FROGGER:AADA ; FROGGER:AADA ld c, a FROGGER:AADB and 30h FROGGER:AADD or b FROGGER:AADE rlca FROGGER:AADF rlca FROGGER:AAE0 rlca FROGGER:AAE1 rlca FROGGER:AAE2 ld (iy+sxdata_t.atten_freq), a FROGGER:AAE5 FROGGER:AAE5 ; FROGGER:AAE5 ; Take the lower nibble of the high byte and combine with FROGGER:AAE5 ; lower nibble of low byte. It seems to ignore the high FROGGER:AAE5 ; nibble of the low byte FROGGER:AAE5 ; FROGGER:AAE5 ld a, c FROGGER:AAE6 sla a FROGGER:AAE8 sla a FROGGER:AAEA sla a FROGGER:AAEC sla a FROGGER:AAEE ld d, a FROGGER:AAEF ld a, (ix+song_t.current_freq) FROGGER:AAF2 and 0Fh FROGGER:AAF4 or d FROGGER:AAF5 ld (iy+sxdata_t.freq_lsb), a FROGGER:AAF8 FROGGER:AAF8 FROGGER:AAF8 xor a FROGGER:AAF9 jr loc_1AAFD FROGGER:AAFB ; --------------------------------------------------------------------------- FROGGER:AAFB FROGGER:AAFB silent: ; CODE XREF: set_play_songs_data+5↑j FROGGER:AAFB ld a, 0FFh FROGGER:AAFD FROGGER:AAFD loc_1AAFD: ; CODE XREF: set_play_songs_data+2A↑j FROGGER:AAFD ld (iy+sxdata_t.ch_songnum), a FROGGER:AB00 ret FROGGER:AB00 ; End of function set_play_songs_data And the hop data itself: FROGGER:ACF8 hop_sfx_table: dw hop_sfx ; DATA XREF: sub_1A6D7+24↑o FROGGER:ACFA hop_sfx: dw hop_freq_list ; DATA XREF: FROGGER:hop_sfx_table↑o FROGGER:ACFC dw hop_duration_list FROGGER:ACFE db 20h ; The note list: 0,4,8,7 with frame-length transitions FROGGER:ACFF db 24h ; $ FROGGER:AD00 db 28h ; ( FROGGER:AD01 db 47h ; G FROGGER:AD02 db 0 Now the frequency list. This is stored in an odd way, but whatever. A frequency in the TI sound chip is defined by a 10-bit count. The first byte has the upper 6 bits, and the second byte has the lower 4. That means the upper 2 bits of the first byte and the upper 4 bits of the second byte are ignored by masking. So the frequency list below is effectively 0BE, 0B4, 0AA, 0A0, 097, 087, 078, 071. FROGGER:AB94 hop_freq_list: dw 0B0Eh, 0B04h, 0A0Ah, 0A00h, 907h, 807h, 708h, 701h ; Last value is 4701h in the corrupted ROM. Fortunately, the 4 is masked off FROGGER:AB94 ; DATA XREF: FROGGER:hop_sfx↓o FROGGER:ABA4 dw 50Fh FROGGER:AC14 hop_duration_list:db 0, 0, 1, 2, 3 Quote Link to comment Share on other sites More sharing options...
Tursi Posted June 29, 2021 Share Posted June 29, 2021 Frig, if I knew a disassembly was available I wouldn't have wasted my time Nice to see though! Quote Link to comment Share on other sites More sharing options...
Ikrananka Posted June 29, 2021 Author Share Posted June 29, 2021 First off, huge thanks to Tursi and ChildOfCv for your enormous help with this. I'm so very grateful for the time you've put into this. 5 hours ago, ChildOfCv said: Now the frequency list. This is stored in an odd way, but whatever. A frequency in the TI sound chip is defined by a 10-bit count. The first byte has the upper 6 bits, and the second byte has the lower 4. That means the upper 2 bits of the first byte and the upper 4 bits of the second byte are ignored by masking. So the frequency list below is effectively 0BE, 0B4, 0AA, 0A0, 097, 087, 078, 071. FROGGER:AB94 hop_freq_list: dw 0B0Eh, 0B04h, 0A0Ah, 0A00h, 907h, 807h, 708h, 701h ; Last value is 4701h in the corrupted ROM. Fortunately, the 4 is masked off FROGGER:AB94 ; DATA XREF: FROGGER:hop_sfx↓o FROGGER:ABA4 dw 50Fh So, should that mean if the byte in the rom at 2BA3 was say F7 then this shouldn't affect the hop sound as the "F" is still masked? The reason I ask is that if I do change it to F7, or even 97, then the last part of the frog hop sound does audibly change. Interestingly, changing some of the earlier frequencies in that list also affects the main theme tune that plays during a game. Quote Link to comment Share on other sites More sharing options...
ChildOfCv Posted June 29, 2021 Share Posted June 29, 2021 (edited) The top nibble of the low byte is ignored, but only the top 2 bits of the high byte are ignored. So 47 is the same as 07, 87, or C7 in the high byte. 0F in the low byte is the same as 1F, 2F, 3F, 4F, 5F, 6F, 7F, 8F, 9F, AF, BF, CF, DF, EF, or FF Edited June 29, 2021 by ChildOfCv Quote Link to comment Share on other sites More sharing options...
ChildOfCv Posted June 29, 2021 Share Posted June 29, 2021 43 minutes ago, Tursi said: Frig, if I knew a disassembly was available I wouldn't have wasted my time Nice to see though! It is as of last night, but only for the parts that deal with that data 1 Quote Link to comment Share on other sites More sharing options...
Ikrananka Posted June 30, 2021 Author Share Posted June 30, 2021 2 minutes ago, ChildOfCv said: The top two bits are ignored, but the next 2 are part of the frequency. So 0x, 4x, 8x, and Cx are the same. Ah - gotcha. And thank you for the extensive analysis of this. Was that disassembly all done by you using IDA Pro? So, in effect the two game ROMs in question are exactly identical in execution. I'm not so certain that the PROM is corrupted as it seems kind of convenient that the effect on the game is absolutely nothing. But can't see why anyone would make this change intentionally either. Quote Link to comment Share on other sites More sharing options...
ChildOfCv Posted June 30, 2021 Share Posted June 30, 2021 3 minutes ago, Ikrananka said: Ah - gotcha. And thank you for the extensive analysis of this. Was that disassembly all done by you using IDA Pro? Yeah. Tursi's debugger found the primary access to the data, but it's always possible that somebody that wasn't run under normal circumstances also accessed the data, so I decided to resolve all references. Once all the code is properly analyzed, IDA can tell you everybody who could possibly access a specific byte. In this case, it turns out that only the sound does, so Tursi's evaluation was essentially complete anyway. Well, as I noted, I didn't look too far outside of direct access either, but now that we know exactly what it's used for, it's quite unlikely that anybody else looks at it. 3 minutes ago, Ikrananka said: So, in effect the two game ROMs in question are exactly identical in execution. I'm not so certain that the PROM is corrupted as it seems kind of convenient that the effect on the game is absolutely nothing. But can't see why anyone would make this change intentionally either. ROMs can degrade. It's almost certain that when they were being created at the factory, they were all tested correct. But over time, it's possible that a bit rotted and is now high where it should have been low. On the good side, it happened in a place that nobody cares about. 1 Quote Link to comment Share on other sites More sharing options...
mumbai Posted June 30, 2021 Share Posted June 30, 2021 54 minutes ago, Ikrananka said: So, in effect the two game ROMs in question are exactly identical in execution. I'm not so certain that the PROM is corrupted as it seems kind of convenient that the effect on the game is absolutely nothing. But can't see why anyone would make this change intentionally either. Lacking production records from the era, one means by which to settle such questions is to have a sizable set of samples to compare. The closest analogy that comes to mind in the moment would be the AccurateRip database for CDs, in terms of the sort of firmer ground needed. A case such as this here with your four Frogger cartridges was one of the things hinted around in the (unanswered) questions I PM'd to you last Spring. Among other things, I am still curious as to how many cartridges were dumped per-title for the ROM project, which is unstated, and what guided decisions to dump more than one where that may have been the case. All that aside, the sleuthing in this thread has been quite interesting. Thanks all. Quote Link to comment Share on other sites More sharing options...
Ikrananka Posted June 30, 2021 Author Share Posted June 30, 2021 Did a bit more investigating of the dumps of my four Frogger carts and also the one from Pitou. Discovered some differences I'd missed before. First off, none of this affects the actual 12K game ROM which is identical in every case with the exception of the suspicious cart with 47h at 2BA3 which as ChildOfCv concluded has absolutely zero effect on the game/sounds. What is interesting is that there are quite a few variations in the unused ROM data between 12K and 16K. This tends to support that the burning of these ROMs and the cart manufacture were changed a number of times over their manufacturing lifetime. 20 hours ago, ChildOfCv said: ROMs can degrade. It's almost certain that when they were being created at the factory, they were all tested correct. But over time, it's possible that a bit rotted and is now high where it should have been low. On the good side, it happened in a place that nobody cares about. What is even more interesting is that in the suspicious cart the byte value at 3BA3 is identical to that at 2BA3, i.e. 47h. I suspect this cart contains two 8K ROMs with the second 8K ROM containing two copies of the last 4K of the game. Now, if bit rot had indeed caused the value to 2BA3 to go high to 47h (from the other carts 07h value), then what are the chances that exactly the same bit would also go high to exactly the same value exactly 4K further into the ROM? I would suggest that because 47h occurs in both locations then this was how it was originally programmed into the ROM and is not due to bit rot. BTW - I've dumped hundreds of CV carts over the years and never had a single verifiable case of bit rot. Now, I did have a Front Line cart that seemed to have died on me but years later I found that even that was actually working once I tried it on a few different consoles. I have also heard of others who have reported dead carts, but cases of single bit rot wouldn't necessarily cause a cartridge to appear dead, but might otherwise cause a game to misbehave in some way. Personally I have never heard of anyone reporting these kind of issues with a cartridge that didn't turn out to be either dirty connections or issues with the console. Now, I'm not dismissing bit rot but amongst all of the CV carts that are out there I would say that it must still be extremely rare. Thoughts? Quote Link to comment Share on other sites More sharing options...
mumbai Posted June 30, 2021 Share Posted June 30, 2021 5 minutes ago, Ikrananka said: BTW - I've dumped hundreds of CV carts over the years and never had a single verifiable case of bit rot. [...] I have also heard of others who have reported dead carts, but cases of single bit rot wouldn't necessarily cause a cartridge to appear dead, but might otherwise cause a game to misbehave in some way. Personally I have never heard of anyone reporting these kind of issues with a cartridge that didn't turn out to be either dirty connections or issues with the console. Now, I'm not dismissing bit rot but amongst all of the CV carts that are out there I would say that it must still be extremely rare. It is not clear what you intend in full by the phrase "these kinds of issues" above, or what level of incidence you consider extremely rare, but I've encountered scores of cartridges exhibiting operational faults unrelated to connection/console issues. Whether original manufacturing error or from the passage of time, it happens. Quote Link to comment Share on other sites More sharing options...
Ikrananka Posted June 30, 2021 Author Share Posted June 30, 2021 6 minutes ago, mumbai said: It is not clear what you intend in full by the phrase "these kinds of issues" above, or what level of incidence you consider extremely rare, but I've encountered scores of cartridges exhibiting operational faults unrelated to connection/console issues. Whether original manufacturing error or from the passage of time, it happens. Well I guess I've been much more fortunate than you. And I don't doubt that it happens. Quote Link to comment Share on other sites More sharing options...
ChildOfCv Posted June 30, 2021 Share Posted June 30, 2021 44 minutes ago, Ikrananka said: What is even more interesting is that in the suspicious cart the byte value at 3BA3 is identical to that at 2BA3, i.e. 47h. I suspect this cart contains two 8K ROMs with the second 8K ROM containing two copies of the last 4K of the game. Now, if bit rot had indeed caused the value to 2BA3 to go high to 47h (from the other carts 07h value), then what are the chances that exactly the same bit would also go high to exactly the same value exactly 4K further into the ROM? I would suggest that because 47h occurs in both locations then this was how it was originally programmed into the ROM and is not due to bit rot. If the chips have a standard part number (and if you actually want to open the cartridge to find out), it would be interesting to see if that top level chip is really a 4K chip. If so, you'd see the exact copy of the lower 4K of that chip simply because it has 4K worth of address lines but it still sits on the 8K select line. Quote Link to comment Share on other sites More sharing options...
Ikrananka Posted June 30, 2021 Author Share Posted June 30, 2021 21 minutes ago, ChildOfCv said: If the chips have a standard part number (and if you actually want to open the cartridge to find out), it would be interesting to see if that top level chip is really a 4K chip. If so, you'd see the exact copy of the lower 4K of that chip simply because it has 4K worth of address lines but it still sits on the 8K select line. Ah I see - interesting. I'll guess this will have to wait as I'm not keen on destroying my cart to answer this. Apparently, Parker Bros. carts are glued together in addition to the screw. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.