6502-OS/utility.h65

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