ys2-intro/shared/standard.inc
2025-11-13 19:07:39 +03:00

195 lines
4.8 KiB
PHP

.ifndef _STANDARD_INC_
_STANDARD_INC_ = 1
.include "cpu.inc"; SKIPWORD
;; binary arithmetics
.define IS_POWER_OF_2(value) (((value) & ((value) - 1)) = 0)
.define ALIGN(value, align) (((value) + (align) - 1) & ~((align) - 1))
.define TOP_BIT(value) (-1 + ((value) >= $1) + ((value) >= $2) + ((value) >= $4) + ((value) >= $8) + ((value) >= $10) + ((value) >= $20) + ((value) >= $40) + ((value) >= $80) + ((value) >= $0100) + ((value) >= $0200) + ((value) >= $0400) + ((value) >= $0800) + ((value) >= $1000) + ((value) >= $2000) + ((value) >= $4000) + ((value) >= $8000))
; rounds up to nearest power of 2
.define PAD(value) (1 + (((value) > $1) * $1) + (((value) > $2) * $2) + (((value) > $4) * $4) + (((value) > $8) * 8) + (((value) > $10) * $10) + (((value) > $20) * $20) + (((value) > $40) * $40) + (((value) > $80) * $80) + (((value) > $100) * $100) + (((value) > $200) * $200) + (((value) > $400) * $400) + (((value) > $800) * $800) + (((value) > $1000) * $1000) + (((value) > $2000) * $2000) + (((value) > $4000) * $4000) + (((value) > $8000) * $8000))
;; subroutine calling using the stack
; arguments are always pushed and popped with 16-bit sizes
.macro PUSH_ARG value
.ifnblank value
.if (.match (.left (1, {value}), #))
lda #<(.right (.tcount ({value}) - 1, {value}))
pha
lda #>(.right (.tcount ({value}) - 1, {value}))
pha
.else
lda value + 0
pha
lda value + 1
pha
.endif
.endif
.endmacro
.macro POP_ARG
pla
tay
pla
.endmacro
.macro CALL address, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8
.local postcall
lda #>(postcall - 1)
pha
lda #<(postcall - 1)
pha
tsx
PUSH_ARG arg8
PUSH_ARG arg7
PUSH_ARG arg6
PUSH_ARG arg5
PUSH_ARG arg4
PUSH_ARG arg3
PUSH_ARG arg2
PUSH_ARG arg1
txa
pha
jmp address
postcall:
.endmacro
.macro VARARGS_START
pla
tax
.endmacro
.macro VARARGS_END
txs
.endmacro
;; memory manipulation
.macro MEMSET address, size, value
.ifref __memset_impl
CALL __memset_impl, address, size, value
.else
.local __post_memset_impl
CALL __memset_impl, address, size, value; for .ifref detection
jmp __post_memset_impl
; this implementation is optimized for
; fast inner loops at reasonable overhead
; (only one write access per loop pass)
__memset_impl:
; not re-entrant
.scope
VARARGS_START
POP_ARG
sta memsetlo
sty memsethi
POP_ARG
sta memsetsizelo
sty memsetsizehi
POP_ARG
sta memsetvalue
VARARGS_END
; check if region to fill starts on a page boundary
memsetlo = * + $01
ldx #$00
beq memsetpage
; if not, fill <256 bytes at the beginning of the region
sec
lda #$00
sbc memsetlo; number of bytes until next page boundary
sec
memsetsizelo = * + $01
sbc #$00
tax
lda #$00
sbc memsetsizehi
bcc :++
; region is between two page boundaries
lda memsetlo
sta :+ + $01
lda memsethi
sta :+ + $02
: sta a:$00,x
dex
bne :-
rts; that's it
: ; region stretches beyond at least one page boundary
lda memsethi
sta :+ + $02
lda memsetvalue
ldx memsetlo
: sta a:$00,x
inx
bne :-
sec; update memsetsize
;ldx #$00
txa
sbc memsetlo
sta :+ + $01
lda memsetsizelo
: sbc #$00
sta memsetsizelo
bcs :+
dec memsetsizehi
:
; fill whole pages
memsetpage:
memsetsizehi = * + $01
ldy #$00
beq memsetend
lda memsethi
sta :+ + $02
memsetvalue = * + $01
lda #$00
;ldx #$00
: sta a:$00,x
inx
bne :-
inc :- + $02
dey
bne :-
lda :- + $02
SKIPWORD
memsetend: ; fill remaining <256 bytes
memsethi = * + $01
lda #$00
sta :+ + $03
lda memsetvalue
ldx memsetsizelo
inx
: dex
sta a:$00,x
bne :-
rts
.endscope
__post_memset_impl:
.endif; __memset_impl
.endmacro
.endif; !_STANDARD_INC_