Jump to content
IGNORED

Programmer Help Needed


Recommended Posts

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.

Link to comment
Share on other sites

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 by cdoty
Link to comment
Share on other sites

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:

 

1249288247_FroggerDisassemblyDiff.thumb.png.2fb0572ceca20c341a11057def3ffcd9.png

Frogger.zip

Link to comment
Share on other sites

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.........

 

  • Thanks 1
Link to comment
Share on other sites

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.

  • Like 1
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

 

 

 

  • Thanks 1
Link to comment
Share on other sites

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 by ChildOfCv
  • Like 1
Link to comment
Share on other sites

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

 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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 by ChildOfCv
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

  • Like 1
Link to comment
Share on other sites

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.

 

 

Link to comment
Share on other sites

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.

 

image.thumb.png.e8a07219c972fddebbaa1f1bd80511c1.png

 

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?

 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...