220 lines
6.3 KiB
Plaintext
220 lines
6.3 KiB
Plaintext
.ifndef INCLUDE_UTILITY
|
|
INCLUDE_UTILITY = 1
|
|
|
|
;;********************************************************************************
|
|
;; @file
|
|
;; @brief Various useful macros
|
|
;; @ingroup libs
|
|
;;********************************************************************************
|
|
|
|
.macpack longbranch ; jeq, jge...
|
|
.macpack generic ; bge, add, sub
|
|
|
|
.feature string_escapes
|
|
.feature underline_in_numbers
|
|
|
|
.include "bit_macros.h65"
|
|
|
|
.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
|
|
lda #>dbyte
|
|
sta addr+1
|
|
.elseif .match(reg, X)
|
|
ldx #<dbyte
|
|
stx addr
|
|
ldx #>dbyte
|
|
stx addr+1
|
|
.elseif .match(reg, Y)
|
|
ldy #<dbyte
|
|
sty addr
|
|
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
|
|
.if .not .match(value, A)
|
|
lda value
|
|
.endif
|
|
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 using a @ref REVERSE_TABLE "table"
|
|
;; @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
|