Quote:
For context: I've learned a good amount about writing a game in assembly, so I'm wondering if I should consider writing any parts of a game in a higher level language.
Beyond what others have said, you can dramatically raise the level of assembly language by using macros, making the source code much shorter and easier to read and maintain, producing fewer bugs, and doing it with (in most cases) absolutely no penalty in the final run speed or memory taken. Beware however macros have been used poorly by many, resulting in a lot of invalid advice against macros.
Here's a simple one to start. When you have to copy a 2-byte variable from one place to another, you usually get this:
Code:
LDA VARIABLE1
STA VARIABLE2
LDA VARIABLE1+1
STA VARIABLE2+1
It keeps coming up; so why not make it a macro that assembles exactly the same thing:
Code:
COPY2 VARIABLE1, TO, VARIABLE2
Code:
DISPLAY_IMM "Press ENTER to begin"
WAIT_FOR_KEY ENTER_KEY
is more readable and intuitive than
Code:
JSR DISP_QUOTE
BYTE dim2#-dim1# ; Lay down the string length byte,
dim1#: BYTE "Press ENTER to begin." ; followed by the string. (Counted string, not nul-terminated.)
dim2#: JSR INPUT_KEY
CMP #KEY
BNE dim2#
but assembles the same thing.
You can do conditionals and looping controls (etc.) too. Here's a simple one:
Code:
CMP #14
IF_EQ
<actions>
<actions>
<actions>
ELSE_
<actions>
<actions>
<actions>
END_IF
(Note there are no labels necessary.) "<actions>" can be any assembly-language instructions, more macro invocations, and even nested IF structures. In the structure above, the IF_EQ assembles a BNE to the first instruction after the ELSE_. It won't know that instruction's address yet, so it puts its own address on a stack in the assembler itself, then ELSE_, when encountered, will calculate the branch distance and go up and fill in the operand for the IF_EQ. ELSE_ then does a similar thing so that END_IF can fill in the right operand for ELSE_. ELSE assembles a JMP (or, for the 65c02, an unconditional branch, BRA) down to the END_IF. END_IF does not add any code, only fill in the operand for the instruction laid down by ELSE_. And again, these are nestable. IOW, you could have one IF...END_IF inside another one, many levels deep, and each one will branch to the right place.
How about a looping one:
Code:
LDX #8
label: <do stuff>
<do stuff>
DEX
BNE label
can be made more clear (although not much shorter) by writing it this way:
Code:
FOR_X 8, DOWN_TO, 0
<do stuff>
<do stuff>
NEXT_X
and the resulting machine code will be exactly the same. (Note that the FOR...NEXT works slightly differently from how BASIC does. Just keep in mind how the assembly language works, that the decrement (DEX in this case) comes before the conditional branch (BNE in this case) to the top of the loop; so the loop will get run for 8, 7, 6,...1, but not 0, since it will drop through on 0.)
I have lots more of these, plus some accessory macros for example RTS_IF_MINUS in my article at
http://wilsonminesco.com/StructureMacros/, and extended code examples in the last 40% of the page on simple multitasking without a multitasking OS, at
http://wilsonminesco.com/multitask/index.html . There's additional explanation of how to form the structure macros, in the section of my 6502 stacks treatise on forming nestable structures, at
http://wilsonminesco.com/stacks/pgmstruc.html .
So again, you can become much more productive in assembly language, and get code that is more readable and maintainable, and less buggy, and in most cases there will be
absolutely zero penalty in performance or memory taken, unlike the situation with higher-level languages. It sure increased my own enjoyment of assembly,
a lot!