<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>commodore &#8211; Nate&#039;s Blog</title>
	<atom:link href="https://oldblog.natebarney.com/tag/commodore/feed/" rel="self" type="application/rss+xml" />
	<link>https://oldblog.natebarney.com</link>
	<description>Abandon all hope, ye who enter here</description>
	<lastBuildDate>Mon, 21 Aug 2023 15:47:11 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Moon Blaster</title>
		<link>https://oldblog.natebarney.com/2023/08/19/moon-blaster/</link>
					<comments>https://oldblog.natebarney.com/2023/08/19/moon-blaster/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Sat, 19 Aug 2023 04:28:24 +0000</pubDate>
				<category><![CDATA[Retro Computers]]></category>
		<category><![CDATA[6502]]></category>
		<category><![CDATA[assembly]]></category>
		<category><![CDATA[c128]]></category>
		<category><![CDATA[c64]]></category>
		<category><![CDATA[commodore]]></category>
		<category><![CDATA[computers]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[programming]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=580</guid>

					<description><![CDATA[When I was about 11 or 12 years old, I wrote a simple game in Commodore 128 BASIC. It&#8217;s not very fancy, but it is a complete game, with sound, graphics, a title screen, and even a backstory. The object of the game is to fire missiles at a moon moving across the top of [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>When I was about 11 or 12 years old, I wrote a simple game in <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_128" data-type="link" data-id="https://en.wikipedia.org/wiki/Commodore_128" target="_blank">Commodore 128</a> <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/BASIC" data-type="link" data-id="https://en.wikipedia.org/wiki/BASIC" target="_blank">BASIC</a>. It&#8217;s not very fancy, but it <em>is</em> a complete game, with sound, graphics, a title screen, and even a backstory. The object of the game is to fire missiles at a moon moving across the top of the screen. The player can move the missile launcher to one of three bases using the joystick, and (of course) launch missiles with the fire button. By any standard, it&#8217;s not a great game, but I was pretty proud of it at the time.</p>



<p>The C128 included <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_BASIC" data-type="link" data-id="https://en.wikipedia.org/wiki/Commodore_BASIC" target="_blank">Commodore BASIC</a> 7.0, which was much better than the older Commodore BASIC 2.0 that was on the <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_64" data-type="link" data-id="https://en.wikipedia.org/wiki/Commodore_64" target="_blank">C64</a>. BASIC 7.0 had lots of additional commands to control the graphics and sound chips in the machine, read the joysticks, save and load binary files between RAM and disk, etc., and the computer came with a really nice <a href="https://www.commodore.ca/manuals/128_system_guide/toc.htm" data-type="link" data-id="https://www.commodore.ca/manuals/128_system_guide/toc.htm" target="_blank" rel="noreferrer noopener">system guide</a> <a rel="noreferrer noopener" href="http://www.zimmers.net/anonftp/pub/cbm/manuals/c128/C128_System_Guide.pdf" data-type="link" data-id="http://www.zimmers.net/anonftp/pub/cbm/manuals/c128/C128_System_Guide.pdf" target="_blank">[PDF]</a> that described all of them. <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MOS_Technology_6502" data-type="link" data-id="https://en.wikipedia.org/wiki/MOS_Technology_6502" target="_blank">6502</a> <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Machine_code" data-type="link" data-id="https://en.wikipedia.org/wiki/Machine_code" target="_blank">machine language</a> was out of my reach at the time—I didn&#8217;t have an <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Assembly_language#Assembler" data-type="link" data-id="https://en.wikipedia.org/wiki/Assembly_language#Assembler" target="_blank">assembler</a>, or even any reference material. The C128 included a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Machine_code_monitor" data-type="link" data-id="https://en.wikipedia.org/wiki/Machine_code_monitor" target="_blank">machine language monitor</a> with a rudimentary assembler, but I had no idea what it was for. Nevertheless, I decided to try to make a game using the enhanced C128 BASIC, and Moon Blaster was the result.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-Screenshot.png" alt="" class="wp-image-585" style="width:640px" width="640" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-Screenshot.png 1024w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-Screenshot-300x257.png 300w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-Screenshot-768x659.png 768w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Moon Blaster screenshot</figcaption></figure>



<h5 class="wp-block-heading">Machine Language Port</h5>



<p>My <a rel="noreferrer noopener" href="https://blog.natebarney.com/2023/08/05/quest-for-tires/" data-type="link" data-id="https://blog.natebarney.com/2023/08/05/quest-for-tires/" target="_blank">recent foray</a> into C64 game reverse-engineering gave me an idea: re-write Moon Blaster in assembly language for the C64. I&#8217;ve never written a game in machine language for the C64 before, and this game is pretty simple—great for a first project. I had a couple main goals: 1) stay as true as possible to the original game, and 2) build both disk and cartridge versions. I&#8217;m pleased to report that I was successful. Here&#8217;s a short video of the new version of the game <strong><em>(headphone warning, it might be a bit loud)</em></strong>:</p>



<figure class="wp-block-video aligncenter"><video controls="" style="display:block; margin-left: auto; margin-right: auto;" width="320" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-ML-Gameplay.mp4"></video><figcaption style="text-align: center;" class="wp-element-caption">Moon Blaster ML gameplay</figcaption></figure>



<p>If you&#8217;d like to play the game, you can <a href="https://www.natebarney.com/files/moon-blaster/moon-blaster-ml.zip" data-type="link" data-id="https://www.natebarney.com/files/moon-blaster/moon-blaster-ml.zip">download it here</a>. The download contains cartridge and disk images for the new version, as well as the assembly source code for it. It also includes a disk image of the original BASIC game, if you&#8217;d like to compare the two. You&#8217;ll need a C64/C128 emulator (<a rel="noreferrer noopener" href="https://vice-emu.sourceforge.io/" data-type="link" data-id="https://vice-emu.sourceforge.io/" target="_blank">VICE</a> is a good one), or a real C64/C128. (If you&#8217;re using real hardware, I&#8217;ll assume you know how to get the images to it. If not, let me know.) The easiest way to start the game in VICE is to attach the cartridge image, either by using the menu or by pressing <kbd>Alt+C</kbd>. You&#8217;ll need to set up the joystick as well. VICE works with a game controller, or can emulate a joystick using the keyboard. It&#8217;s pretty straightforward to set up, but if you have trouble, let me know.</p>



<h5 class="wp-block-heading">It&#8217;s Not a Bug</h5>



<p>An amusing story (at least to me) about Moon Blaster is the way I inadvertently introduced the concept of a <a href="https://en.wikipedia.org/wiki/Critical_hit" data-type="link" data-id="https://en.wikipedia.org/wiki/Critical_hit">critical hit</a>. The moving objects on the screen—the moon, missile launcher, and missiles—were implemented as <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" data-type="link" data-id="https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" target="_blank">sprites</a>. C128 BASIC included commands for sprite collision detection, which I used to detect when the player got a hit. When two sprites touch, the program jumps to a specified line, which in this case plays the hit sound, updates the score, etc.</p>



<p>Unfortunately, BASIC is so slow that, in the original game, if the player hit the moon close to dead center, the missile collided <em>twice</em> before it was moved back to the launcher. This had the effect of doubling the hit sound and animation, and scoring twice instead of once. I recall trying to fix it, and being stumped. Ultimately, in the grand tradition of <a href="https://web.archive.org/web/20230305155314/http://catb.org/jargon/html/F/feature.html" data-type="link" data-id="https://web.archive.org/web/20230305155314/http://catb.org/jargon/html/F/feature.html" target="_blank" rel="noreferrer noopener">&#8220;It&#8217;s not a bug; it&#8217;s a feature,&#8221;</a> I decided just to add it to the instructions as an &#8220;intentional&#8221; bonus. However, the machine language version runs <em>much</em> faster, so this didn&#8217;t happen, and to stay true to the original, I actually had to write code specifically to replicate this <s>bug</s> feature.</p>



<h3 class="wp-block-heading">Technical Stuff</h3>



<p>The rest of this post contains technical details intended to be of interest to other programmers. If you&#8217;re not a programmer, you&#8217;re welcome to stick around, but if you want to bail at this point, I won&#8217;t mind <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" />. I won&#8217;t go over all of the game code, but there were a few things I was pretty happy with, and I thought they were worth talking about. The assembler I used for this project is <a rel="noreferrer noopener" href="https://cc65.github.io/doc/ca65.html" data-type="link" data-id="https://cc65.github.io/doc/ca65.html" target="_blank">ca65</a>, which is part of the <a rel="noreferrer noopener" href="https://cc65.github.io/" data-type="link" data-id="https://cc65.github.io/" target="_blank">cc65</a> compiler suite. In case it&#8217;s helpful, here&#8217;s a link to a <a rel="noreferrer noopener" href="http://6502.org/users/obelisk/6502/instructions.html" data-type="link" data-id="http://6502.org/users/obelisk/6502/instructions.html" target="_blank">6502 instruction set reference</a>.</p>



<h5 class="wp-block-heading">Linear Feedback Shift Register</h5>



<p>The original game initially used BASIC drawing commands to draw the star field. This was painfully slow, so I ended up changing it to load the bitmap memory from a disk file, which I had saved after running the drawing routines. This was still pretty slow, but was a noticeable improvement. When doing the assembly port, I had the raw power of a 1 MHz processor at my command, so I decided the game should draw the stars at startup every time. Doing this in machine code is way faster than loading a screen image from disk. Plus, with the cartridge version, there wouldn&#8217;t even be a disk.</p>



<p>However, I wanted the stars to be the same every time, too, so I needed a <a href="https://en.wikipedia.org/wiki/Pseudorandom_number_generator" data-type="link" data-id="https://en.wikipedia.org/wiki/Pseudorandom_number_generator" target="_blank" rel="noreferrer noopener">pseudorandom number generator</a>. The <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MOS_Technology_6581" data-type="link" data-id="https://en.wikipedia.org/wiki/MOS_Technology_6581" target="_blank">SID</a> sound chip in the C64 can be <a rel="noreferrer noopener" href="https://youtu.be/-ADjfx79wNg" data-type="link" data-id="https://youtu.be/-ADjfx79wNg" target="_blank">used to generate random numbers</a>, but there&#8217;s not a good way to seed it. So using that approach, the stars would change every game. Instead, I implemented a 16-bit <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Linear-feedback_shift_register" data-type="link" data-id="https://en.wikipedia.org/wiki/Linear-feedback_shift_register" target="_blank">Linear Feedback Shift Register (LFSR)</a> routine, and sampled bits from it to get pseudorandom coordinates for drawing the stars. I used the constants from the Wikipedia article, since that seemed as good a set as any. I don&#8217;t claim it&#8217;s the most optimal LFSR ever written for the 6502, but I think it&#8217;s pretty slick:</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.EXPORTZP LFSR_STATE = $fd

.CODE

.PROC lfsr_16

    ; get XOR of bits 15 and 13
    lda LFSR_STATE+1
    lsr a
    lsr a
    eor LFSR_STATE+1

    ; get XOR of bit 12
    lsr a
    eor LFSR_STATE+1

    ; get XOR of bit 10
    lsr a
    lsr a
    eor LFSR_STATE+1

    ; put result of XORs into carry flag
    lsr a
    lsr a
    lsr a

    ; rotate carry flag into LFSR_STATE as the LSB
    rol LFSR_STATE
    rol LFSR_STATE+1

    rts
.ENDPROC ; lfsr_16</pre>



<h5 class="wp-block-heading">Raster Interrupt Split-Screen</h5>



<p>The game is mostly graphical, but there are text elements to display the score and number of shots fired. C128 BASIC has a command (<kbd><a rel="noreferrer noopener" href="https://www.commodore.ca/manuals/128_system_guide/sect-17a.htm#17.11" data-type="link" data-id="https://www.commodore.ca/manuals/128_system_guide/sect-17a.htm#17.11" target="_blank">CHAR</a></kbd>) to draw character on a bitmap screen. I could have taken the same approach with the machine language port, but that would involve banking in the character ROM and copying the bitmap data for each character. It would be slow (although I had plenty of cycles to do it since the game is so simple) and ugly code. Instead, I decided to go a different direction—changing between graphics and text mode in the middle of the frame.</p>



<p>The <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MOS_Technology_VIC-II" data-type="link" data-id="https://en.wikipedia.org/wiki/MOS_Technology_VIC-II" target="_blank">VIC-II</a> video chip in the C64 can be configured to cause an interrupt when it reaches a specified scan line—a so-called raster interrupt. To achieve a split screen effect, one can enable the raster interrupt at the top of the screen, and then in the interrupt handler, turn on graphics mode, then set the raster interrupt somewhere in the middle of the screen, and turn on text mode. There are a few other details to take care of, but this works surprisingly well. To print the score, I can simply place the characters in the right location in screen memory. Here&#8217;s the code I used to enable the interrupt, and the interrupt handler that implements the split-screen:</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.INCLUDE "vic2.inc"

IRQ_VECTOR = $0314
KERNAL_ISR = $ea31
CIA1_IRQ = $dc0d
CIA2_IRQ = $dd0d

.DATA

FRAME_SYNC: .res 1

.CODE

.PROC setup_irq
    sei

    ; disable CIA-1 interrupts
    lda #%01111111
    sta CIA1_IRQ

    ; clear high bit of raster counter
    and VIC2::CR1
    sta VIC2::CR1

    ; acknowledge pending interrupts
    lda CIA1_IRQ
    lda CIA2_IRQ

    ; set raster interrupt
    lda #0 ; interrupt on this raster line
    sta VIC2::RST

    ; clear frame sync variable
    sta FRAME_SYNC

    ; set interrupt vector
    lda #&lt;split_screen_isr
    sta IRQ_VECTOR
    lda #>split_screen_isr
    sta IRQ_VECTOR+1

    ; enable raster interrupts
    lda #%00000001
    sta VIC2::IMA

    cli
    rts
.ENDPROC ; setup_irq

.PROC teardown_irq
    sei

    ; disable raster interrupts
    lda #%00000000
    sta VIC2::IMA

    ; acknowledge pending interrupts
    asl VIC2::IRQ ; acknowledge VIC-II raster interrupt by clearing low bit

    ; enable CIA-1 interrupts
    lda #%11111111
    sta CIA1_IRQ

    ; restore interrupt vector
    lda #&lt;KERNAL_ISR
    sta IRQ_VECTOR
    lda #>KERNAL_ISR
    sta IRQ_VECTOR+1

    cli
    rts
.ENDPROC ; teardown_irq

.PROC split_screen_isr

    ; clear decimal flag
    cld

    ; look at current raster line to see if we should turn bitmap on or off
    lda VIC2::CR1
    ldx VIC2::RST
    cpx #100
    bcs bitmap_off

    ; turn bitmap mode on
bitmap_on:
    inc FRAME_SYNC
    ora #%00100000  ; bitmap on
    sta VIC2::CR1
    lda VIC2::PTR
    ora #%00001100  ; set bitmap memory to $2000-$3FFF
    and #%11111100
    sta VIC2::PTR
    lda #209        ; next interrupt raster line
    jmp return

    ; turn bitmap mode off
bitmap_off:
    and #%11011111  ; bitmap off
    sta VIC2::CR1
    lda VIC2::PTR
    ora #%00000100  ; set bitmap memory to $0000-$1FFF
    and #%11110100
    sta VIC2::PTR
    lda #0          ; next interrupt raster line

return:
    sta VIC2::RST
    asl VIC2::IRQ   ; acknowledge VIC-II raster interrupt by clearing low bit
    jmp KERNAL_ISR
.ENDPROC ; split_screen_isr
</pre>



<h5 class="wp-block-heading">Exit to BASIC</h5>



<p>C64 games rarely include an option to exit the game and return to BASIC. Even though 64K of RAM was pretty roomy for the time, most games needed a great deal of it, and so banked out the BASIC ROM and clobbered BASIC memory areas. It&#8217;s also possible this was used as a rudimentary form of copy protection. However, the original Moon Blaster included an option to quit; being a BASIC program itself, this was simple to do.</p>



<p>I wanted the machine language port to have this same capability. It was in the original game, and I&#8217;m not worried about copy protection. The game doesn&#8217;t take much memory (less than 5K of code and data, not counting the 8K bitmap and 1K text screen memory areas), and I was able to place everything so as not to interfere with BASIC. To exit to BASIC, the disk-based game can simply jump to the BASIC warm-start routine <kbd>$E38B</kbd>. However, there are two problems with this.</p>



<p>The first problem is that just jumping to BASIC warm-start doesn&#8217;t work at all if you&#8217;re running the cartridge version, because BASIC isn&#8217;t initialized in that case. To address this, I call a few BASIC ROM routines from the cartridge entry point to initialize BASIC to the point that it can be warm-started. Here&#8217;s the code I used to do that:</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="14-16, 21-23" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.INCLUDE "main.inc"
.SEGMENT "START"

; entry point which jumps away to BASIC, so it works right from a cartidge
.PROC start

    ; initialize the KERNAL
    jsr $fda3 ; initialize I/O devices
    jsr $fd50 ; test RAM and initialize memory pointers
    jsr $fd15 ; initialize KERNAL vectors
    jsr $e518 ; initialize the screen and keyboard
    cli ; enable interrupts

    ; initialize BASIC
    jsr $e453 ; initialize BASIC vectors
    jsr $e3bf ; initialize BASIC RAM

    ; run the game
    jsr main

    ; jump to BASIC
    ldx #$80 ; no error code
    jmp $e38b ; BASIC warm start

.ENDPROC ; start</pre>



<p>The second problem is that I wanted the user to be able to re-start the game simply by typing <kbd>RUN</kbd>. This kind of works with the disk version, but that runs the BASIC loader program and loads the whole game from disk again, even though it&#8217;s still in memory. With a cartridge, there would be no BASIC program to <kbd>RUN</kbd> at all. To deal with this, I included a simple BASIC program to restart the game in the constant data section. Before exiting, I replace whatever BASIC program is loaded (possibly none) with this program, which immediately starts the game again when run. To do this, not only the BASIC program area at <kbd>$0801</kbd> needs to be populated, but also several pointers that BASIC uses, starting at <kbd>$2B</kbd> in zero page. (The details about these locations can be found in a <a href="http://sta.c64.org/cbm64mem.html" target="_blank" rel="noreferrer noopener">C64 memory map</a>.) Here&#8217;s the code I used to do that:</p>



<p></p>



<pre class="EnlighterJSRAW" data-enlighter-language="asm" data-enlighter-theme="" data-enlighter-highlight="34-49,51-66" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">.RODATA

; BASIC program to re-start the game
;
; 10 SYS 32804
;
BASIC_LOADER_ADDR = $0801
BASIC_LOADER:
.byte $0d, $08, $0a, $00, $9e, $20, $33, $32, $38, $30, $34, $00, $00, $00
BASIC_LOADER_SIZE = * - BASIC_LOADER

BASIC_POINTERS_ADDR = $2b
BASIC_POINTERS:
.byte $01, $08, $0f, $08, $0f, $08, $0f, $08, $00, $80, $00, $00, $00, $80
BASIC_POINTERS_SIZE = * - BASIC_POINTERS

.SEGMENT "MAIN"

; entry point which does an RTS, so it works right from BASIC's SYS command
.PROC main

    ; initialize SID
    jsr init_sid

    ; initialize screen
    jsr init_screen

    ; show title screen
    jsr title_screen

    ; restore screen
    jsr restore_screen

    ; put the BASIC loader into BASIC program memory
    lda #&lt;BASIC_LOADER
    sta $fb
    lda #>BASIC_LOADER
    sta $fc
    lda #&lt;BASIC_LOADER_ADDR
    sta $fd
    lda #>BASIC_LOADER_ADDR
    sta $fe
    ldy #0
loop1:
    lda ($fb),y
    sta ($fd),y
    iny
    cpy #BASIC_LOADER_SIZE
    bne loop1

    ; update the BASIC pointers for the new BASIC program just copied
    lda #&lt;BASIC_POINTERS
    sta $fb
    lda #>BASIC_POINTERS
    sta $fc
    lda #&lt;BASIC_POINTERS_ADDR
    sta $fd
    lda #>BASIC_POINTERS_ADDR
    sta $fe
    ldy #0
loop2:
    lda ($fb),y
    sta ($fd),y
    iny
    cpy #BASIC_POINTERS_SIZE
    bne loop2

    rts

.ENDPROC ; main</pre>



<p>With those two problems solved, the user can cleanly exit the game and then jump right back in, with no load time, regardless of whether the game is in cartridge or disk form. It&#8217;s not earth-shattering or anything, but I think it&#8217;s pretty cool.</p>



<h3 class="wp-block-heading">Closing Thoughts</h3>



<p>I had a lot of fun doing this project, and learned quite a few things. It was neat to revisit something I wrote so many years ago, and to breathe new life into it. The game won&#8217;t win any awards, but that&#8217;s okay. If you download and the play the game, or look at the code, and have any questions about how I did this or that, feel free to ask. If you have any suggestions on how I could have done things better, I&#8217;d love to hear about that too.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2023/08/19/moon-blaster/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://blog.natebarney.com/wp-content/uploads/2023/08/Moon-Blaster-ML-Gameplay.mp4" length="491883" type="video/mp4" />

			</item>
		<item>
		<title>Quest for Tires</title>
		<link>https://oldblog.natebarney.com/2023/08/05/quest-for-tires/</link>
					<comments>https://oldblog.natebarney.com/2023/08/05/quest-for-tires/#respond</comments>
		
		<dc:creator><![CDATA[Nate Barney]]></dc:creator>
		<pubDate>Sat, 05 Aug 2023 08:32:35 +0000</pubDate>
				<category><![CDATA[Retro Computers]]></category>
		<category><![CDATA[6502]]></category>
		<category><![CDATA[assembly]]></category>
		<category><![CDATA[c64]]></category>
		<category><![CDATA[commodore]]></category>
		<category><![CDATA[computers]]></category>
		<category><![CDATA[games]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[reverse-engineering]]></category>
		<guid isPermaLink="false">https://blog.natebarney.com/?p=457</guid>

					<description><![CDATA[This is a story of nostalgia, retro-computing, gaming, reverse-engineering, and fixing a decades-old bug. It might be a bit long, and can get rather technical in places (a lot of places), but if you're into that sort of thing, I think you may find it enjoyable. [...]]]></description>
										<content:encoded><![CDATA[
<p>This is a story of nostalgia, retro-computing, gaming, reverse-engineering, and fixing a decades-old bug. It might be a bit long, and can get rather technical in places (a lot of places), but if you&#8217;re into that sort of thing, I think you may find it enjoyable.</p>



<h3 class="wp-block-heading">The Game</h3>



<p>When I was a kid, my family had a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_128" data-type="URL" data-id="https://en.wikipedia.org/wiki/Commodore_128" target="_blank">Commodore 128</a> (C128) computer. The C128 was the successor to the famous <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_64" data-type="URL" data-id="https://en.wikipedia.org/wiki/Commodore_64" target="_blank">Commodore 64</a> (C64), and it could be booted into a C64-compatible mode. I used this computer all the time, for playing games, writing papers for school, and, of course, programming. Probably most of my time on the computer was spent playing games. One of my favorites was a game called <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/B.C.%27s_Quest_for_Tires" data-type="URL" data-id="https://en.wikipedia.org/wiki/B.C.%27s_Quest_for_Tires" target="_blank">B.C.&#8217;s Quest for Tires</a>. It&#8217;s a pretty simple side-scrolling game in which the player moves steadily to the right, and has to jump and duck obstacles to proceed.</p>



<figure class="wp-block-image aligncenter size-full"><img fetchpriority="high" decoding="async" width="768" height="660" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Screenshot.png" alt="" class="wp-image-553" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Screenshot.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Screenshot-300x258.png 300w" sizes="(max-width: 768px) 100vw, 768px" /><figcaption class="wp-element-caption">Quest for Tires screenshot</figcaption></figure>



<p>I had, and still have, a copy (of dubious provenance) of the C64 version of the game on a floppy disk. A couple of years ago, I used a <a rel="noreferrer noopener" href="http://www.go4retro.com/products/zoomfloppy/" data-type="URL" data-id="http://www.go4retro.com/products/zoomfloppy/" target="_blank">ZoomFloppy</a> with my <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_1571" data-type="URL" data-id="https://en.wikipedia.org/wiki/Commodore_1571" target="_blank">Commodore 1571</a> disk drive to make images of many of my old Commodore disks, including the one containing Quest for Tires. I loaded the game up in the <a rel="noreferrer noopener" href="https://vice-emu.sourceforge.io/" data-type="URL" data-id="https://vice-emu.sourceforge.io/" target="_blank">VICE</a> emulator and started playing, but the nostalgia I felt was quickly and rudely interrupted when the game crashed.</p>



<figure style="display: block; margin-left: auto; margin-right: auto;" class="wp-block-video"><video style="display: block; margin-left: auto; margin-right: auto;" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-For-Tires-Crash.mp4" controls="controls" width="384" height="330"></video>
<figcaption style="text-align: center;" class="wp-element-caption">Quest for Tires Gameplay and Crash</figcaption>
</figure>



<p>One of the things the player can do in the game is to change the main character&#8217;s speed, with a minimum speed of 10 and a maximum of 80. Partway through the game, the minimum speed is increased to 40. (There are no units specified, but I assume it&#8217;s meant to be in miles per hour.) The faster you go, the more difficult the game is, but the more points you get along the way. Unfortunately, every time I got the speed up to 80, the game crashed. Everything else seemed to work fine, so I just kept my speed below 80, but it was annoying that the game crashed at all.</p>



<p>A few days ago, I was re-watching <a rel="noreferrer noopener" href="https://www.youtube.com/@8_Bit" data-type="URL" data-id="https://www.youtube.com/@8_Bit" target="_blank">8-Bit Show and Tell</a>&#8216;s YouTube video about <a rel="noreferrer noopener" href="https://youtu.be/r4Px_JIh7Dg" data-type="URL" data-id="https://youtu.be/r4Px_JIh7Dg" target="_blank">fixing an old bug in the C64 version of Pac-Man</a>, and I was inspired to see if I could resolve the mystery of the Quest for Tires crash, and possibly even fix it. (As an aside, if you have any interest in retro-computing in general, and Commodore computers in particular, you should definitely check out that channel. Right now. I&#8217;ll be here when you get back.)</p>



<h3 class="wp-block-heading">Finding the Bug</h3>



<p>To figure out what&#8217;s going on, let&#8217;s use VICE&#8217;s built-in <a href="https://vice-emu.sourceforge.io/vice_12.html" data-type="URL" data-id="https://vice-emu.sourceforge.io/vice_12.html" target="_blank" rel="noreferrer noopener">machine language monitor</a>, which is a <a href="https://en.wikipedia.org/wiki/Machine_code_monitor" data-type="URL" data-id="https://en.wikipedia.org/wiki/Machine_code_monitor" target="_blank" rel="noreferrer noopener">debugging tool</a> that can examine and modify code and data in the machine&#8217;s memory. Some reference material to keep handy will be:</p>



<ul class="wp-block-list">
<li><a rel="noreferrer noopener" href="https://sta.c64.org/" data-type="URL" data-id="https://sta.c64.org/" target="_blank">Joe Forster</a>&#8216;s <a href="https://sta.c64.org/cbm64mem.html" data-type="URL" data-id="https://sta.c64.org/cbm64mem.html" target="_blank" rel="noreferrer noopener">Commodore 64 memory map</a></li>



<li><a rel="noreferrer noopener" href="https://www.pagetable.com/" data-type="URL" data-id="https://www.pagetable.com/" target="_blank">Michael Steil</a>&#8216;s <a rel="noreferrer noopener" href="https://www.pagetable.com/c64ref/" data-type="URL" data-id="https://www.pagetable.com/c64ref/" target="_blank">Ultimate Commodore 64 Reference</a>, especially the <a rel="noreferrer noopener" href="https://www.pagetable.com/c64ref/kernal/" data-type="URL" data-id="https://www.pagetable.com/c64ref/kernal/" target="_blank">C64 KERNAL API</a> section</li>
</ul>



<p>The game on disk consists of two files: <kbd>"QUEST FOR TIRES"</kbd>, which is a short BASIC program that loads and executes the second file, <kbd>"QFT.8000-C010"</kbd>, which is the machine code for the game itself. Based on the filename, it seems pretty likely that the machine code gets loaded to the addresses <kbd>$8000</kbd> through <kbd>$C010</kbd>. (Note: the convention with <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/MOS_Technology_6502" data-type="URL" data-id="https://en.wikipedia.org/wiki/MOS_Technology_6502" target="_blank">6502</a>-based computers, like the C64, is to represent <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Hexadecimal" data-type="URL" data-id="https://en.wikipedia.org/wiki/Hexadecimal" target="_blank">hexadecimal</a> numbers like addresses with a leading <kbd>$</kbd>, and <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Binary_number" data-type="URL" data-id="https://en.wikipedia.org/wiki/Binary_number" target="_blank">binary</a> numbers with a leading <kbd>%</kbd>.)</p>



<p>Since the crash is clearly related to changing the player&#8217;s speed, a good place to start might be to look for the code that updates the speed. To change the character&#8217;s speed, the player presses and holds the joystick button and moves the joystick left (to slow down) or right (to speed up), so we should look for instructions that read and process the joystick state.</p>



<h5 class="wp-block-heading">Using the Monitor</h5>



<p>VICE&#8217;s machine language monitor, when active, pauses the computer and presents a prompt where the user can enter commands to examine or modify the state of the computer, or to resume normal execution. Here&#8217;s a summary of the monitor commands we&#8217;ll be using, with abbreviations where applicable:</p>



<ul class="wp-block-list">
<li><kbd>a</kbd> &#8211; <a href="https://en.wikipedia.org/wiki/Assembly_language#Assembler" data-type="URL" data-id="https://en.wikipedia.org/wiki/Assembly_language#Assembler" target="_blank" rel="noreferrer noopener">assemble</a> instructions into memory</li>



<li><kbd>backtrace</kbd> (<kbd>bt</kbd>) &#8211; display the chain of subroutine calls leading to the current instruction</li>



<li><kbd>bank</kbd> &#8211; control which devices and memory ranges are visible in the monitor</li>



<li><kbd>break</kbd> (<kbd>bk</kbd>) &#8211; set a <a href="https://en.wikipedia.org/wiki/Breakpoint" data-type="URL" data-id="https://en.wikipedia.org/wiki/Breakpoint" target="_blank" rel="noreferrer noopener">breakpoint</a> on a memory location</li>



<li><kbd>disass</kbd> (<kbd>d</kbd>) &#8211; <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Disassembler" data-type="URL" data-id="https://en.wikipedia.org/wiki/Disassembler" target="_blank">disassemble</a> the contents of a memory range</li>



<li><kbd>goto</kbd> (<kbd>g</kbd>) &#8211; resume execution, optionally jumping to a new location</li>



<li><kbd>hunt</kbd> (<kbd>h</kbd>) &#8211; search through a memory range for a byte sequence</li>



<li><kbd>load</kbd> (<kbd>l</kbd>) &#8211; load a file from disk into memory</li>



<li><kbd>mem</kbd> (<kbd>m</kbd>) &#8211; display raw contents of a memory range</li>



<li><kbd>next</kbd> (<kbd>n</kbd>) &#8211; execute a single instruction, stepping over subroutine calls</li>



<li><kbd>save</kbd> (<kbd>s</kbd>) &#8211; save a region of memory to a file on disk</li>



<li><kbd>step</kbd> (<kbd>z</kbd>) &#8211; execute a single instruction, stepping into subroutine calls</li>
</ul>



<h5 class="wp-block-heading">Reading the Joystick</h5>



<p>On the C64, the state of joystick port #1 can be read from memory address <kbd>$DC01</kbd>, and the state of joystick port #2 can be read from <kbd>$DC00</kbd>. The value read from the joystick port is encoded in the lowest five bits, and each bit is active low, so <kbd>0</kbd> if pressed and <kbd>1</kbd> if not pressed. The five bits from right to left are:</p>



<ul class="wp-block-list">
<li>Bit 0: joystick up</li>



<li>Bit 1: joystick down</li>



<li>Bit 2: joystick left</li>



<li>Bit 3: joystick right</li>



<li>Bit 4: fire button</li>
</ul>



<p>For example, if the joystick is idle, the binary value read from the joystick port would be <kbd>%00011111</kbd>, or <kbd>$1F</kbd> in hexadecimal. If the joystick is pointed down and to the left, and the fire button is being pressed, the binary value read from the joystick port would be <kbd>%00001001</kbd>, or <kbd>$09</kbd> in hexadecimal. (Actually, the top three bits will probably be different. They&#8217;re related to the state of the keyboard.)</p>



<p>The game is played with the joystick connected to port #2, so it must have some code somewhere to read from <kbd>$DC00</kbd>. Let&#8217;s see if we can find it.</p>



<h5 class="wp-block-heading">Bank Switching</h5>



<p>There&#8217;s one more thing we need to deal with before running our search—<a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Bank_switching" data-type="URL" data-id="https://en.wikipedia.org/wiki/Bank_switching" target="_blank">bank switching</a>. The C64&#8217;s <a href="https://en.wikipedia.org/wiki/MOS_Technology_6510" data-type="URL" data-id="https://en.wikipedia.org/wiki/MOS_Technology_6510" target="_blank" rel="noreferrer noopener">6510</a> CPU has a 16-bit address bus, which means it can address a maximum of 64k (65,536) different memory locations. The C64 has a full 64k of <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Random-access_memory" data-type="URL" data-id="https://en.wikipedia.org/wiki/Random-access_memory" target="_blank">RAM</a>, but it also has <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Read-only_memory" data-type="URL" data-id="https://en.wikipedia.org/wiki/Read-only_memory" target="_blank">ROM</a>s and <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Input/output" data-type="URL" data-id="https://en.wikipedia.org/wiki/Input/output" target="_blank">I/O</a> devices that need to fit into the same address space, which the C64 handles by bank switching. This strategy involves mapping different devices into and out of the address space so that all of the memory and devices can be accessed, though not all at the same time.</p>



<p>The C64, by default, has a <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Commodore_BASIC" data-type="URL" data-id="https://en.wikipedia.org/wiki/Commodore_BASIC" target="_blank">BASIC</a> ROM mapped into the address range <kbd>$A000</kbd>&#8211;<kbd>$BFFF</kbd>, masking the RAM at those locations, which in our case will contain the second half of the game code. If a program writes to those addresses, the data will be written to the RAM. As a result, with the default bank setup, we can load the game into memory, but if we try to read that code, we&#8217;ll get the contents of the BASIC ROM instead.</p>



<p>Memory location $01 controls what devices are banked in and out, and location $00 controls writes to $01. (By convention, addresses less than $0100 are written with just two hex digits instead of four.) We could write to those addresses with the monitor to bank out the BASIC ROM. However, we can use the monitor&#8217;s <kbd>bank</kbd> command to read from the RAM without changing the computer&#8217;s state, so let&#8217;s stick to that approach, at least for now.</p>



<h5 class="wp-block-heading">searching the code</h5>



<p>Let&#8217;s load the machine code file into memory and search for instructions that refer to <kbd>$DC00</kbd>. The 6510 processor in the C64, like the 6502 it&#8217;s based on, uses <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/Endianness" target="_blank">little-endian byte ordering</a>, so instead of searching for the byte sequence <kbd>$DC $00</kbd>, we&#8217;ll need to search for the sequence <kbd>$00 $DC</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$e5d4) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$e5d4) bank ram
(C:$e5d4) h 8000 c00f 00 dc
a089
ad71</pre>



<p>Great, we found two memory addresses which reference that address. Let&#8217;s disassemble and look at the code around the first one, <kbd>$A089</kbd>. To get some context about the code you&#8217;re looking at, a good tactic is to disassemble several bytes before and after the location of interest. So, let&#8217;s disassemble starting at <kbd>$A07E</kbd> and going through <kbd>$A0A5</kbd>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="6" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$e5d4) d a07e a0a5
.C:a07e  AD 0F 12    LDA $120F
.C:a081  D0 3C       BNE $A0BF
.C:a083  AD 5E 40    LDA $405E
.C:a086  D0 37       BNE $A0BF
.C:a088  AD 00 DC    LDA $DC00
.C:a08b  29 10       AND #$10
.C:a08d  F0 14       BEQ $A0A3
.C:a08f  A5 C5       LDA $C5
.C:a091  C9 04       CMP #$04
.C:a093  F0 0E       BEQ $A0A3
.C:a095  C9 06       CMP #$06
.C:a097  F0 07       BEQ $A0A0
.C:a099  C9 05       CMP #$05
.C:a09b  D0 3F       BNE $A0DC
.C:a09d  4C 06 BF    JMP $BF06
.C:a0a0  4C 30 BF    JMP $BF30
.C:a0a3  EE 0F 12    INC $120F</pre>



<p>We can see the instruction <kbd>LDA $DC00</kbd> at address <kbd>$A088</kbd>. This instruction reads the value at address <kbd>$DC00</kbd> (the joystick port) and loads it into the CPU&#8217;s accumulator register, also called the <kbd>A</kbd> register. The next two instructions, <kbd>AND #$10</kbd> and <kbd>BEQ $A0BF</kbd>, check if the fire button is being pressed. However, none of the other joystick bits are being checked here, so it&#8217;s unlikely that this is in-game code. More likely, it&#8217;s part of code that runs at the &#8220;game over&#8221; screen, waiting for the player to press the fire button to start the next game.</p>



<p>Perhaps we&#8217;ll have better luck with the other address where we found the joystick port being accessed, <kbd>$AD71</kbd>. Again, let&#8217;s expand out a bit to get context, and disassemble <kbd>$AD61</kbd> to <kbd>$AD80</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="10" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$a0a6) d ad61 ad80
.C:ad61  D0 AD       BNE $AD10
.C:ad63  70 40       BVS $ADA5
.C:ad65  69 00       ADC #$00
.C:ad67  A2 00       LDX #$00
.C:ad69  20 13 B3    JSR $B313
.C:ad6c  4C 1C AD    JMP $AD1C
.C:ad6f  60          RTS
.C:ad70  AD 00 DC    LDA $DC00
.C:ad73  8D 3D 40    STA $403D
.C:ad76  60          RTS
.C:ad77  AD 39 40    LDA $4039
.C:ad7a  C9 20       CMP #$20
.C:ad7c  D0 03       BNE $AD81
.C:ad7e  4C F6 B2    JMP $B2F6</pre>



<p>This is interesting. The <kbd>LDA $DC00</kbd> instruction reads the value of the joystick port and the next instruction, <kbd>STA $403D</kbd>, stores that value into a memory address. Presumably, this is so that it can be read multiple times by the code without it potentially changing between reads. Let&#8217;s see how many places in the game code read that location. Let&#8217;s look for intances of the instruction <kbd>LDA $403D</kbd>, which assembles to the three-byte sequence <kbd>$AD $3D $40</kbd>. (If we don&#8217;t find much, we can expand the search to other instructions that use that address.)</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$ad81) h 8000 c00f ad 3d 40
9fd0
9fd7
9fde
afee
b001
b03d
b044
b05b
b14d
b161</pre>



<p>That instruction is found in ten different places in the code. Not a huge number, but not a small number either. Notice, however, that they seem kind of clustered into groups. This would make sense if we expect that the joystick state might be read multiple times in a subroutine. Let&#8217;s start going through them. To look at the first group of three, let&#8217;s disassemble and examine <kbd>$9FC0</kbd> to <kbd>$9FF</kbd>D:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="9, 12, 15" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$ad81) d 9fc0 9ffd
.C:9fc0  18          CLC
.C:9fc1  69 0F       ADC #$0F
.C:9fc3  8D 30 40    STA $4030
.C:9fc6  CE 25 40    DEC $4025
.C:9fc9  10 20       BPL $9FEB
.C:9fcb  A9 0A       LDA #$0A
.C:9fcd  8D 25 40    STA $4025
.C:9fd0  AD 3D 40    LDA $403D
.C:9fd3  29 10       AND #$10
.C:9fd5  D0 14       BNE $9FEB
.C:9fd7  AD 3D 40    LDA $403D
.C:9fda  29 08       AND #$08
.C:9fdc  F0 10       BEQ $9FEE
.C:9fde  AD 3D 40    LDA $403D
.C:9fe1  29 04       AND #$04
.C:9fe3  D0 06       BNE $9FEB
.C:9fe5  CE 61 40    DEC $4061
.C:9fe8  EE 24 40    INC $4024
.C:9feb  4C 03 A0    JMP $A003
.C:9fee  EE 61 40    INC $4061
.C:9ff1  CE 24 40    DEC $4024
.C:9ff4  AD 24 40    LDA $4024
.C:9ff7  C9 04       CMP #$04
.C:9ff9  B0 08       BCS $A003
.C:9ffb  4C 00 A0    JMP $A000</pre>



<p>This is very promising. The joystick state (<kbd>$403D</kbd>) is read three times, each time to check a different bit. The three bits checked are bit 4 (<kbd>$10</kbd>, fire button), bit 3 (<kbd>$08</kbd>, joystick right), and bit 2 (<kbd>$04</kbd>, joystick left). These are exactly the relevant bits for controlling the speed. The first check, at <kbd>$9FD0</kbd>, looks to see if the fire button is pressed, and if not, jumps ahead to <kbd>$9FEB</kbd>, which immediately jumps to <kbd>$A003</kbd>. </p>



<p>The next two checks only occur if the fire button is pressed. The second check, at <kbd>$9FD7</kbd>, looks to see if the joystick is pointing right (to speed up), and if it is, jumps ahead to <kbd>$9FEE</kbd>, which increments location <kbd>$4061</kbd> and decrements location <kbd>$4024</kbd>. It then checks if the value stored at <kbd>$4024</kbd> is less than <kbd>4</kbd>. If it&#8217;s greater than or equal to 4, it jumps ahead to <kbd>$A003</kbd>, but if it&#8217;s less than <kbd>4</kbd>, it jumps to <kbd>$A000</kbd> instead.</p>



<p>If the second check doesn&#8217;t find the joystick pointing right, the code falls through to the third check, at <kbd>$9FDE</kbd>. This looks to see if the joystick is pointing left (to slow down), and if it&#8217;s not, jumps ahead to <kbd>$9FBE</kbd>, which as we&#8217;ve already seen, immediately jumps to <kbd>$A003</kbd>. If the joystick <em>is</em> pointing left, then <kbd>$4061</kbd> is decremented, and <kbd>$4024</kbd> is incremented, and then the code jumps to <kbd>$A003</kbd>.</p>



<p>This very much appears to be the subroutine that updates the speed based on player input. It seems like <kbd>$A003</kbd> is the location to jump to when we&#8217;re done updating the speed, and <kbd>$4061</kbd> and/or <kbd>$4024</kbd> are good candidate locations for where some representation of the speed is stored. <kbd>$4061</kbd> is incremented when the character speeds up and decremented when the character slows down, so that at least moves in the right direction. <kbd>$4024</kbd> goes in the opposite direction, but it <em>is</em> compared against some kind of limit (<kbd>4</kbd>), which fits with the fact that the in-game speed is limited. Maybe they&#8217;re both different representations of the speed. Let&#8217;s look at the disassembly for <kbd>$A000</kbd> to <kbd>$A020</kbd> and see if we can learn anything more.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffe) d a000 a020
.C:a000  EE 24 40    INC $4024
.C:a003  AD 24 40    LDA $4024
.C:a006  CD 30 40    CMP $4030
.C:a009  90 06       BCC $A011
.C:a00b  AD 30 40    LDA $4030
.C:a00e  8D 24 40    STA $4024
.C:a011  4A          LSR A
.C:a012  4A          LSR A
.C:a013  AC 1B 40    LDY $401B
.C:a016  AE 1A 40    LDX $401A
.C:a019  C0 07       CPY #$07
.C:a01b  D0 04       BNE $A021
.C:a01d  E0 95       CPX #$95
.C:a01f  F0 0C       BEQ $A02D</pre>



<p>Let&#8217;s look at <kbd>$A003</kbd> first, as it&#8217;s the target of several jump and branch instructions. This section of code loads the value at <kbd>$4024</kbd> into the <kbd>A</kbd> register, then compares it to the value at <kbd>$4030</kbd>. If the value at <kbd>$4024</kbd> is less than the value at <kbd>$4030</kbd>, the code jumps ahead to <kbd>$A011</kbd>. Otherwise, if the value at <kbd>$4024</kbd> is greater than or equal to the value at <kbd>$4030</kbd>, the value at <kbd>$4030</kbd> is copied into <kbd>$4024</kbd>, and the next instruction is at <kbd>$A011</kbd>. So, this seems to be setting an upper limit for <kbd>$4024</kbd>, with an adjustable limit value stored at <kbd>$4030</kbd>.</p>



<p><kbd>$A000</kbd> is the address jumped to when the value at <kbd>$4024</kbd> is less than <kbd>4</kbd>. This instruction increments <kbd>$4024</kbd>, and therefore enforces a hard-coded lower limit of <kbd>4</kbd> for this value. The next instruction is <kbd>$A003</kbd>, which, as we&#8217;ve just seen, contains the upper-limit code for the same location. Since <kbd>$A000</kbd> has just enforced the lower limit, the upper-limit code will likely have nothing to do. After this, the code once again reaches <kbd>$A011</kbd>.</p>



<p><kbd>$A011</kbd> seems not to have much more to do with setting the speed, and likely just carries on with other game logic. Nevertheless, I think we&#8217;ve finally achieved our first milestone, which is to find the code which handles the speed. That is, after all, where the bug seems to be. In particular, the crash happens when the player accelerates past 80.</p>



<h5 class="wp-block-heading">Stepping Through the Crash</h5>



<p>The VICE monitor has the capability to set breakpoints, which stop the computer and open the monitor prompt when the breakpoint&#8217;s condition is satisfied. These conditions are usually something like &#8220;stop when execution reaches a certain address&#8221;, but they can also break on reading/writing memory locations, and can be restricted to break only when other memory locations have specific values. It&#8217;s a very powerful debugging tool, because while the computer is paused at a breakpoint, the user can examine and/or modify memory, and step execution forward one instruction at a time.</p>



<p>Since the crash we&#8217;re looking for happens when the player tries to speed up past the limit, and since instruction <kbd>$9FFB</kbd> is executed if and only if a limit is exceeded during a speed-up action, let&#8217;s do the following:</p>



<ol class="wp-block-list">
<li>start the game,</li>



<li>break into the monitor,</li>



<li>set a breakpoint at <kbd>$9FFB</kbd>,</li>



<li>resume the game, and</li>



<li>accelerate past 80.</li>
</ol>



<p>Then, if/when our breakpoint is hit, we can trace through the code and see what might be causing the crash.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$bf5e) bk 9ffb
BREAK: 1  C:$9ffb  (Stop on exec)
(C:$bf5e) g
#1 (Stop on  exec 9ffb)   69/$045,  55/$37
.C:9ffb  4C 00 A0    JMP $A000      - A:03 X:00 Y:00 SP:ff N.-.....   63751795</pre>



<p>Nice, we hit our breakpoint, right when we expected to. Now let&#8217;s trace through the next few instructions to see if we can determine what&#8217;s going wrong.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffb) z
.C:a000  55 24       EOR $24,X      - A:03 X:00 Y:00 SP:ff N.-.....   63751798
(C:$a000) z
.C:a002  40          RTI            - A:03 X:00 Y:00 SP:ff ..-.....   63751802
(C:$a002)</pre>



<p>That&#8217;s definitely very wrong. <kbd>RTI</kbd> is an instruction meant to return from an interrupt handler. Executing that when <em>not</em> in an interrupt handler is a good way to corrupt the stack and crash the computer. We have our smoking gun. But why is this instruction being executed? And why is <kbd>$A000</kbd> now <kbd>EOR $24,X</kbd> instead of the <kbd>INC $4024</kbd> we saw before? The three bytes starting at <kbd>$A000</kbd> are currently <kbd>$55 $24 $40</kbd>. But before we started the game, they were <kbd>$EE $24 $40</kbd>, which disassembles to the expected <kbd>INC $4024</kbd>. Only the first byte is different. Somehow, the byte at <kbd>$A000</kbd> is getting corrupted and breaking the code.</p>



<h3 class="wp-block-heading">Understanding the Bug</h3>



<h5 class="wp-block-heading">The Source of the Corruption</h5>



<p>As I mentioned, the breakpoints in VICE&#8217;s monitor are pretty powerful, and they can be set up to break when a specific memory location is written to. We can use that try to find the culprit. But there&#8217;s a small complication to deal with. We don&#8217;t want to break when loading the machine code into RAM. We only want to break when <kbd>$A000</kbd> changes after that initial load.</p>



<p>So, instead of the typical <kbd>LOAD"*",8</kbd> followed by <kbd>RUN</kbd> to start the game, we&#8217;re going to do things a bit more manually. First we&#8217;ll load the BASIC loader program to look and see what steps it takes to load and start the game. Then we&#8217;ll load the machine code into RAM ourselves, and then set our breakpoint. Finally, we&#8217;ll run the command from the loader program that actually starts the game.</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-BASIC-Loader.png" alt="" class="wp-image-478" style="width:576px;height:495px" width="576" height="495" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-BASIC-Loader.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-BASIC-Loader-300x258.png 300w" sizes="(max-width: 576px) 100vw, 576px" /><figcaption class="wp-element-caption">Quest for Tires BASIC Loader Listing</figcaption></figure>



<p>The relevant lines of code for us are 50 and 60. The rest are boilerplate code to display a loading screen and handle some quirks when a BASIC program loads another program. Line 50 loads the machine code with <kbd>LOAD"QFT.8000-C010",8,1</kbd>. Line 60 then executes that code with <kbd>SYS64738</kbd>. SYS is a BASIC command that jumps to an address and starts executing the machine code there. BASIC doesn&#8217;t use hexadecimal, so the address is specified in decimal.</p>



<p>64,738 in decimal is <kbd>$FCE2</kbd> in hexadecimal, which some readers may recognize as the address of the <a rel="noreferrer noopener" href="https://en.wikipedia.org/wiki/KERNAL" data-type="URL" data-id="https://en.wikipedia.org/wiki/KERNAL" target="_blank">KERNAL</a> routine to reset the computer (without clearing the RAM). The loader program apparently loads the machine code into RAM and then resets the computer, which somehow causes the game to start. We&#8217;ll revisit this a bit later. For now, we know what steps we need to take to set our breakpoint and find out what&#8217;s corrupting memory at address <kbd>$A000</kbd>. Let&#8217;s do that now.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$e5cf) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$e5cf) bk store a000
WATCH: 1  C:$a000  (Stop on store)
(C:$e5cf) bank default
(C:$e5cf) g fce2
#1 (Stop on store a000)    2/$002,  10/$0a
.C:fd73  91 C1       STA ($C1),Y    - A:55 X:94 Y:00 SP:fd ..-..I.C 2315364035
(C:$fd75) m c1 c2
>C:00c1  00 a0                                                ..</pre>



<p>And there&#8217;s our culprit. For some reason, the instruction at <kbd>$FD73</kbd> is being executed, and that instruction stores the value of the <kbd>A</kbd> register (<kbd>$55</kbd>) into the location pointed to by <kbd>$C1</kbd> and <kbd>$C2</kbd>, which is <kbd>$A000</kbd> (remember, the CPU is little-endian). The value of the <kbd>Y</kbd> register is added to the destination address before the store happens, but <kbd>Y</kbd> is currently <kbd>0</kbd>, so that doesn&#8217;t change anything. What is address <kbd>$FD73</kbd>, and why is it writing into our program&#8217;s code? Why is it even executing at all? The monitor&#8217;s <kbd>backtrace</kbd> command might help us find out.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$00c3) bt
(0) 800f
(C:$00c3)</pre>



<p>Interesting. The caller of this routine is at <kbd>$800F</kbd>, which is very near the start of our game&#8217;s machine code. Let&#8217;s disassemble the code from the beginning and look at <kbd>$800F</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="10" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$00c3) d 8000 8020
.C:8000  00          BRK
.C:8001  C0 20       CPY #$20
.C:8003  80 C3       NOOP #$C3
.C:8005  C2 CD       NOOP #$CD
.C:8007  38          SEC
.C:8008  30 8E       BMI $7F98
.C:800a  16 D0       ASL $D0,X
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
.C:8019  78          SEI
.C:801a  4C 47 FE    JMP $FE47
.C:801d  8D 18 03    STA $0318
.C:8020  20 BC F6    JSR $F6BC
(C:$8023)</pre>



<h5 class="wp-block-heading">RAMTAS</h5>



<p>At <kbd>$800F</kbd>, we find the instruction <kbd>JSR $FD50</kbd>. This instruction calls the subroutine at the specified address. <kbd>$FD50</kbd> is pretty close to the offending instruction, <kbd>$FD73</kbd>, so this makes sense. <kbd>$FD50</kbd> is within the KERNAL ROM&#8217;s address space, and is in fact a documented routine, named RAMTAS, that can be called by other programs. The pagetable.com KERNAL API reference says this about RAMTAS:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Perform RAM test</strong></p>



<ul class="wp-block-list">
<li>Communication registers: A, X, Y</li>



<li>Preparatory routines: None</li>



<li>Error returns: None</li>



<li>Stack requirements: 2</li>



<li>Registers affected: A, X, Y</li>
</ul>



<p><strong>Description</strong>: This routine is used to test RAM and set the top and bottom of memory pointers accordingly. It also clears locations $00 to $0101 and $0200 to $03FF. It also allocates the cassette buffer, and sets the screen base to $0400. Normally, this routine is called as part of the initialization process of a Commodore 64 program cartridge.</p>
</blockquote>



<p>and also this:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>RAMTAS</strong></p>



<p>This routine clears zero-page RAM (locations $02-$FF) and initializes Kernal memory pointers in zero page. For the 64 only, the routine also clears pages 2 and 3 (locations $0200-$03FF), tests all RAM locations from $0400 upwards until ROM is encountered, and sets the top-of-memory pointer. For the 128, the routine sets the BASIC restart vector ($0A00) to point to BASIC&#8217;s cold-start entry address, $4000.</p>
</blockquote>



<p>This line is particularly interesting: <em>&#8220;Normally, this routine is called as part of the initialization process of a Commodore 64 program cartridge.&#8221;</em> Quest for Tires was released both on floppy disk and on ROM cartridge, and there were some utility programs available to convert a cartridge program to be able to load and run from disk. Furthermore, ROM cartridges use the memory space <kbd>$8000</kbd>&#8211;<kbd>$9FFF</kbd> for 8k cartridges, and <kbd>$8000</kbd>&#8211;<kbd>$BFFF</kbd> for 16k cartridges. This latter range matches our code&#8217;s address range almost exactly. It seems probable that this floppy disk copy of the game was originally created by converting from the cartridge version. The extra 16 bytes at the end could be some glue code to help with the conversion.</p>



<h5 class="wp-block-heading">Cartridge Conversion</h5>



<p>Why does it matter that this code was converted from a cartridge? First, it explains why the BASIC loader resets the computer to start the game. Cartridges are inserted before powering on the computer, and when the booting system notices that a cartridge is plugged in, it starts executing code from that cartridge. Also, since the game was originally a cartridge, it needs to call the RAMTAS routine itself, as the KERNAL doesn&#8217;t do that automatically when a cartridge is inserted.</p>



<p>The RAMTAS routine, among other things, writes a <kbd>$55</kbd> byte (sound familiar?) to each memory location and reads it back to make sure it matches. If the byte matches, the original byte that was stored at the location is restored, and testing moves on to the next address. If the byte doesn&#8217;t match, the routine assumes it&#8217;s found a ROM, stops testing RAM, and updates some KERNAL pointers to indicate where the top and bottom of RAM are. Take a look at the <a rel="noreferrer noopener" href="https://www.pagetable.com/c64ref/c64disasm/" data-type="URL" data-id="https://www.pagetable.com/c64ref/c64disasm/" target="_blank">C64 BASIC &amp; KERNAL ROM Disassembly</a> section of Michael Steil&#8217;s reference site for full details on this routine.</p>



<p>When the game is run from a ROM cartridge, it&#8217;s mapped into memory at <kbd>$8000</kbd>&#8211;<kbd>$BFFF</kbd>. As part of its initialization, it calls RAMTAS, which then starts testing RAM, but stops at <kbd>$8000</kbd>, because it hits the ROM. But, when the game is in RAM instead, RAMTAS keeps going when it hits <kbd>$8000</kbd>. This is generally non-destructive, since each byte is put back after it passes the RAM test. However, recall that the BASIC ROM is mapped in at <kbd>$A000</kbd>&#8211;<kbd>$BFFF</kbd>, the first byte of which is exactly our problem address.</p>



<p>Here&#8217;s the crucial point. RAMTAS writes <kbd>$55</kbd> to <kbd>$A000</kbd>, and reads back a different value (the first byte in the BASIC ROM), so it (correctly) concludes that it&#8217;s found a ROM. However, <em>it has already</em> <em>written to the RAM underneath the ROM at that address</em>, which is holding our program! Furthermore, RAMTAS doesn&#8217;t try to restore the byte, since it thinks it&#8217;s from a ROM, and what would be the point of trying to write to ROM? This is the root cause of our corrupted byte.</p>



<p>But shouldn&#8217;t the BASIC ROM be banked out already? It&#8217;s masking half of the game code, so it will need to be banked out for the game to be able to run. Let&#8217;s take a look at that glue code at <kbd>$C000</kbd> to <kbd>$C00F</kbd>.</p>



<h5 class="wp-block-heading">Glue Code</h5>



<p>When the C64 boots up, it looks at addresses <kbd>$8004</kbd> to <kbd>$8008</kbd> for the byte sequence <kbd>$C3 $C2 $CD $38 $30</kbd>. That&#8217;s <a href="https://en.wikipedia.org/wiki/PETSCII" data-type="URL" data-id="https://en.wikipedia.org/wiki/PETSCII" target="_blank" rel="noreferrer noopener">PETSCII</a> for &#8220;CBM80&#8221;. &#8220;CBM&#8221; stands for Commodore Business Machines. I&#8217;m not sure what the &#8220;80&#8221; signifies. If that sequence is found, the system sets the <a href="https://en.wikipedia.org/wiki/Non-maskable_interrupt" data-type="URL" data-id="https://en.wikipedia.org/wiki/Non-maskable_interrupt" target="_blank" rel="noreferrer noopener">NMI (Non-Maskable Interrupt)</a> <a href="https://en.wikipedia.org/wiki/Interrupt_vector_table" data-type="URL" data-id="https://en.wikipedia.org/wiki/Interrupt_vector_table" target="_blank" rel="noreferrer noopener">vector</a> to point to the address found at <kbd>$8002</kbd> and <kbd>$8003</kbd>. The C64&#8217;s Restore key is wired to the CPU&#8217;s NMI line, so pressing that key will trigger an NMI and jump to the address pointed to by the NMI vector. After setting up the vector, the system jumps to the address pointed to by <kbd>$8000</kbd> and <kbd>$8001</kbd> and starts executing code there.</p>



<p>If we look at memory at <kbd>$8000</kbd> to <kbd>$8008</kbd>, we find the CBM80 signature at <kbd>$8004</kbd>, and the two pointers at <kbd>$8000</kbd> and <kbd>$8002</kbd>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8023) m 8004 8008
>C:8004  c3 c2 cd 38  30                                      ...80
(C:$8009) m 8000 8001
>C:8000  00 c0                                                ..
(C:$8002) m 8002 8003
>C:8002  20 80                                                 .</pre>



<p>This is definitely a cartridge conversion, or at the very least, is using the cartridge mechanism. <kbd>$8000</kbd> points to <kbd>$C000</kbd>, so that&#8217;s where the system will start executing code once it&#8217;s finished booting. Notably, this is just outside the 16k ROM cartridge address range, so it must have been added by the conversion utility. Let&#8217;s look at that code:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8004) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  AA          TAX
.C:c008  AA          TAX
.C:c009  AA          TAX
.C:c00a  AA          TAX
.C:c00b  AA          TAX
.C:c00c  AA          TAX
.C:c00d  AA          TAX
.C:c00e  AA          TAX
.C:c00f  AA          TAX</pre>



<p>Aha, the first two instructions set the memory bank register to <kbd>$36</kbd>, or <kbd>%00110110</kbd> in binary. The default value for this register is <kbd>$37</kbd>, or <kbd>%00110111</kbd> in binary, so this clears the lowest-order bit. That happens to be the bit that controls the BASIC ROM, and setting it to <kbd>0</kbd> banks out that ROM. This is the sort of thing we should expect to see here.</p>



<p>The next instruction jumps to the start of the game code. It&#8217;s likely that the original cartridge had the entry point at <kbd>$8000</kbd> set to <kbd>$8009</kbd>, and the conversion utility redirected execution to <kbd>$C000</kbd>, where it put code to bank out the BASIC ROM (because that&#8217;s necessary when running from RAM) and then jump back to where the code would have started. The remaining <kbd>$AA</kbd> bytes are simply padding to ensure that the file size is a multiple of 16 bytes. That&#8217;s not really necessary, but it doesn&#8217;t hurt anything either.</p>



<p>This technique is sometimes referred to as patching, although that term is more broadly applicable than this specific approach. The additional code itself is sometimes called patch code, for hopefully obvious reasons, or glue code, since its purpose is to stick together things that normally don&#8217;t go together (in this case, code from a ROM cartridge running from RAM).</p>



<p>If the glue code sets the bank register to bank out the BASIC ROM, why is the RAMTAS routine hitting it and messing up the game code? Let&#8217;s trace through the glue code and see if we can find out.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$c010) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$c010) bk c000
BREAK: 1  C:$c000  (Stop on exec)
(C:$c010) g fce2
#1 (Stop on  exec c000)   60/$03c,  17/$11
.C:c000  A9 36       LDA #$36       - A:C3 X:00 Y:91 SP:ff ..-..IZC    6482922
(C:$c000) m 00 01
>C:0000  2f 37                                                /7
(C:$0002) z
.C:c002  85 01       STA $01        - A:36 X:00 Y:91 SP:ff ..-..I.C    6482924
(C:$c002) z
.C:c004  4C 09 80    JMP $8009      - A:36 X:00 Y:91 SP:ff ..-..I.C    6482927
(C:$c004) m 00 01
>C:0000  2f 36                                                /6
(C:$0002) z
.C:8009  8E 16 D0    STX $D016      - A:36 X:00 Y:91 SP:ff ..-..I.C    6482930
(C:$8009) z
.C:800c  20 A3 FD    JSR $FDA3      - A:36 X:00 Y:91 SP:ff ..-..I.C    6482934
(C:$800c) m 00 01
>C:0000  2f 36                                                /6
(C:$0002) n
.C:800f  20 50 FD    JSR $FD50      - A:D7 X:FF Y:91 SP:ff N.-..I.C    6483073
(C:$800f) m 00 01
>C:0000  2f 37                                                /7</pre>



<p>As we can see from this trace, The glue code does, in fact, successfully bank out the BASIC ROM, and it stays banked out right up until the <kbd>JSR $FDA3</kbd> subroutine call at <kbd>$800C</kbd>. That routine must have banked it back in, just before the call to RAMTAS (<kbd>$FD50</kbd>). What is this routine?</p>



<h5 class="wp-block-heading">IOINIT</h5>



<p>According to pagetable.com&#8217;s KERNAL ROM disassembly and KERNAL API reference, <kbd>$FDA3</kbd> contains a KERNAL routine named IOINIT. This is what it has to say about that routine:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Initialize I/O devices</strong></p>



<ul class="wp-block-list">
<li>Communication registers: None</li>



<li>Preparatory routines: None</li>



<li>Error returns:</li>



<li>Stack requirements: None</li>



<li>Registers affected: A, X, Y</li>
</ul>



<p><strong>Description</strong>: This routine initializes all input/output devices and routines. It is normally called as part of the initialization procedure of a Commodore 64 program cartridge.</p>



<p>EXAMPLE:</p>



<p><kbd>    JSR IOINIT</kbd></p>
</blockquote>



<p>and also this:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Initialize I/O devices</strong></p>



<p><strong>Called by</strong>: None.</p>



<p>JMP FDA3 to initialize the CIA registers, the 6510 I/O data registers, the SID chip registers, start CIA #1 timer A, enable CIA #1 timer A interrupts, and set the serial clock output line high.</p>



<p>The closest equivalent to this routine on the VIC is JMP FDF9 to initialize the 6522 registers, set VIA #2 timer 1 value, start timer 1, and enable timer 1 interrupts.</p>



<p>Since these routines are called during system reset, the main use for IOINIT is for an autostart cartridge that wants to use the same I/O settings that the Kernal normally uses.</p>
</blockquote>



<p>So, ultimately, the cartridge conversion utility used a patch and some glue code to bank out the BASIC ROM to let the game run, but wasn&#8217;t clever enough to notice that IOINIT banks it back in right away, and then RAMTAS comes along and clobbers the code at <kbd>$A000</kbd>, leading to the crash bug we&#8217;ve been diagnosing. This is almost everything we need to know to understand why the game crashes when it does, but one small question remains. If the BASIC ROM has been banked in over the top of the second half of the game code, how does the game run at all?</p>



<p>The answer is that, a few instructions later, the game code itself banks the BASIC ROM back out again, and so it&#8217;s only banked in for a brief moment, during which RAMTAS gets confused and introduces the bug.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$0002) d 8009 8033
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
.C:8019  78          SEI
.C:801a  4C 47 FE    JMP $FE47
.C:801d  8D 18 03    STA $0318
.C:8020  20 BC F6    JSR $F6BC
.C:8023  20 15 FD    JSR $FD15
.C:8026  20 A3 FD    JSR $FDA3
.C:8029  20 18 E5    JSR $E518
.C:802c  58          CLI
.C:802d  A9 36       LDA #$36
.C:802f  85 01       STA $01
.C:8031  4C CD 8F    JMP $8FCD</pre>



<p>At <kbd>$802D</kbd>&#8211;<kbd>$8030</kbd>, the game sets <kbd>$01</kbd> back to <kbd>$36</kbd>, banking out the BASIC ROM (for good this time) and then jumps off to <kbd>$8FCD</kbd>, which presumably shows the title screen and then starts the game itself.</p>



<h3 class="wp-block-heading">Fixing the Bug</h3>



<p>Now that we&#8217;ve figured out what&#8217;s going wrong, how do we fix it? Through my journey diagnosing this issue, I&#8217;ve tried several different approaches, some of which were successful. In this section, I&#8217;ll describe some of the successful fixes and discuss their pros and cons.</p>



<h5 class="wp-block-heading">Fix 1: Avoid the Corrupted Instruction</h5>



<p>The first fix I tried that actually worked was to adjust to code to skip over the instruction at <kbd>$A000</kbd>, instead achieving the same effect by jumping somewhere else. Recall that the correct instruction is <kbd>INC $4024</kbd>, which is followed by <kbd>LDA $4024</kbd> at location <kbd>$A003</kbd>. Let&#8217;s see if we can find another place in the code that does that:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8034) h 8000 bfff ee 24 40
9fe8
a000</pre>



<p>There are only two instances of that instruction in the entire program. One of them is <kbd>$A000</kbd>, which is what we&#8217;re trying to avoid. Let&#8217;s look at the other one at <kbd>$9FE8</kbd>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="2-3" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8034) d 9fe8 9ffd
.C:9fe8  EE 24 40    INC $4024
.C:9feb  4C 03 A0    JMP $A003
.C:9fee  EE 61 40    INC $4061
.C:9ff1  CE 24 40    DEC $4024
.C:9ff4  AD 24 40    LDA $4024
.C:9ff7  C9 04       CMP #$04
.C:9ff9  B0 08       BCS $A003
.C:9ffb  4C 00 A0    JMP $A000</pre>



<p>As expected, the code at <kbd>$9FE8</kbd> executes the same increment instruction as the one that got corrupted. However, somewhat miraculously, it then unconditionally jumps to <kbd>$A003</kbd>, which is immediately after the corrupted instruction, and just where we need to go next. So, if we replace every jump to $A000 with a jump to $9FE8 instead, that should avoid the corruption and do what the original program intended. Let&#8217;s look for the places we need to change:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffe) h 8000 bfff 4c 00 a0
9ffb
bffb</pre>



<p>Only two places found. The first ($9FFB), we just saw above. What about the second?</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="14" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffe) d bfe0 bfff
.C:bfe0  01 80       ORA ($80,X)
.C:bfe2  01 80       ORA ($80,X)
.C:bfe4  01 80       ORA ($80,X)
.C:bfe6  01 80       ORA ($80,X)
.C:bfe8  01 1E       ORA ($1E,X)
.C:bfea  1F 20 46    SLO $4620,X
.C:bfed  47 48       SRE $48
.C:bfef  6E 6F 70    ROR $706F
.C:bff2  97 98       SAX $98,Y
.C:bff4  BF C0 E6    LAX $E6C0,Y
.C:bff7  E7 E8       ISB $E8
.C:bff9  B0 08       BCS $C003
.C:bffb  4C 00 A0    JMP $A000
.C:bffe  F9 07 A9    SBC $A907,Y</pre>



<p>This is basically gibberish, so it&#8217;s probably not code. It&#8217;s likely some sort of data, perhaps graphics or sound data. We shouldn&#8217;t need to worry about this, so the only <kbd>JMP $A000</kbd> instruction we&#8217;ve found that we need to update is the one at <kbd>$9FFB</kbd>. Note that there could be conditional branch instructions or indirect jump instructions that target <kbd>$A000</kbd>.</p>



<p>The branch instructions would be within the 256 bytes around <kbd>$A000</kbd>, since they can only jump a limited distance. That&#8217;s still lot of disassembly code to show, however, so perhaps you&#8217;ll take my word for it that I looked and didn&#8217;t see any. Indirect jumps to a specific location are hard to find, since it would require dynamic analysis of the code to determine the jump target. If we miss one, the game might still crash, but let&#8217;s try it and see what happens. The following commands make the change and then save the updated code to a new file on disk called <kbd>"FIX1.8000-C010"</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="12" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$c000) a 9ffb
.9ffb  jmp $9fe8
.9ffe  
(C:$9ffe) d 9fe8 9ffd
.C:9fe8  EE 24 40    INC $4024
.C:9feb  4C 03 A0    JMP $A003
.C:9fee  EE 61 40    INC $4061
.C:9ff1  CE 24 40    DEC $4024
.C:9ff4  AD 24 40    LDA $4024
.C:9ff7  C9 04       CMP #$04
.C:9ff9  B0 08       BCS $A003
.C:9ffb  4C E8 9F    JMP $9FE8
(C:$9ffe) bank ram
(C:$9ffe) s "fix1.8000-c010" 8 8000 c00f
Saving file `fix1.8000-c010' from $8000 to $c00f</pre>



<p>Next, we need to update the BASIC loader to load our fixed version of the code:</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img decoding="async" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Loader-Fix-1.png" alt="" class="wp-image-541" style="width:576px;height:495px" width="576" height="495" srcset="https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Loader-Fix-1.png 768w, https://oldblog.natebarney.com/wp-content/uploads/2023/08/Quest-for-Tires-Loader-Fix-1-300x258.png 300w" sizes="(max-width: 576px) 100vw, 576px" /><figcaption class="wp-element-caption">Quest for Tires BASIC Loader (Fix 1)</figcaption></figure>



<p>Now, all that&#8217;s left to do is try it! I started this section by saying I would only discuss successful fixes, so there&#8217;s not much suspense, but here&#8217;s a video of this fixed code running, with the speed going all the way up to 80 without crashing! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<figure style="display: block; margin-left: auto; margin-right: auto;" class="wp-block-video"><video style="display: block; margin-left: auto; margin-right: auto;" src="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-For-Tires-Fix1.mp4" controls="controls" width="384" height="330"></video>
<figcaption style="text-align: center;" class="wp-element-caption">Quest for Tires Gameplay (Fix 1)</figcaption>
</figure>



<p>Clearly, this fix works, which is great. One downside, however, is that it slightly changes the number of cycles it takes to execute the speed update code during the main game loop. In practice, it&#8217;s unlikely to matter, but it might subtly change the gameplay. Another drawback is more aesthetic—it&#8217;s not the same control flow as what was originally published. Let&#8217;s see how we might improve on this.</p>



<h5 class="wp-block-heading">Fix 2: Undo the Corruption</h5>



<p>Instead of modifying the main game loop, maybe there&#8217;s a way we can repair the damage RAMTAS did to our code. We know exactly which byte it clobbered and what the value was before said clobbering. Perhaps we can borrow the trick that the cartridge conversion utility used, and insert some code right after RAMTAS returns that restores the byte&#8217;s original value. We have nine padding bytes to work with, and since this is stored on a disk, we could add quite a bit of extra code if we needed to. But, I think we can fit what we need to do into the existing padding bytes.</p>



<p>What we want to do is store the value <kbd>$EE</kbd> into the location <kbd>$A000</kbd> as soon as possible after the call to RAMTAS returns. One way to do that is to write our own subroutine that calls RAMTAS and then immediately fixes up memory before returning. That would look something like this:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="26-29" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$9ffe) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$9ffe) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  AA          TAX
.C:c008  AA          TAX
.C:c009  AA          TAX
.C:c00a  AA          TAX
.C:c00b  AA          TAX
.C:c00c  AA          TAX
.C:c00d  AA          TAX
.C:c00e  AA          TAX
.C:c00f  AA          TAX
(C:$c010) a c007
.c007  jsr $fd50
.c00a  lda #$ee
.c00c  sta $a000
.c00f  rts
.c010
(C:$c010) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  20 50 FD    JSR $FD50
.C:c00a  A9 EE       LDA #$EE
.C:c00c  8D 00 A0    STA $A000
.C:c00f  60          RTS</pre>



<p>We used up all the padding bytes, but we now have the routine we need stored in memory. Next, we need to patch the game code to call our routine instead of calling RAMTAS directly:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="4,14" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$c010) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
(C:$8019) a 800f
.800f  jsr $c007
.8012
(C:$8012) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 07 C0    JSR $C007
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI</pre>



<p>And that should do it. Let&#8217;s save the code, set some breakpoints, start the game, and trace through to make sure our patch restores the value of <kbd>$A000</kbd> to <kbd>$EE</kbd> and then resumes normal execution, as we expect:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="12,19,25" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8019) bank ram
(C:$8019) s "fix2.8000-c010" 8 8000 c00f
Saving file `fix2.8000-c010' from $8000 to $c00f
(C:$8019) bk 800f
BREAK: 1  C:$800f  (Stop on exec)
(C:$8019) bk c00a
BREAK: 2  C:$c00a  (Stop on exec)
(C:$8019) g fce2
#1 (Stop on  exec 800f)   77/$04d,  49/$31
.C:800f  20 07 C0    JSR $C007      - A:D7 X:FF Y:A0 SP:ff N.-..I.C   49939549
(C:$800f) m a000 a000
>C:a000  ee                                                   .
(C:$a001) z
.C:c007  20 50 FD    JSR $FD50      - A:D7 X:FF Y:A0 SP:fd N.-..I.C   49939555
(C:$c007) n
#2 (Stop on  exec c00a)  156/$09c,  62/$3e
.C:c00a  A9 EE       LDA #$EE       - A:04 X:00 Y:A0 SP:fd ..-..I..   52115762
(C:$c00a) m a000 a000
>C:a000  55                                                   U
(C:$a001) z
.C:c00c  8D 00 A0    STA $A000      - A:EE X:00 Y:A0 SP:fd N.-..I..   52115764
(C:$c00c) z
.C:c00f  60          RTS            - A:EE X:00 Y:A0 SP:fd N.-..I..   52115768
(C:$c00f) m a000 a000
>C:a000  ee                                                   .
(C:$a001) z
.C:8012  20 15 FD    JSR $FD15      - A:EE X:00 Y:A0 SP:ff N.-..I..   52115774</pre>



<p>It worked! The trace shows that execution gets diverted to our new routine. Before we call RAMTAS, <kbd>$A000</kbd> has the correct value. After RAMTAS returns, the value is corrupted. But after the next two instructions, it&#8217;s restored to the correct value again. Finally, our routine returns, and the game continues as if nothing happened.</p>



<p>This is much nicer than the first fix, because it doesn&#8217;t modify code in the middle of the game loop. It&#8217;s still annoying, however, that we have to let the code get corrupted in the first place. Perhaps we can improve this further.</p>



<h5 class="wp-block-heading">Fix 3: Prevent the Corruption</h5>



<p>Recall that the reason RAMTAS corrupts our program is that, even though the glue code banked out the BASIC ROM, the call to IOINIT banked it back in right before RAMTAS is called. What if we inserted some code between those two calls to bank it out again. Then RAMTAS should be able to complete without screwing anything up. Let&#8217;s give it a shot. First we set up our routine in the padding area, as before. This time, however, instead of calling RAMTAS right away, we set the bank register first. Then we jump directly to the RAMTAS routine, so that when it returns, it will return to our routine&#8217;s caller.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="25-27" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8012) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$8012) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  AA          TAX
.C:c008  AA          TAX
.C:c009  AA          TAX
.C:c00a  AA          TAX
.C:c00b  AA          TAX
.C:c00c  AA          TAX
.C:c00d  AA          TAX
.C:c00e  AA          TAX
.C:c00f  AA          TAX
(C:$c010) a c007
.c007  lda #$36
.c009  sta $01
.c00b  jmp fd50
.c00e  
(C:$c00e) d c000 c00f
.C:c000  A9 36       LDA #$36
.C:c002  85 01       STA $01
.C:c004  4C 09 80    JMP $8009
.C:c007  A9 36       LDA #$36
.C:c009  85 01       STA $01
.C:c00b  4C 50 FD    JMP $FD50
.C:c00e  AA          TAX
.C:c00f  AA          TAX</pre>



<p>Then, as before, we replace the original call to RAMTAS with a call to our routine instead:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="4,14" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$c010) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
(C:$8019) a 800f
.800f  jsr $c007
.8012  
(C:$8012) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 07 C0    JSR $C007
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI</pre>



<p>All right, let&#8217;s try this out.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="13,16,25,31,34" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8019) bank ram
(C:$8019) s "fix3.8000-c010" 8 8000 c00f
Saving file `fix3.8000-c010' from $8000 to $c00f
(C:$8019) bk 800f
BREAK: 1  C:$800f  (Stop on exec)
(C:$8019) bk 8012
BREAK: 2  C:$8012  (Stop on exec)
(C:$8019) g fce2
#1 (Stop on  exec 800f)  184/$0b8,  60/$3c
.C:800f  20 07 C0    JSR $C007      - A:D7 X:FF Y:0A SP:ff N.-..I.C    4200295
(C:$800f) bank default
(C:$800f) m 00 01
>C:0000  2f 37                                                /7
(C:$0002) bank ram
(C:$0002) m a000 a000
>C:a000  ee                                                   .
(C:$a001) z
.C:c007  A9 36       LDA #$36       - A:D7 X:FF Y:0A SP:fd N.-..I.C    4200301
(C:$c007) z
.C:c009  85 01       STA $01        - A:36 X:FF Y:0A SP:fd ..-..I.C    4200303
(C:$c009) z
.C:c00b  4C 50 FD    JMP $FD50      - A:36 X:FF Y:0A SP:fd ..-..I.C    4200306
(C:$c00b) bank default
(C:$c00b) m 00 01
>C:0000  2f 36                                                /6
(C:$0002) g
#2 (Stop on  exec 8012)   39/$027,   2/$02
.C:8012  20 15 FD    JSR $FD15      - A:04 X:11 Y:D0 SP:ff ..-..I..    6994392
(C:$8012) bank default
(C:$8012) m 00 01
>C:0000  2f 36                                                /6
(C:$0002) bank ram
(C:$0002) m a000 a000
>C:a000  ee                                                   .</pre>



<p>This worked nicely. Because we set the bank register before calling RAMTAS, <kbd>$A000</kbd> was never corrupted. As before, the game continues after our patch, none the wiser. This is a pretty good fix, but there&#8217;s one thing I can think of that might be even better.</p>



<h5 class="wp-block-heading">Fix 4: Prevent the Corruption, but Even Better</h5>



<p>The reason the game code calls IOINIT and RAMTAS is that, as a ROM cartridge, these routines would not be called by the KERNAL during bootup. However, we&#8217;re not running this code from ROM anymore, and there&#8217;s no cartridge in the expansion port. Maybe these calls are redundant, and could be entirely eliminated. Let&#8217;s remove the calls, and set some breakpoints to see if the routines still get called from somewhere else.</p>



<p>To remove these subroutine calls, the simplest thing to do is overwrite them with the NOP instruction. That&#8217;s a one-byte instruction that tells the processor to do nothing for a couple of clock cycles. Each subroutine call assembles to three bytes of code, so we need to put in six NOPs in total, starting at <kbd>$800C</kbd>.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="5-6,20-25" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$e5cd) l "qft.8000-c010" 8
Loading qft.8000-c010 from 8000 to C00F (4010 bytes)
(C:$e5cd) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  20 A3 FD    JSR $FDA3
.C:800f  20 50 FD    JSR $FD50
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI
(C:$8019) a 800c
.800c  nop
.800d  nop
.800e  nop
.800f  nop
.8010  nop
.8011  nop
.8012  
(C:$8012) d 8009 8018
.C:8009  8E 16 D0    STX $D016
.C:800c  EA          NOP
.C:800d  EA          NOP
.C:800e  EA          NOP
.C:800f  EA          NOP
.C:8010  EA          NOP
.C:8011  EA          NOP
.C:8012  20 15 FD    JSR $FD15
.C:8015  20 18 E5    JSR $E518
.C:8018  58          CLI</pre>



<p>Okay, let&#8217;s try it:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="9-10" data-enlighter-linenumbers="false" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">(C:$8019) bank ram
(C:$8019) s "fix4.8000-c010" 8 8000 c00f
Saving file `fix4.8000-c010' from $8000 to $c00f
(C:$8019) bk fda3
BREAK: 1  C:$fda3  (Stop on exec)
(C:$8019) bk fd50
BREAK: 2  C:$fd50  (Stop on exec)
(C:$8019) g fce2
#1 (Stop on  exec fda3)   66/$042,   2/$02
.C:fda3  A9 7F       LDA #$7F       - A:31 X:30 Y:FF SP:fa N.-..I..   34382337
(C:$fda3) g</pre>



<p>The breakpoint at IOINIT was hit, so the KERNAL must be calling that for us. The breakpoint at RAMTAS wasn&#8217;t hit. Interestingly, however, the game still started and ran just fine, and did not exhibit the crash bug. Apparently neither of these calls was needed. So, we have a fix that consists solely of removing two subroutine calls in the initialization code for the game, and replacing them with NOPs. That feels pretty elegant to me, and so I&#8217;m going to call this solved. There may be even more elegant solutions, and I&#8217;d love to hear about them, but I&#8217;m pretty happy with this.</p>



<h2 class="wp-block-heading">Conclusion</h2>



<p>When I started the process of diagnosing and fixing this crash, I had no idea what I&#8217;d find, or whether it would be something I could fix. It was a lot of fun though. There were quite a few twists and turns, and I&#8217;ve now fixed the copy of a game I&#8217;ve had since childhood, which is pretty cool.</p>



<p>Out of curiosity, I tried a few different copies of Quest for Tires that I acquired from <em>places</em> (ahem), both disk and cartridge images, and none of them exhibited this bug. So, it seems that it was a quirk of my particular copy. (I&#8217;m honestly not even sure exactly where the disk came from.) I suppose I could just play those copies, but it feels very satisfying to be able to play the version I grew up with, albeit slightly altered, without any more crashing.</p>



<p>This is the first time I&#8217;ve dug into the code of a commercial C64 game to try to make modifications, and I might not have been as efficient as someone more experienced in this domain. If I&#8217;ve made mistakes, or made inaccurate statements, or done things the hard way, I&#8217;d love to hear about it. I&#8217;d also very much like to hear about other solutions people might come up with to fix the crash. In any case, I hope you found this at least a bit entertaining, and possibly even educational. I definitely learned a lot.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://oldblog.natebarney.com/2023/08/05/quest-for-tires/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-For-Tires-Crash.mp4" length="767058" type="video/mp4" />
<enclosure url="https://blog.natebarney.com/wp-content/uploads/2023/08/Quest-For-Tires-Fix1.mp4" length="1483013" type="video/mp4" />

			</item>
	</channel>
</rss>
