Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 9

"USAGIMOVES" Assembler Code Listing

-----------------------------------
07.07.2023 by sirhadisen

Notes:
- For a manual test run, load all modules, set $E4 to #$01, and start with G038F
(or with SYS 911, but from program mode the latter only works if RTS exits the
initializers instead of BRK!)
- To edit bank switching region (Basic interpreter is banked out to use $A500-), do
not forget POKE 2040, 128 to see the 32K RAM under the ROM.
- ! mark lines that use addresses, or their low or high bytes, which depend on the
structure and relative or absolute placement of routines or cache regions. This
includes all relative jumps and pieces of self-modifying code.
- <...> denotes names of jump, read and write addresses within a routine that
change frequently during code development

Reserved memory for work variables (zero page)


----------------------------------------------
$D3 IRQ-extension skipping counter
$D6-$D7 low-byte, high-byte for indirectly indexed addressing by the music routine
$D8 idle status
$D9-$DA low-byte, high-byte for indirectly indexed addressing of animation table
(pointer1)
$DB frame width
$DC frame height
$DD frame row offset
$DE-$DF low-byte, high-byte for indirectly indexed addressing of frame data
(pointer2)
$E0 frame column offset
$E1 slowing counter
$E2 background info last frame row offset [vs. background info low bytes, high
bytes of TED addresses of affected cells]
$E3 background info last frame column offset
$E4 switch which value #$00 indicates that the RAM-access stub is to be
executed only once and not as part of a persistent IRQ-extension
$E5 cache for pressed button
$E6 direction of view
$E7 process flag: 1 = restore background, 0 = memorize background and draw new
frame

Reserved memory for caching


---------------------------
$A680-$A77F region for memorizing the background character bytes behind a frame
$A780-$A87F region for memorizing the background attribute bytes behind a frame
$????-$???? 1K buffer for video matrix used to prevent flickering of sprites due
to the lag between restoration of background and drawing of new frame

Memory regions of routines


--------------------------
$0333-$0355 General bank-switching-independent initialization and toggle of the
IRQ RAM vector between $CE0E and $0352
$0357-$038B Universal RAM-access stub, which IRQ detour address in the high-RAM
region (JSR $#### statement) must have been set in advance
$038F-$03BA Usagi moves-specific initialization (music, parameter, IRQ-detour
address & -toggle)

$A500-$A57D Usagi moves: IRQ animation player main


$A590-$A61C Drawing routine (can only handle rectangular frames with an even
number of up to 254 cells!)
$A630-$A64B Copy TED base address to other STA and LDA statements
$A660-$A671 Background restoring routine
$A880-$A89F Update next frame info
$A8A0-$A8C8 Move column offset for left/right usagi moves
$A9BF-$B??? Music player

*** 1) Routines in the tape buffer (erased during soft reset) ***

--- $0333-$0355 General bank-switching-independent initialization and toggle of


the IRQ RAM vector between $CE0E and $0352
.0333 lda #$0e
.0335 eor #$57
.0337 eor $0314
.033a sta $0314
.033d lda #$ce
.033f eor #$03
.0341 eor $0315
.0344 sta $0315
-- reset IRQ-extension skipping counter
.0347 lda #$04
.0349 sta $d3
.034b rts

--- $0357-$038B Universal RAM-access stub, which IRQ detour address in the high-
RAM region (JSR $#### statement) must have been set in advance
-- skip IRQ-extension unless skipping counter has reached 0, in which case it is
reset to #$04
.0357 dec $d3
!.0359 bne $<ende>
.035b lda #$04
.035d sta $d3
-- detect key strokes with SCNKEY GETIN and cache in $E5
(https://github.com/cc65/cc65/issues/324)
.035f php
.0360 jsr $ff9f
.0363 jsr $ffe4
?? idea: compare with $E5 in order to change key stroke recognition behavior
.0366 cmp #$00
?? idea: use POKE to change !!byte 6C to 6A in order to change key stroke
recognition behavior
!.0368 bne $036c
.036a sta $e5
.036c plp
-- raster line synchronization for music
- line #$10 seems to be a good balance for musik speed; should probably be derived
from music idler with LSR
.036d lda #$10
.036f cmp $ff1d
!.0372 bne $036f
.0374 - .037a nop
-- bank-Switching ROM->RAM
.037a sta $ff3f
-- jump address is set in the initialization routine, so modify it there
.037e jsr $0000
-- bank-Switching RAM->ROM
.0381 sta $ff3e
-- if $E4 = #$00, do an RTS, as the stub only serves a single execution, otherwise
resume with system-IRQ tasks at $CE0E
.0384 lda $e4
!.0386 bne $<ende>
.0388 rts
<ende>.0389 jmp $ce0e

--- $038F-$03BA Usagi moves-specific initialization (music, parameter, IRQ-detour


address & -toggle)
.038f sei
-- general initialization and toggle execution of the universal RAM-accmmess stub
!.0390 jsr $0333
-- set idle status and pointer1 to start of idle animation (get start address in
animation table from $95a8-$95a9 !!)
.0393 lda #$01
.0395 sta $d8
!.0397 lda $95a8
.039a sta $d9
!.039c lda $95a9
.039f sta $da
-- bank-Switching ROM->RAM
.03a1 sta $ff3f
-- music initialization
?? To be clarified with Hermit: Would another LDA #$00 here avoid issues with saves
of code after an initialization has already taken place (and apparently marked
somehow due to self-modification of the code)?
-insert jsr $a96a instead of 3 nop once the music routines exists
.03a4 nop
.03a5 nop
.03a6 nop
-- bank-Switching RAM->ROM
.03a7 sta $ff3e
-- set the detour address in the RAM-access stub to $A500
!.03aa lda #$00
.03ac sta $037f
!.03af lda #$a5
.03b1 sta $0380
.03b4 cli
-- initialize first frame by resetting cache of background restoring routine
-- (set width and height to 1 and provide one row as the image)
?? it would be nice to make it possible to restore 0 rows!
.03b5 lda #$01
.03b7 sta $db
.03b9 sta $dc
.03bb sta $dd
!.03bd lda #$2f
.03bf sta $de
-- initialize pointer 1 to TED starting address $0CE0 + 2 rows - 1
-- (start 1 earlier for Y register loop)
!.03c1 lda #$0d
.03c3 sta $df
.03c5 ldy #$08
.03c7 lda ($de),y
!.03c9 sta $a680,y
.03cc dey
!.03cd bne $03c7
.03cf rts
*** 2) Routines in the bank-switching-RAM ***

--- $A500-$A57D Usagi moves: IRQ animation player main


-- play one music frame each call
-insert .a500 jsr $a9bf instead of 3 nop for music once it exists
.a500 nop
.a501 nop
.a502 nop
-- wait for running animations to finish, so skip the following if idle status is 0
.a503 lda $d8
!.a505 beq $a546
-- check whether a defined key has been cached in $E5
-- Note: Less efficient than writing one's own key scanner. Also, KERNAL routines
cannot be readily called from here because the ROM was banked out to use this
memory region, and because we are inside an IRQ which seems to conflict with
calling KERNAL routines (or would it work with PHP?)
.a507 ldx #$02
.a509 lda $e5
.a50b cmp #$11
!.a50d beq $a538
.a50f inx
.a510 inx
.a511 cmp #$9d
!.a513 beq $a536
.a515 inx
.a516 inx
.a517 cmp #$91
!.a519 beq $a538
.a51b inx
.a51c inx
.a51d cmp #$1d
!.a51f beq $a536
-- if the use button is pressed, add 2 or 4 to X depending on sign of value in E6
(= last left/right code)
.a521 inx
.a522 inx
.a523 cmp #$20
!.a525 beq $a52a
-- if no defined key was pressed, do not change pointer1
.a527 jmp $a546
-- distinguish use animation by last view direction left (#$9D, left shift sets
carry flag) or right (#$1D, left shift clears carry flag)
.a52a lda $e6
.a52c asl
!.a52d bcc $a538
.a52f inx
.a530 inx
!.a531 bcs $a538
!.a533 jmp $a546
-- memorize view direction by copying current $E5 to $E6
.a536 sta $e6
-- map x-register to pointer1 address and store this address in $D9-$DA (using list
of start addresses of sequences in the animation table that starts at $95a8 !! with
animation order idle, down, left, up, right, use left, use right)
!.a538 lda $95a8,x
.a53b sta $d9
!.a53d lda $95a9,x
.a540 sta $da
-- clear idle status
.a542 lda #$00
.a544 sta $d8
-- clear cache for pressed key (otherwise animation would loop indefinitely)
.a546 lda #$00
.a548 sta $e5
-- apply additional skipping counter for the animation routines
-- Note: Should be adjusted with the others to keep a regular reoccurrence of
keyboard scans that supports a fluent animation
?? consider clearing the keyboard buffer at this point
.a54a dec $e1
!.a54c bne $a585
.a54e lda #$03
.a550 sta $e1
.a552 - .a562 nop
-- call background restoring routine, call update next frame metadata, call drawing
routine, call move column offset routine
!.a563 jsr $a660
!.a566 jsr $a880
!.a569 jsr $a8a0
!.a56c jsr $a590
.a56f - .a577 nop
-- promote pointer1 to next frame (2-byte addition routine with increment #$05)
.a578 clc
.a579 lda $d9
.a57b adc #$05
.a57d sta $d9
.a57f lda $da
.a581 adc #$00
.a583 sta $da
-- if width of next frame is 0 (= after finishing an animation), set idle flag to
enable keyboard scanning at the beginning of the next step for neatless
continuation of the motion (max. #$FF times before overflow of $D8)
.a585 ldy #$00
.a587 lda ($d9),y
!.a589 bne $a58f
?? welche Vor- oder Nachteile hat inc gegenueber einem sauberen lda #$01 sta $d8?
.a58b lda #$01
.a58d sta $d8
.a58f rts

--- $A590-$A61C Drawing routine (can only handle rectangular frames with an even
number of up to 254 cells!)
-- set target TED base address (lb hb) to upper left corner according to offsets of
row and column $DD and $E0
-- 1. initialize base address at $a5??-$a5??+1 for indirect addressing according to
column offset (self-modifying code!!)
-- self-modifying code!!
!.a590 lda #$0c
!.a592 sta $<4++>
!.a595 lda $e0
!.a597 sta $<4+>
-- 2. 2-byte-add (row offset + height - 1)-times #$28 to start in last row
-- Note: I neglect the -1 for simplicity, meaning the minimal offset is 1, which
has the advantage that usagyuuun cannot hurt its ears
.a59a clc
.a59b lda $dc
.a59d adc $dd
.a59f tax
<1>.a5a0 clc
!.a5a1 lda $<4+>
.a5a4 adc #$28
-- self-modifying code!!
!.a5a6 sta $<4+>
!.a5a9 lda $<4++>
.a5ac adc #$00
-- self-modifying code!!
!.a5ae sta $<4++>
.a5b1 dex
!.a5b2 bne $<1>
-- copy base address to other lda and sta statements
!.a5b4 jsr $a630
-- 3. loop over frame data at ($DE),Y and copy to TED memory address row-wise
backwards: outer loop over rows of the frame, inner loop over columns of the frame
-- initialize Y as character counter with width*height = $DB*$DC, which must be <
#$FF
.a5b7 ldy $dc
.a5b9 lda #$00
<2>.a5bb clc
.a5bc adc $db
.a5be dey
!.a5bf bne $<2>
.a5c1 tay
-- initialize column offset X with frame width
<10>.a5c2 ldx $db
-- skip memorization of background if the routine was called to restore the
background ($E7 = #$01)
<12>.a5c4 lda $e7
!.a5c6 bne $<11>
-- memorize background (from TED memory at self-modified indirect address) in
restoration cache
<7>.a5c8 lda $0000,x
!.a5cb sta $a680,y
<8>.a5ce lda $0000,x
!.a5d1 sta $a780,y
-- if the routine was not called to restore the background, do character checks but
do not restore attribute bytes
-- otherwise, do it the other way around
-- retrieve frame data
.a5d4 lda ($de),y
-- skip transparent cells (#$20) and replace untransparent white (#$2E) with #$20
.a5d6 cmp #$20
!.a5d8 beq $<6>
.a5da cmp #$2e
!.a5dc bne $<4>
.a5de lda #$20
!.a5e0 jmp $<4>
-- restore attribute byte in TED memory from cache at !! $a780
!<11>.a5e3 lda $a780,y
<5>.a5e6 sta $0000,x
-- retrieve frame data
.a5e9 lda ($de),y
-- write character to the TED memory at self-modified indirect address
<4>.a5eb sta $0000,x
<6>.a5ee dey
!.a5ef beq $<9>
.a5f1 dex
!.a5f2 bne $<12>
-- subtract #$28 from the TED base to adjust it to the left boarder of the previous
row (= "new line backwards")
-- self-modifying code!!
.a5f4 sec
!.a5f5 lda $<4+>
.a5f8 sbc #$28
!.a5fa sta $<4+>
!.a5fd sta $<5+>
!.a600 sta $<7+>
!.a603 sta $<8+>
!.a606 lda $<4++>
.a609 sbc #$00
!.a60b sta $<4++>
!.a60e sta $<7++>
.a611 sbc #$04
!.a613 sta $<5++>
!.a616 sta $<8++>
!.a619 jmp $<10>
<9>.a61c rts

--- $A630-$A64B Copy TED base address to other STA and LDA statements
-- self-modifying code!!
!.a630 lda $<4+>
!.a633 sta $<5+>
!.a636 sta $<7+>
-- lb is identical for character and attribute addresses
!.a639 sta $<8+>
!.a63c lda $<4++>
!.a63f sta $<7++>
-- hb of attribute address = character address minus #$04
.a642 sec
!.a643 sbc #$04
!.a645 sta $<5++>
!.a648 sta $<8++>
.a64b rts

--- $A660-$A671 Background restoring routine


-- set pointer2 to starting address of memorized chars
!.a660 lda #$80
.a662 sta $de
!.a664 lda #$a6
.a666 sta $df
-- call drawing routine with process flag set to restore background without
memorizing current frame and without skipping transparent cells
.a668 lda #$01
.a66a sta $e7
!.a66c jsr $a590
.a66f dec $e7
.a671 rts

--- $A680-$A77F region for memorizing the background character bytes

--- $A780-$A87F region for memorizing the background attribute bytes


--- $A880-$A89F Update next frame info
-- get width of next frame; width 0 implies that no key was pressed and the final
frame has been drawn, so default to idle animation that loops indefinitely
.a880 ldy #$00
.a882 lda ($d9),y
!.a884 bne $<update>
-- set idle status and pointer1 to idle animation (get start address in animation
table that starts at $95a8 !!)
.a886 lda #$01
.a888 sta $d8
!.a88a lda $95a8
.a88d sta $d9
!.a88f lda $95a9
.a892 sta $da
-- copy width, height, row offset, pointer2 at $DB, $DB, $DD, $DE-$DF from
animation table to corresponding work variables (which are arranged in the same
order)
<update>.a894 ldy #$04
.a896 lda ($d9),y
.a898 sta $00db,y
.a89b dey
!.a89c bpl $a896
.a89e rts

--- $A8A0-$A8C8 Move column offset for left/right usagi moves


-- Left move decrements column offset and right move increments it based on idle
status $D8,( current key stroke $E5,) and last direction of view $E6
-- skip routine while idling
.a8a0 lda $d8
!.a8a2 bne $<ende>
-- skip routine while jumping

??Clarify: Why is the move still executed when pressing up? Should it be skipped if
$e5 is 0, or everything that is not left or right?

.a8a4 lda $e5


.a8a6 cmp #$91
!.aaa8 beq $<ende>
-- move in last direction (for left (#$9D), left shift sets the carry flag)
.a8aa dec $e0
.a8ac lda $e6
.a8ae asl
!.a8af bcs $<chkoffset>
.a8b1 inc $e0
.a8b3 inc $e0
-- reset offset $E0 when it drops below #$00 / exceeds #$1C
-- (as usagi reaches left or right edge of screen)
<chkoffset>.a8b5 lda $e0
.a8b7 cmp #$28
!.a8b9 bcs $<setul>
.a8bb cmp #$1d
!.a8bd bcc $<ende>
-- #$1d < A < #$28 means entering from the left, so set offset to lower limit
.a8bf lda #$00
.a8c1 sta $e0
.a8c3 rts
-- #$28 < A means entering from the right, so set offset to upper limit
.a8c4 <setul>.a8c4 lda #$1c
.a8c6 sta $e0
<ende>.a8c8 rts

You might also like