.ifndef INCLUDE_UTILITY INCLUDE_UTILITY = 1 .macpack longbranch ; jeq, jge... .macpack generic ; bge, add, sub .feature string_escapes .feature underline_in_numbers .macro DEBUG_LED_OFF nr pha lda IO1 + IO::RA .if nr = 0 and #%11111110 .elseif nr = 1 and #%11111101 .else and #%11111011 .endif sta IO1 + IO::RA pla .endmacro .macro DEBUG_LED_ON nr pha lda IO1 + IO::RA .if nr = 0 ora #%00000001 .elseif nr = 1 ora #%00000010 .else ora #%00000100 .endif sta IO1 + IO::RA pla .endmacro ;;******************************************************************************** ;; @macro Store a double byte (immediate) at an address ;; @param addr Address, double byte will be loaded in LE to addr, addr+1 ;; @param dbyte Location or value ;; @param reg Register, defaults to A ;; @modifies ;;******************************************************************************** .macro StoreDByte dbyte,addr,reg ; .out .sprintf("%x, %x", dbyte, addr) .if .blank(reg) .or .match(reg, A) lda #dbyte sta addr+1 .elseif .match(reg, X) ldx #dbyte stx addr+1 .elseif .match(reg, Y) ldy #dbyte sty addr+1 .else .fatal "Invalid reg given to StoreDByte" .endif .endmacro ;;******************************************************************************** ;; @macro Update a byte in memory using a mask ;; @param addr Address of the byte to update ;; @param value New value ;; @param mask Mask of the bits to affect by the new value ;; @details ;; xor #value with addr -> only bits that need to flip are 1 ;; and result with #mask -> only selected bits that need to flip stay 1 ;; xor result with addr -> flips selected bits ;; @TODO optimize when immediate is used ;;******************************************************************************** .macro MaskedWrite addr,value,mask lda value eor addr and mask eor addr sta addr .endmacro ;;******************************************************************************** ;; @macro Jump to the subroutine and let the routine return at another location ;; @details ;; By using a indirect address `(addr)` and no ret_val, this macro behaves ;; like an indirect version of jsr. ;; @param addr Target of the jump (can be: `addr`, `(addr)` or `{(addr,x)}` ;; @param ret_addr Return address (optional) ;; @details ;;******************************************************************************** .macro JsrIndirect addr,ret_addr ; -1 because rts increments it .if .blank(ret_addr) lda #<(:+ - 1) pha lda #>(:+ - 1) pha .else lda #<(ret_addr -1) pha lda #>(ret_addr -1) pha .endif jmp addr .if .blank(ret_addr) : .endif .endmacro _n_genlabel .set 0 ;;******************************************************************************** ;; @macro Generate a unique label ;;******************************************************************************** .macro GenLabel name .ident(.sprintf("generated_label%04X", _n_genlabel)) _n_genlabel .set _n_genlabel + 1 .endmacro ;;******************************************************************************** ;; @macro Export labels with a prefix ;; @details ;; Equivalent to: ;; .export prefix_l1:=l1 ;; .export prefix_l2:=l2 ;; ... ;;******************************************************************************** .macro Export prefix,l1,l2,l3,l4,l5,l6,l7,l8 .if .blank(l1) .exitmacro .endif ; .out .sprintf("Exporting %s as %s_%s", .string(l1), .string(prefix), .string(l1)) .export .ident(.sprintf("%s_%s", .string(prefix), .string(l1))):=l1 Export prefix,l2,l3,l4,l5,l6,l7,l8 .endmacro .macro ExportZp prefix,l1,l2,l3,l4,l5,l6,l7,l8 .if .blank(l1) .exitmacro .endif ; .out .sprintf("Exporting (zp) %s as %s_%s", .string(l1), .string(prefix), .string(l1)) .exportzp .ident(.sprintf("%s_%s", .string(prefix), .string(l1))):=l1 ExportZp prefix,l2,l3,l4,l5,l6,l7,l8 .endmacro ;;******************************************************************************** ;; @macro Import labels and remove prefix ;; @details ;; Equivalent to: ;; .import prefix_l1 ;; .import prefix_l2 ;; ... ;; l1 = prefix_l1 ;; l2 = prefix_l2 ;; ... ;; Use in a scope to have the lX available as scope::lX ;;******************************************************************************** .macro Import prefix,l1,l2,l3,l4,l5,l6,l7,l8 .if .blank(l1) .exitmacro .endif ; .out .sprintf("Importing %s_%s as %s", .string(prefix), .string(l1), .string(l1)) .import .ident(.sprintf("%s_%s", .string(prefix), .string(l1))):absolute .ident(.sprintf("%s", .string(l1))) = .ident(.sprintf("%s_%s", .string(prefix), .string(l1))) Import prefix,l2,l3,l4,l5,l6,l7,l8 .endmacro .macro ImportZp prefix,l1,l2,l3,l4,l5,l6,l7,l8 .if .blank(l1) .exitmacro .endif ; .out .sprintf("Importing (zp) %s_%s as %s", .string(prefix), .string(l1), .string(l1)) .importzp .ident(.sprintf("%s_%s", .string(prefix), .string(l1))):zeropage .ident(.sprintf("%s", .string(l1))) = .ident(.sprintf("%s_%s", .string(prefix), .string(l1))) ImportZp prefix,l2,l3,l4,l5,l6,l7,l8 .endmacro .import REVERSE_TABLE ;;******************************************************************************** ;; @macro Reverse a byte ;; @param reg: The byte to reverse. May be in X, Y or A ;; @returns A: The reversed byte ;; @modifies A, X (only when reg = A) ;;******************************************************************************** .macro Reverse reg .if .match(reg, A) tax lda REVERSE_TABLE,x .elseif .match(reg, x) lda REVERSE_TABLE,x .elseif .match(reg, y) lda REVERSE_TABLE,y .else .fatal ("Reverse macro got invalid argument. This syntax error is intentional to show the line number") .endif .endmacro .endif ; guard