Site Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
articles:iicplus_merlin [2017/12/14 04:26]
M.G. [Disassembling the ROM, redux]
articles:iicplus_merlin [2017/12/14 19:07] (current)
M.G. [A Wild Bug Appears]
Line 5: Line 5:
 ===== A Wild Bug Appears ===== ===== A Wild Bug Appears =====
  
-Recently, ​a few members of the "Apple II Enthusiasts"​ Facebook group discovered ​a bug, seemingly in ROM 5X for the Apple IIc Plus:  The Merlin assembler, specifically the 128K versions 2.58 and 2.59, would crash at the main menu when ROM 5X was installed, but worked fine with the stock Apple ROM.+Recently, "Apple II Enthusiasts"​ Facebook group members Skander Ben Abdelkrim, Eric Leung, and Wyatt Wong reported ​a bug, seemingly in ROM 5X for the Apple IIc Plus:  The Merlin assembler, specifically the 128K versions 2.58 and 2.59, would crash at the main menu when ROM 5X was installed, but worked fine with the stock Apple ROM.
  
 This had me a bit perplexed, as I ran through possibilities in my head.  After booting a copy of [[http://​mirrors.apple2.org.za/​ftp.apple.asimov.net/​images/​programming/​assembler/​merlin/​Merlin-8%20v2.58%20%28ProDOS%29%20Disk%201-2.dsk|Merlin 2.58]] in an emulator and looking at its normal behavior, I had a place to start - the crash was happening immediately after the prompt was displayed at the main menu. This had me a bit perplexed, as I ran through possibilities in my head.  After booting a copy of [[http://​mirrors.apple2.org.za/​ftp.apple.asimov.net/​images/​programming/​assembler/​merlin/​Merlin-8%20v2.58%20%28ProDOS%29%20Disk%201-2.dsk|Merlin 2.58]] in an emulator and looking at its normal behavior, I had a place to start - the crash was happening immediately after the prompt was displayed at the main menu.
Line 41: Line 41:
 The first two instructions starting at BELL1 produce a short delay before the bell is sounded, so that multiple bells are distinct. ​ The remaining code starting at ''​ldy #​$C0''​ makes the actual beep sound. The first two instructions starting at BELL1 produce a short delay before the bell is sounded, so that multiple bells are distinct. ​ The remaining code starting at ''​ldy #​$C0''​ makes the actual beep sound.
  
-The ''​sta $c028''​ switches to the aux firmware bank to the ROM 5X dispatcher, which does one of a few thing depending on the contents of the A register. ​ Two of them, if the A register contains $A9 or $EA are to dispatch the reset or boot hooks that provide for the main ROM 5X features, it would be obvious if we were accidentally hitting these. ​ One is to sound the classic "air raid beep" if the A register contains $40, like it would if someone were calling BELL1 to sound the speaker. ​ Finally, if the A register is not one of those three values, the dispatcher calls the WAIT routine and exits back to the main firmware at the ''​ldy #​$C0''​ and falls through to the original BELL sounder. ​ We'd know if we were hitting that if we heard the "IIc Plus beep"​. ​ But we actually ​don't hear the beep when Merlin crashes. ​ We crash into the monitor and that is what causes the beep.+The ''​sta $c028''​ switches to the aux firmware bank to the ROM 5X dispatcher, which does one of a few things ​depending on the contents of the A register. ​ Two of them, if the A register contains $A9 or $EAare to dispatch the reset or boot hooks that provide for the main ROM 5X features.  It would be obvious if we were accidentally hitting these. ​ One is to sound the classic "air raid beep" if the A register contains $40, like it would if someone were calling BELL1 to sound the speaker. ​ Finally, if the A register is not one of those three values, the dispatcher calls the WAIT routine and exits back to the main firmware at the ''​ldy #​$C0''​ and falls through to the original BELL sounder. ​ We'd know if we were hitting that if we heard the "IIc Plus beep"​. ​ But we don'​t ​actually ​hear the beep that Merlin ​is attempting to make near where it crashes. ​ We crash into the monitor and that is what causes the beep we actually hear.
  
 I hadn't really tested this fall-through much and Occam'​s Razor suggested that Merlin was using its own delay value and calling BELL1 at the ''​jsr WAIT''/''​sta $C028''​ instruction,​ and that I was doing something like trashing the stack or returning an unexpected value. I hadn't really tested this fall-through much and Occam'​s Razor suggested that Merlin was using its own delay value and calling BELL1 at the ''​jsr WAIT''/''​sta $C028''​ instruction,​ and that I was doing something like trashing the stack or returning an unexpected value.
Line 65: Line 65:
 WAIT:   nop WAIT:   nop
         jmp     ​$C2EE ​   ; what's this?         jmp     ​$C2EE ​   ; what's this?
-LFCAC: ​ sta     $C028+LFCAC: ​ sta     ​$C028 ​   ; bank switch
         nop         nop
         nop         nop
Line 115: Line 115:
 This code saves the language card state and switches to the ROM.  But we just found this code in the ROM.  What gives? This code saves the language card state and switches to the ROM.  But we just found this code in the ROM.  What gives?
  
-Initially I had assumed the IIc Plus had some weird thing happening that would put it at odds with the other Apple IIs with regard to the memory select soft switches. ​ But then I had another idea.+Initially I had assumed the IIc Plus had some weird thing happening that would put it at odds with the other Apple IIs with regard to the memory select soft switches. 
 + 
 +Butthen I had another idea. 
 + 
 +===== Or is it? =====
  
 What would happen if this code wasn't in the ROM.  Say, perhaps, executing out of the language card RAM? What would happen if this code wasn't in the ROM.  Say, perhaps, executing out of the language card RAM?
Line 129: Line 133:
 ===== Crash! ===== ===== Crash! =====
  
-What happened? ​ We had already toggled the firmware bank once.  So the aux ROM was already switched in when the $CFE5 code enabled the ROM.  The next instruction was not what it was supposed to be.+What happened?  ​ 
 + 
 +Well... ... ... We had already toggled the firmware bank once.  So the aux ROM was already switched in when the $CFE5 code enabled the ROM. 
 + 
 +The next instruction was not what it was supposed to be.
  
 Could that be our problem? Could that be our problem?
Line 158: Line 166:
  
 The fix is easy... either don't mess with the BELL routine, or make sure we get back to the right place if we are running out of RAM, and the latter is exactly what happens in the 12/10/2017 update of ROM 5X. The fix is easy... either don't mess with the BELL routine, or make sure we get back to the right place if we are running out of RAM, and the latter is exactly what happens in the 12/10/2017 update of ROM 5X.
 +
 +While there are other miscellaneous changes and code reorganizations around fixing this problem (they can be seen in the [[https://​github.com/​mgcaret/​rom4x/​commit/​d5969104cdca6fb3746e47080364c6deb2874f7b|commit]]),​ they are not centrally important to the problem we are fixing.
 +
 +The main parts of the fix are as follows: ​ (Important note: The ROM in $Cxxx is //always visible// no matter what, there'​s no way to switch it out for RAM.)
 +
 +Move the dispatcher bank switch out of the BELL1 routine to a few free bytes at $C7FC in the main bank and $C7FF of the aux bank:
 +
 +<​code>​
 +; Main bank:
 + .org  $c7fc
 + sta   $c028
 +; Aux bank:
 +        .org  $c7ff
 +        jmp   ​$fb3c ​    ; to 5X dispatch
 +</​code>​
 +
 +Now, to do the beep fix, we patch BELL1:
 +
 +<​code>​
 +        .org  $fbe6
 +        jmp   ​$c2fc ​       ; to 5X beep with merlin fix
 +</​code> ​  
 +
 +This replaces the ''​jsr WAIT''​ in the "​BELL1.2"​ portion to trigger the "air raid beep" sound via the ROM 5X dispatcher, using the preceding ''​lda #​$0c''​ as the function code.
 +
 +Now, we know that we have to jump through the language card hoops in order to run out of RAM, so that's what happens at $C2FC:
 +
 +<​code>​
 +        .org  $c2fc
 +        phx
 +        jmp   ​$c9a1 ​    ; next step
 +</​code>​
 +
 +There'​s not a ton of space, so we have just enough room to save the X register and jump to some more code:
 +
 +<​code>​
 +        .org  $c9a1
 +        jsr   ​$cfe5 ​  ; get memory config we need to fix
 +        jsr   ​$c7fc ​  ; call ROM 5X dispatch
 +        jmp   ​$c2f5 ​  ; fix memory, restore x, a=$00
 +</​code>​
 +
 +We make use of the IIc Plus's existing routines to save and restore the language card state, and in between we call the ROM 5X dispatch, which executes the classic "air raid beep" that the Apple II line is known for.  In fact, this can be used to call any of the ROM 5X functions, now or in the future, and preserve the RAM state, provided we don't care about the contents of the A register.
 +
  
 ===== Conclusion ===== ===== Conclusion =====
Line 165: Line 217:
 There are plenty of programs that copy the ROM into RAM in order to modify it or make use of the routines in a non-standard memory configuration. ​ That's what's going on here. There are plenty of programs that copy the ROM into RAM in order to modify it or make use of the routines in a non-standard memory configuration. ​ That's what's going on here.
  
-Apple probably hit this problem early on in testing, perhaps noting an odd crash in a popular assembler. +Apple's engineers ​probably hit this problem early on in testing ​the IIc Plus, perhaps noting an odd crash in a popular assembler.