Quick summary of features:
Basic flag test syntax:
if (flag condition) ;code endif
Flag refers to 6502 CPU status flags, Z, C, N , V as well as an added flag I called G for greater or less or equal tests. the text inside the brackets above can be any of the flags plus 'set' or 'clear' (required). This is the simplest form of the macro. Example: C set would result in true if the carry flag was set after an instruction:
ror a if C set ; do code endifYou can add readability by creating or using the .define macros in the source file, so you could use 'zero' for example, and ca65 will substitute Z set.
Macro 'Function' Calling
The next feature I added was the ability for the expression evaluation to try and call a macro if it didn't recognize the flag to be tested. You can name any macro as a condition to be tested as long as the macro ends with 'set_flag_test foo', where foo is the flag to test:
.macro padpressed buttons lda _pads_new_pressed and #(buttons) set_flag_test Z clear .endmacro ;elsewhere: if padpressed BUTTON_A ; do stuff endifAND and OR Evaluation
Now there is also and and or support. There are some rules due to the way the macro code and 6502 work: - All ORs must be before ANDs in the same bracket level - There cannot be more than one test on the left side of an AND or OR in a bracket. For example:
If (zero and carry) or minus
The test on the left of the OR will not assemble. - You can place multiple test in brackets on the right of an AND or OR:
If minus or (zero and carry)
This is okay.
The negate symbol (! or not by default) currently will only work for one test, not a set of brackets. All tests are short circuited for the best possible code. An example from Super Mario Brothers:
if (comp OperMode = #VictoryModeValue) || ((comp a = #GameModeValue) && (comp OperMode_Task = #$03)) ; code block endif ;original lda OperMode ;are we in victory mode? cmp #VictoryModeValue ;if so, go ahead beq ChkPauseTimer cmp #GameModeValue ;are we in game mode? bne ExitPause ;if not, leave lda OperMode_Task ;are we running game engine? cmp #$03 bne ExitPause ;if not, leave ChkPauseTimer: ;code block ExitPause:The code in the if structure assembles to the exact same machine code as the block below it. Just for the sake of understanding what is required of the syntax, please note that this would work as well:
if comp OperMode = #VictoryModeValue || comp a = #GameModeValue && comp OperMode_Task = #$03The brackets do not matter in this case, but they can be useful in the case of having an and before an or:
if comp a = #GameModeValue && (comp OperMode_Task = #$03 || comp OperMode_Task = #$04)As well , if you needed to pass an and or or symbol to a macro you could do so with brackets. Brackets are only processed by the macro code if necessary. It will look inside a bracket pair if that is all there is (outside brackets over the entire expression) or if brackets are the next thing after an and or or. But if the brackets are part of an argument to a macro they will be left alone. Example:
if scomp #(<-5) < xcoordLong Jumps
There will come a time when a long branch is needed. This can be turned on with set_long_branch:
set_long_branch + ; turn on long branching set_long_branch - ; turn off long branching set_long_branch +,- ; turn on long branching turn off warning messagesAll branching code will use a jmp opcode when long branching is turned on. If the macro is able to branch without a long jump (sometimes not 100% accurate at this point) it will output an error message unless messages are turned off as shown above.
Other Features
Besides the standard IF-ENDIF shown above, there are a number of other supported syntaxes and features:
if-else-endif
Use a else with the if-endif block.
if - goto label
Use an if with a conditional expression ending with a goto label to jump to that label, where label is a valid label in your code, rather than creating an if-endif block.
do-while
A do while code block. Code between the do and the while will be executed until the expression after the while is false.
repeat - until
This is the same as the do-while block but the conditions are reversed - repeat until the expression is true.
Note: The keywords do and repeat are exactly the same.
while-do-endwhile
The while keyword starts this code block. To differentiate between this and do-while, the while must end in do.. example:
while not buttonpressed BUTTON_A do jsr read_pad endwhile
Inline code:
Sometimes it may be clearer to read code if it is included as part of the conditional code. For example:
do lda Timers,x if not zero dec Timers,x endif dex until negativeThis isn't too hard too read, but consider:
do lda Timers,x if not zero dec Timers,x endif until dex == negativeThis, to me, is even more readable. This also allows for some flexible coding solutions:
; orignal example: lda TimerControl beq DecTimers dec TimerControl bne NoDecTimers DecTimers: ;code block NoDecTimers:This is pretty difficult to change into an if structure, but:
if (lda TimerControl == zero ) || (dec TimerControl == zero) ;code block endif..does the exact same thing. The use of the == indicates the end of code and the start of what flag is going to be used to determine the branch. Assembly commands can be separated by + and you can mix in macros as well, or end the list of commands with a macro if it sets set_flag_test.
For now, a block of example code from SMB:
lda GamePauseStatus lsr if carry clear if (lda TimerControl == zero ) || (dec TimerControl == zero) ldx #$14 dec IntervalTimerControl if negative lda #$14 sta IntervalTimerControl ldx #$23 endif do lda Timers,x if not zero dec Timers,x endif until dex == negative endif inc FrameCounter endif
continued...
ca65 reads commas as macro parameter seperators. An example of where curly braces are needed:
do if {lda Timers,x == not zero} dec Timers,x endif until dex == negative ;original code: DecTimersLoop: lda Timers,x ;check current timer beq SkipExpTimer ;if current timer expired, branch dec Timers,x ;otherwise decrement the current timer SkipExpTimer: dex ;move onto next timer bpl DecTimersLoop
Code here: http://pastebin.com/azMMvh4r updated 1 time since this post was first created.
No comments:
Post a Comment