Thursday, September 6, 2012

CA65 Highlevel Macros


EDIT: I see this blog post may still turn up in search engines. Please use the code maintained here: https://gitlab.com/ca65/ca65hl
This macro code is much cleaner and faster than any of the old code releated to these posts. I always liked the idea of higher level assembly as long as it didn't get too carried away and there was no worry about getting too far away from the opcodes. I was trying to use NESHLA earlier but it just seems to be missing something, and CA65 has a lot more going for it. I decided to try to get some high-level functionality with ca65 macros, and I think I am pretty much done.

I had some rough code going before, but it was a bit sloppy and I think what I have now is pretty good and very near, if not entirely complete. There may be some bugs, but not many. So a summary of the features:

All the macro code is doing is taking a simple expression representing 6502 flags, and evaluating which branch to use, as well as generating some labels. Valid expressions are:
C set
C clear
Z set
Z clear
N set
N clear
V set
V clear

You can also always negate the flag check with a no or not in front of these expressions. You can also add an additional set or clear at the end. The set will do nothing, but the clear will negate the 'set'. This make more sense with .define macros:
 .define less              C clear 
 .define greaterORequal    C set   
 .define carry             C set     
 .define zero              Z set     
 .define equal             Z set     
 .define plus              N clear 
 .define positive          N clear 
 .define minus             N set    
 .define negative          N set   
 .define bit7              N set   
 .define overflow          V set     
 .define bit6              V set     

You can create any identifiers you like for different flags as long as they follow the format above. There is a special case for testing for greater or less or equal:

.define greater                G set
.define lessORequal            G clear

This is a fake flag that makes it easier to evaluate the condition.


IF-ELSE-ENDIF

IF-ENDIF blocks can be created and nested without any practical limitation:

lda $1234
cmp #$aa
if greater
 lda $4321
 sta $5678
 bit $2000
 if bit7 set
   if bit6 clear
    ldx #3
   else
    ldx #2
   endif
 endif
endif

You can add {} on the condition if you like: if {carry clear}

DO-WHILE ... REPEAT-UNTIL

You can start a block with do or repeat and end it with while <condition> or until <condition> . Repeat is identical to the functionality of do, it just reads better sometimes. Can also be nested as needed.

 do
  do
   lda (address),y
   sta PPU_DATA
   iny
  while not zero
  inc address+1
  dex
 while not zero   

WHILE-ENDWHILE ..

This is a while <condition> do. The do indicates this begins a block. Use an endwhile to close the block.

OTHER NOTES

There is some decent error checking and I have tested quite a bit and am about to try it in my code. Please let me know of any problems if you use this.

Updated: Changed the macro code to use one set of global variable 'constants'. Before this change, a new set of counters would be defined every time that the code was inside a new scope, which sounds good, but I don't see much advantage, so the macro has been changed to all global counters. I actually like it better.

Please see updated code here: http://mynesdev.blogspot.ca/2012/10/ca65-hl-macros-updated-again.html

2 comments:

  1. Added: Ability to call a macro with a .define, let me know if you are interested.


    .define padpressed(buttons) Z clear do_code padpressedMAC buttons

    .macro padpressedMAC buttons
    lda _pads_new_pressed
    and #(buttons)
    .endmacro


    ...

    if padpressed BUTTON_A|BUTTON_START
    ...
    endif

    ReplyDelete
    Replies
    1. Please see updated code here: http://mynesdev.blogspot.ca/2012/10/ca65-hl-macros-updated-again.html

      Delete