Assembly Headaches By Jeremiah Stoddard on January 18, 2023 Just a mundane post about the standard dumb mistakes that virtually all programmers make now and then. I left the software development business some years ago, but have been doing some programming on some old 8-bit systems for fun. Today I faced the 6502 assembly language equivalent of forgetting a semicolon at the end of a C statement, and had some fun troubleshooting. I'm working on a little toy app that's supposed to go through some Dragon Quest style battle routines in text mode, hopefully ultimately resulting in a melee engine that can be used in a graphical RPG for the Apple IIe. Nothing big and serious like Nox Archaist, just a little hobby project in my spare time to do the type of game I wanted, but never managed, to make as a kid. Well yeah, battle routines. So I started by writing some assembly routines to print out text to the screen, then turned it into a menu and a loop to get user input and repeat until the user asks to 'Quit' the program. Since a battle engine is the goal, at this point I should have put in a menu entry to start a battle, like an arena-type game, I guess. But no, I started thinking that the game is going to need sound effects, and although I know accessing memory location $C030 "tweaks" the speaker, I hadn't really tried to do anything with sound. So I naturally forgot about the melee system and the first menu option other than 'Quit' was to play a sound effect. I actually got more or less the noise I wanted out of the speaker on the first try. That's not what this post is about. The issue I had arose out of some additional tweaks to the menu. I had set up something like this: ; menu display code ... LOOP JSR RDKEY ; read a character from the keyboard AND #$5F ; force to uppercase CMP #$50 ; 'P' (for play sound) BEQ PLYSND ... CMP #$51 ; 'Q' (for quit) BNE LOOP RTS PLYSND JSR SUBROUTINE JMP LOOP ... At least, that's what I thought I had set up. This was written up in the editor for the ORCA/M assembler. I assembled the file, then in the linking stage the linker gave me an error along the lines of 'Relative address out of range' after MAIN at location 67, program counter 2067, blah blah blah. Because of the error, and because it was in the MAIN section, I knew it had to be one of the BEQs or BNEs in the menu loop. I was confused, though, since I glanced through the beginning of the code, counting in my head the number of bytes I expected each instruction to take up, and 67 would land me in the middle of printing the menu. There are JSRs there, but nothing that would use a relative address. Moreover, the branches in the loop should have all been not more than a couple dozen bytes from their destination, at most. Of course it didn't occur to me that I should be counting in hexadecimal, not decimal. Anyway, I could check the assembler listing and linker output easily enough, since the ORCA system provides Unix-like output redirection: # ASML TEST.ASM >OUTPUT.TXT Sure enough, at address 2067 in the assembly listing was the BEQ after checking for the 'P' keypress. The error was simple. Instead of branching to the nearby label that JSRs to the sound SUBROUTINE, I was trying to directly branch to the subroutine: BEQ SUBROUTINE Had that subroutine been close enough to the menu loop, there would have been a tricky bug to find, since no return address would have been pushed to the stack for the subroutine. I don't want to think about the headaches that would have caused.