===== Flexible 32 Sprite Multiplexer - Version 2 =====
The original code is from [[Flexible 32 Sprite Multiplexer]]
This code has been converted to ACME.
Compared to the previous version this has had various small tweaks, a bug fix (the interrupt was not always saving X for the RTI in all execution paths) and optimisations mostly shown by the "MPi:" comments.
There are two source files in two sections below. Assemble RasterTest.asm and you can easily load the resulting prg in an emulator and it will start. The standard library file (stdlib.asm) is also included in the section below which has lots of handy definitions for zero page, kernal, VIC and SID.
=== Create as "RasterTest.asm" ===
; Change list
; Original code from http://codebase64.org/doku.php?id=base:flexible_32_sprite_multiplexer
; 25th October 2007 - Martin Piper
; Conversion to ACME plus various tweaks, bug fix (the interrupt was not always saving X for the RTI in all execution paths) and optimisations mostly shown by the "MPi:" comments.
; 26th October 2007 - Martin Piper
; Fixed a slight bug where if one particular sprite was the very last one to be drawn it wouldn't end the IRQ chain correctly.
; Added a test for sprite Y pos = $ff and then it then finishes rendering all further sprites. This is a quick way to disable a sprite from being rendered.
; Added some extra documentation comments.
; TODO
; Tidy this so the multiplexor is in a separate file and make a bit modular.
!source "stdlib.asm"
!to "RasterTest.prg", cbm
!sl "RasterTest.map"
!cpu 6502
!ct pet
; This starts at $0801 so that doing a LOAD"*",8 will still work with the default $0801 BASIC start address.
*= BASICSTART
!byte $0c,$08,$0a,$00,$9e ; Line 10 SYS
!convtab pet
!tx "2304" ; Address for sys start in text
!byte $00,$00,$00,$00
!byte $00,$00,$00,$00 ; And a few more zeros for the sake of paranoia and safety.
!macro SpriteLine .v {
!by .v>>16, (.v>>8)&255, .v&255
}
; Some sprite data high up in memory
*=$3f00
!by 255,255,255,255,255,255,255,255
!by 255,255,255,255,255,255,255,255
!by 255,255,255,255,255,255,255,255
!by 255,255,255,255,255,255,255,255
!by 255,255,255,255,255,255,255,255
!by 255,255,255,255,255,255,255,255
!by 255,255,255,255,255,255,255,255
!by 255,255,255,255,255,255,255,255
+SpriteLine %........................
+SpriteLine %.#......................
+SpriteLine %.##.....................
+SpriteLine %.###....................
+SpriteLine %.####...................
+SpriteLine %.#####..................
+SpriteLine %.######.................
+SpriteLine %.#######................
+SpriteLine %.########...............
+SpriteLine %.#########..............
+SpriteLine %.########...............
+SpriteLine %.######.................
+SpriteLine %.######.................
+SpriteLine %.##..##.................
+SpriteLine %.#....##................
+SpriteLine %......##................
+SpriteLine %.......##...............
+SpriteLine %.......##...............
+SpriteLine %........##..............
+SpriteLine %........##..............
+SpriteLine %........................
!byte 0
+SpriteLine %########################
+SpriteLine %########################
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %###.......###........###
+SpriteLine %###......#####.......###
+SpriteLine %###......#####.......###
+SpriteLine %###......#####.......###
+SpriteLine %###.......###........###
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %###..................###
+SpriteLine %########################
+SpriteLine %########################
!byte 0
+SpriteLine %########################
+SpriteLine %########################
+SpriteLine %####.......##.......####
+SpriteLine %###.#......##......#.###
+SpriteLine %###..#.....##.....#..###
+SpriteLine %###...#....##....#...###
+SpriteLine %###....#...##...#....###
+SpriteLine %###.....#..##..#.....###
+SpriteLine %###......######......###
+SpriteLine %###......#####.......###
+SpriteLine %########################
+SpriteLine %###......#####.......###
+SpriteLine %###......#####.......###
+SpriteLine %###.....#..#..#......###
+SpriteLine %###....#...#...#.....###
+SpriteLine %###...#....#....#....###
+SpriteLine %###..#.....#.....#...###
+SpriteLine %###.#......#......#..###
+SpriteLine %####.......#.......#.###
+SpriteLine %########################
+SpriteLine %########################
!byte 0
*=$0900
; MPi: Uncomment this line to enable border colour debug display.
; The sprite display IRQs will show different colours depending on how many sprites they have updated in the current band.
; This is useful for showing how many sprites are updated on average per band.
;Multiplexor_DebugBorder
; Define various zeropage working variables
.VarBase = $02
Multiplex_areg = .VarBase+$000
Multiplex_xreg = .VarBase+$001
Multiplex_yreg = .VarBase+$002
Multiplex_abuf = .VarBase+$003
Multiplex_xbuf = .VarBase+$004
Multiplex_ybuf = .VarBase+$005
Multiplex_iobuf = .VarBase+$006
Multiplex_flag = .VarBase+$007
Multiplex_buffer = .VarBase+$008
Multiplex_MaxSpr = .VarBase+$009
Multiplex_counter = .VarBase+$00a
Multiplex_counterx1 = .VarBase+$00b
Multiplex_counterx2 = .VarBase+$00c
Multiplex_countery1 = .VarBase+$00d
Multiplex_countery2 = .VarBase+$00e
Multiplex_xdif = .VarBase+$00f
Multiplex_ydif = .VarBase+$010
Multiplex_xspeed = .VarBase+$011
Multiplex_yspeed = .VarBase+$012
Multiplex_xoffset = .VarBase+$13
Multiplex_yoffset = .VarBase+$14
jumplo = .VarBase+$15
jumphi = .VarBase+$16
Multiplex_bal = .VarBase+$17
Multiplex_bah = .VarBase+$18
Multiplex_oldlo = .VarBase+$19
Multiplex_oldhi = .VarBase+$1a
; Memory
Multiplex_indextable = $e0 ; $20 long
Multiplex_spritepointer = SPRITEFRAME
; Must be <= 32 otherwise Multiplex_indextable goes splat
Multiplex_items = 32
;--------------------------------------
;macros
;--------------------------------------
!zn {
Start
sei
cld
lda #$35 ; RAM visible at $A000-$BFFF and $E000-$FFFF I/O area visible at $D000-$DFFF.
sta ZPProcessorPort
ldx #$ff
txs
inx
stx VIC2ScreenColour
stx CIA1TimerAControl
stx VIC2BorderColour
inx
stx VIC2InteruptControl
lda #$1b
sta VIC2ScreenControlV
lda #$00
sta VIC2SpriteDoubleHeight
sta VIC2SpritePriority
sta VIC2SpriteDoubleWidth
sta VIC2SpriteMulticolour
lda #Multiplex_maininter
sta KERNALIRQServiceRoutineHi
lda #$7f
sta CIA1InterruptControl
lda #0
sta VIC2Raster
ldx #$02
lda #$80
.3 sta $00,x
inx
bne .3
lda #32 ; MPi: Increase to 32 sprites from the original 24 sprite demo
sta Multiplex_MaxSpr
lda #$40
sta Multiplex_xoffset
lda #$00
sta Multiplex_yoffset
lda #$ff
sta Multiplex_xspeed
lda #$01
sta Multiplex_yspeed
lda #$0a
sta Multiplex_xdif
lda #$10
sta Multiplex_ydif
jsr Multiplex_initsort
; MPi: Just to prove all IRQs save all registers. These characters should never flicker or change from ABC in the top left of the screen.
lda #1
ldx #2
ldy #3
.2 cli
sta SCREENRAM
stx SCREENRAM+1
sty SCREENRAM+2
; MPi: Inc'ing these three store variables should not alter the "ABC" printed by the bit above.
; In the previous version this code block would show how reg X was not being preserved by the IRQ because the middle character ("B") would update.
; This is because as the IRQ exits it would sometimes do an extra "ldx Multiplex_xreg" without always doing the corresponding "stx Multiplex_xreg" on entry.
inc Multiplex_areg
inc Multiplex_xreg
inc Multiplex_yreg
jmp .2
}
;--------------------------------------
!zn {
; The main top interrupt that draws the first line of sprites and then figures out what next to plot
Multiplex_maininter
sta Multiplex_areg
stx Multiplex_xreg
sty Multiplex_yreg
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
ldx Multiplex_MaxSpr
cpx #$09
bcs .morethan8
lda #$4c ; Set jmp $0000
sta .switch
lda .activatetab,x
sta VIC2SpriteEnable
lda .jumplo,x
sta jumplo
lda .jumphi,x
sta jumphi
lda #$00
sta VIC2SpriteXMSB
jmp (jumplo)
.morethan8 lda #$ff
sta VIC2SpriteEnable
lda #$08
sta Multiplex_counter
lda #$2c ; Set bit $0000
sta .switch
lda #$00
;--------------------------------------
.dospr7 ldy Multiplex_indextable+7
ldx Multiplex_YTable,y
stx VIC2Sprite7Y
ldx Multiplex_XPosLo,y
stx VIC2Sprite7X
ldx Multiplex_SpriteFrame,y
stx Multiplex_spritepointer+7
ldx Multiplex_Colour,y
stx VIC2Sprite7Colour
ldx Multiplex_XPosHi,y
beq .dospr6
lda #$80
;--------------------------------------
.dospr6 ldy Multiplex_indextable+6
ldx Multiplex_YTable,y
stx VIC2Sprite6Y
ldx Multiplex_XPosLo,y
stx VIC2Sprite6X
ldx Multiplex_SpriteFrame,y
stx Multiplex_spritepointer+6
ldx Multiplex_Colour,y
stx VIC2Sprite6Colour
ldx Multiplex_XPosHi,y
beq .dospr5
ora #$40
;--------------------------------------
.dospr5 ldy Multiplex_indextable+5
ldx Multiplex_YTable,y
stx VIC2Sprite5Y
ldx Multiplex_XPosLo,y
stx VIC2Sprite5X
ldx Multiplex_SpriteFrame,y
stx Multiplex_spritepointer+5
ldx Multiplex_Colour,y
stx VIC2Sprite5Colour
ldx Multiplex_XPosHi,y
beq .dospr4
ora #$20
;--------------------------------------
.dospr4 ldy Multiplex_indextable+4
ldx Multiplex_YTable,y
stx VIC2Sprite4Y
ldx Multiplex_XPosLo,y
stx VIC2Sprite4X
ldx Multiplex_SpriteFrame,y
stx Multiplex_spritepointer+4
ldx Multiplex_Colour,y
stx VIC2Sprite4Colour
ldx Multiplex_XPosHi,y
beq .dospr3
ora #$10
;--------------------------------------
.dospr3 ldy Multiplex_indextable+3
ldx Multiplex_YTable,y
stx VIC2Sprite3Y
ldx Multiplex_XPosLo,y
stx VIC2Sprite3X
ldx Multiplex_SpriteFrame,y
stx Multiplex_spritepointer+3
ldx Multiplex_Colour,y
stx VIC2Sprite3Colour
ldx Multiplex_XPosHi,y
beq .dospr2
ora #$08
;--------------------------------------
.dospr2 ldy Multiplex_indextable+2
ldx Multiplex_YTable,y
stx VIC2Sprite2Y
ldx Multiplex_XPosLo,y
stx VIC2Sprite2X
ldx Multiplex_SpriteFrame,y
stx Multiplex_spritepointer+2
ldx Multiplex_Colour,y
stx VIC2Sprite2Colour
ldx Multiplex_XPosHi,y
beq .dospr1
ora #$04
;--------------------------------------
.dospr1 ldy Multiplex_indextable+1
ldx Multiplex_YTable,y
stx VIC2Sprite1Y
ldx Multiplex_XPosLo,y
stx VIC2Sprite1X
ldx Multiplex_SpriteFrame,y
stx Multiplex_spritepointer+1
ldx Multiplex_Colour,y
stx VIC2Sprite1Colour
ldx Multiplex_XPosHi,y
beq .dospr0
ora #$02
;--------------------------------------
.dospr0 ldy Multiplex_indextable
ldx Multiplex_YTable,y
stx VIC2Sprite0Y
ldx Multiplex_XPosLo,y
stx VIC2Sprite0X
ldx Multiplex_SpriteFrame,y
stx Multiplex_spritepointer
ldx Multiplex_Colour,y
stx VIC2Sprite0Colour
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
ldx Multiplex_XPosHi,y
beq .over
ora #$01
.over sta VIC2SpriteXMSB
.switch jmp Multiplex_exitinter ; Self modifying for jmp or bit
clc
; MPi: During heavy use (>24 sprites) on average the interrupt updates at least two new sprites and quite often three or four sprites. (Enable Multiplexor_DebugBorder to see this.)
; Armed with this information there is an average time saving by having reg x maintain Multiplex_counter and being able to do
; "ldy Multiplex_indextable,x" instead of "lda Multiplex_indextable,y : tay" even taking into account the extra interrupt x register store and restore.
; This is because the "ldx Multiplex_counter : inx : stx Multiplex_counter" doesn't always need to be done every sprite and can be optimised to be just "inx".
; However Under light use (<16 sprites) the average interrupt updates one sprites but the extra overhead for the extra interrupt x store and restore is small compared to the savings mentioned above.
; Basically the theory being optimise for heavy use since heavy use is where the optimisation is more appreciated.
ldx Multiplex_counter
; MPi: From here until the Multiplex_exitinter the sprite plotting code has been reworked to use an extra register (x) and include the optimisations described above.
;--------------------------------------
; MPi: Calculate with this current raster position and the bottom of the last sprite Y pos
; Is it better to start a new raster IRQ at the new position or shall we update the sprite now?
.nextspr0 lda VIC2Sprite0Y
adc #$17
sbc VIC2Raster
bcc .blit0 ; MPi: Process the sprite now not later
cmp #$03
bcs .next0
lda #$03
.next0 clc ; MPi: Process the sprite later next raster IRQ
adc VIC2Raster
sta VIC2Raster
lda #Multiplex_inter0
sta KERNALIRQServiceRoutineHi
inc VIC2InteruptStatus
lda CIA1InterruptControl
!ifdef Multiplexor_DebugBorder {
lda #3 : sta VIC2BorderColour
}
stx Multiplex_counter
lda Multiplex_areg
ldx Multiplex_xreg
ldy Multiplex_yreg
rti
; MPi: Each Multiplex_interX is entered by each subsequent raster IRQ
Multiplex_inter0 sta Multiplex_areg
stx Multiplex_xreg
sty Multiplex_yreg
ldx Multiplex_counter
; MPi: Each .blitX can also entered by a raster IRQ processing more than one sprite in this band if it is calculated it is better to follow on rather than create a new raster IRQ.
.blit0 ldy Multiplex_indextable,x
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
lda Multiplex_YTable,y
cmp #$ff ; Don't display any sprites once this is reached
beq .intExitInter0
sta VIC2Sprite0Y
lda Multiplex_XPosLo,y
sta VIC2Sprite0X
lda Multiplex_SpriteFrame,y
sta Multiplex_spritepointer
lda Multiplex_Colour,y
sta VIC2Sprite0Colour
lda Multiplex_XPosHi,y
beq .no0
lda #$01
ora VIC2SpriteXMSB
bne .yes0
.no0 lda #$fe
and VIC2SpriteXMSB
.yes0 sta VIC2SpriteXMSB
inx
cpx Multiplex_MaxSpr
bne .nextspr1
.intExitInter0 jmp Multiplex_exitinter
;--------------------------------------
.nextspr1 lda VIC2Sprite1Y
adc #$17
sbc VIC2Raster
bcc .blit1
cmp #$03
bcs .next1
lda #$03
.next1 clc
adc VIC2Raster
sta VIC2Raster
lda #Multiplex_inter1
sta KERNALIRQServiceRoutineHi
inc VIC2InteruptStatus
lda CIA1InterruptControl
!ifdef Multiplexor_DebugBorder {
lda #3 : sta VIC2BorderColour
}
stx Multiplex_counter
lda Multiplex_areg
ldx Multiplex_xreg
ldy Multiplex_yreg
rti
Multiplex_inter1 sta Multiplex_areg
stx Multiplex_xreg
sty Multiplex_yreg
ldx Multiplex_counter
.blit1 ldy Multiplex_indextable,x
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
lda Multiplex_YTable,y
cmp #$ff ; Don't display any sprites once this is reached
beq .intExitInter1
sta VIC2Sprite1Y
lda Multiplex_XPosLo,y
sta VIC2Sprite1X
lda Multiplex_SpriteFrame,y
sta Multiplex_spritepointer+1
lda Multiplex_Colour,y
sta VIC2Sprite1Colour
lda Multiplex_XPosHi,y
beq .no1
lda #$02
ora VIC2SpriteXMSB
bne .yes1
.no1 lda #$fd
and VIC2SpriteXMSB
.yes1 sta VIC2SpriteXMSB
inx
cpx Multiplex_MaxSpr
bne .nextspr2
.intExitInter1 jmp Multiplex_exitinter
;--------------------------------------
.nextspr2 lda VIC2Sprite2Y
adc #$17
sbc VIC2Raster
bcc .blit2
cmp #$03
bcs .next2
lda #$03
.next2 clc
adc VIC2Raster
sta VIC2Raster
lda #Multiplex_inter2
sta KERNALIRQServiceRoutineHi
inc VIC2InteruptStatus
lda CIA1InterruptControl
!ifdef Multiplexor_DebugBorder {
lda #3 : sta VIC2BorderColour
}
stx Multiplex_counter
lda Multiplex_areg
ldx Multiplex_xreg
ldy Multiplex_yreg
rti
Multiplex_inter2 sta Multiplex_areg
stx Multiplex_xreg
sty Multiplex_yreg
ldx Multiplex_counter
.blit2 ldy Multiplex_indextable,x
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
lda Multiplex_YTable,y
cmp #$ff ; Don't display any sprites once this is reached
beq .intExitInter2
sta VIC2Sprite2Y
lda Multiplex_XPosLo,y
sta VIC2Sprite2X
lda Multiplex_SpriteFrame,y
sta Multiplex_spritepointer+2
lda Multiplex_Colour,y
sta VIC2Sprite2Colour
lda Multiplex_XPosHi,y
beq .no2
lda #$04
ora VIC2SpriteXMSB
bne .yes2
.no2 lda #$fb
and VIC2SpriteXMSB
.yes2 sta VIC2SpriteXMSB
inx
cpx Multiplex_MaxSpr
bne .nextspr3
.intExitInter2 jmp Multiplex_exitinter
;--------------------------------------
.nextspr3 lda VIC2Sprite3Y
adc #$17
sbc VIC2Raster
bcc .blit3
cmp #$03
bcs .next3
lda #$03
.next3 clc
adc VIC2Raster
sta VIC2Raster
lda #Multiplex_inter3
sta KERNALIRQServiceRoutineHi
inc VIC2InteruptStatus
lda CIA1InterruptControl
!ifdef Multiplexor_DebugBorder {
lda #3 : sta VIC2BorderColour
}
stx Multiplex_counter
lda Multiplex_areg
ldx Multiplex_xreg
ldy Multiplex_yreg
rti
Multiplex_inter3 sta Multiplex_areg
stx Multiplex_xreg
sty Multiplex_yreg
ldx Multiplex_counter
.blit3 ldy Multiplex_indextable,x
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
lda Multiplex_YTable,y
cmp #$ff ; Don't display any sprites once this is reached
beq .intExitInter3
sta VIC2Sprite3Y
lda Multiplex_XPosLo,y
sta VIC2Sprite3X
lda Multiplex_SpriteFrame,y
sta Multiplex_spritepointer+3
lda Multiplex_Colour,y
sta VIC2Sprite3Colour
lda Multiplex_XPosHi,y
beq .no3
lda #$08
ora VIC2SpriteXMSB
bne .yes3
.no3 lda #$f7
and VIC2SpriteXMSB
.yes3 sta VIC2SpriteXMSB
inx
cpx Multiplex_MaxSpr
bne .nextspr4
.intExitInter3 jmp Multiplex_exitinter
;--------------------------------------
.nextspr4 lda VIC2Sprite4Y
adc #$17
sbc VIC2Raster
bcc .blit4
cmp #$03
bcs .next4
lda #$03
.next4 clc
adc VIC2Raster
sta VIC2Raster
lda #Multiplex_inter4
sta KERNALIRQServiceRoutineHi
inc VIC2InteruptStatus
lda CIA1InterruptControl
!ifdef Multiplexor_DebugBorder {
lda #3 : sta VIC2BorderColour
}
stx Multiplex_counter
lda Multiplex_areg
ldx Multiplex_xreg
ldy Multiplex_yreg
rti
Multiplex_inter4 sta Multiplex_areg
stx Multiplex_xreg
sty Multiplex_yreg
ldx Multiplex_counter
.blit4 ldy Multiplex_indextable,x
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
lda Multiplex_YTable,y
cmp #$ff ; Don't display any sprites once this is reached
beq .intExitInter4
sta VIC2Sprite4Y
lda Multiplex_XPosLo,y
sta VIC2Sprite4X
lda Multiplex_SpriteFrame,y
sta Multiplex_spritepointer+4
lda Multiplex_Colour,y
sta VIC2Sprite4Colour
lda Multiplex_XPosHi,y
beq .no4
lda #$10
ora VIC2SpriteXMSB
bne .yes4
.no4 lda #$ef
and VIC2SpriteXMSB
.yes4 sta VIC2SpriteXMSB
inx
cpx Multiplex_MaxSpr
bne .nextspr5
.intExitInter4 jmp Multiplex_exitinter
;--------------------------------------
.nextspr5 lda VIC2Sprite5Y
adc #$17
sbc VIC2Raster
bcc .blit5
cmp #$03
bcs .next5
lda #$03
.next5 clc
adc VIC2Raster
sta VIC2Raster
lda #Multiplex_inter5
sta KERNALIRQServiceRoutineHi
inc VIC2InteruptStatus
lda CIA1InterruptControl
!ifdef Multiplexor_DebugBorder {
lda #3 : sta VIC2BorderColour
}
stx Multiplex_counter
lda Multiplex_areg
ldx Multiplex_xreg
ldy Multiplex_yreg
rti
Multiplex_inter5 sta Multiplex_areg
stx Multiplex_xreg
sty Multiplex_yreg
ldx Multiplex_counter
.blit5 ldy Multiplex_indextable,x
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
lda Multiplex_YTable,y
cmp #$ff ; Don't display any sprites once this is reached
beq .intExitInter5
sta VIC2Sprite5Y
lda Multiplex_XPosLo,y
sta VIC2Sprite5X
lda Multiplex_SpriteFrame,y
sta Multiplex_spritepointer+5
lda Multiplex_Colour,y
sta VIC2Sprite5Colour
lda Multiplex_XPosHi,y
beq .no5
lda #$20
ora VIC2SpriteXMSB
bne .yes5
.no5 lda #$df
and VIC2SpriteXMSB
.yes5 sta VIC2SpriteXMSB
inx
cpx Multiplex_MaxSpr
bne .nextspr6
.intExitInter5 jmp Multiplex_exitinter
;--------------------------------------
.nextspr6 lda VIC2Sprite6Y
adc #$17
sbc VIC2Raster
bcc .blit6
cmp #$03
bcs .next6
lda #$03
.next6 clc
adc VIC2Raster
sta VIC2Raster
lda #Multiplex_inter6
sta KERNALIRQServiceRoutineHi
inc VIC2InteruptStatus
lda CIA1InterruptControl
!ifdef Multiplexor_DebugBorder {
lda #3 : sta VIC2BorderColour
}
stx Multiplex_counter
lda Multiplex_areg
ldx Multiplex_xreg
ldy Multiplex_yreg
rti
Multiplex_inter6 sta Multiplex_areg
stx Multiplex_xreg
sty Multiplex_yreg
ldx Multiplex_counter
.blit6 ldy Multiplex_indextable,x
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
lda Multiplex_YTable,y
cmp #$ff ; Don't display any sprites once this is reached
beq .intExitInter6
sta VIC2Sprite6Y
lda Multiplex_XPosLo,y
sta VIC2Sprite6X
lda Multiplex_SpriteFrame,y
sta Multiplex_spritepointer+6
lda Multiplex_Colour,y
sta VIC2Sprite6Colour
lda Multiplex_XPosHi,y
beq .no6
lda #$40
ora VIC2SpriteXMSB
bne .yes6
.no6 lda #$bf
and VIC2SpriteXMSB
.yes6 sta VIC2SpriteXMSB
inx
cpx Multiplex_MaxSpr
bne .nextspr7
.intExitInter6 jmp Multiplex_exitinter
;--------------------------------------
.nextspr7 lda VIC2Sprite7Y
adc #$17
sbc VIC2Raster
bcc .blit7
cmp #$03
bcs .next7
lda #$03
.next7 clc
adc VIC2Raster
sta VIC2Raster
lda #Multiplex_inter7
sta KERNALIRQServiceRoutineHi
inc VIC2InteruptStatus
lda CIA1InterruptControl
!ifdef Multiplexor_DebugBorder {
lda #3 : sta VIC2BorderColour
}
stx Multiplex_counter
lda Multiplex_areg
ldx Multiplex_xreg
ldy Multiplex_yreg
rti
Multiplex_inter7 sta Multiplex_areg
stx Multiplex_xreg
sty Multiplex_yreg
ldx Multiplex_counter
.blit7 ldy Multiplex_indextable,x
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
lda Multiplex_YTable,y
cmp #$ff ; Don't display any sprites once this is reached
beq Multiplex_exitinter
sta VIC2Sprite7Y
lda Multiplex_XPosLo,y
sta VIC2Sprite7X
lda Multiplex_SpriteFrame,y
sta Multiplex_spritepointer+7
lda Multiplex_Colour,y
sta VIC2Sprite7Colour
lda Multiplex_XPosHi,y
beq .no7
lda #$80
ora VIC2SpriteXMSB
bne .yes7
.no7 lda #$7f
and VIC2SpriteXMSB
.yes7 sta VIC2SpriteXMSB
inx
cpx Multiplex_MaxSpr
beq Multiplex_exitinter
jmp .nextspr0
.jumplo !by Multiplex_exitinter,>.dospr0,>.dospr1,>.dospr2
!by >.dospr3,>.dospr4,>.dospr5,>.dospr6
!by >.dospr7
.activatetab !by $00,$01,$03,$07,$0f,$1f,$3f,$7f,$ff
}
;--------------------------------------
!zn {
; The last interrupt that displays sprites gets to this exit routine.
Multiplex_exitinter
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
}
lda #$ef
cmp CIA1KeyboardRowsJoystickB
beq .over
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
lda #1
sta VIC2BorderColour
}
; Because we are exiting the current screen of sprites to display we can move the sprites and sort them.
jsr move
.over
!ifdef Multiplexor_DebugBorder {
inc VIC2BorderColour
lda #2
sta VIC2BorderColour
}
; MPi: Even without any sprite move being called this still calls the sort to demonstrate just how quick the sort is.
; The sort (red border area at the bottom of the screen) is actually on average much quicker than the move loop (the white area above the red).
; This runs the sort using the previous results of the sort as a starting point to work from.
; It's called the "Ocean method" since it was commonly used in Ocean games.
jsr Multiplex_sort
!ifdef Multiplexor_DebugBorder {
lda #0
sta VIC2BorderColour
}
; Start the main interrupt back at the top of the screen again
lda #Multiplex_maininter
sta KERNALIRQServiceRoutineHi
; MPi: First raster at the top of the first sprite minus a small amount of raster time to allow the first lot of sprite to be displayed
ldy Multiplex_indextable
lda Multiplex_YTable,y
sec
sbc #8
bcs .storeRaster
lda #0 ; MPi: Don't go up beyond the top line
.storeRaster
sta VIC2Raster
inc VIC2InteruptStatus
lda CIA1InterruptControl
!ifdef Multiplexor_DebugBorder {
lda #3 : sta VIC2BorderColour
}
lda Multiplex_areg
ldx Multiplex_xreg
ldy Multiplex_yreg
rti
}
;--------------------------------------
!zn {
Multiplex_initsort
ldx Multiplex_MaxSpr
dex
.1 txa
sta Multiplex_indextable,x
dex
bpl .1
lda #sortstart
sta Multiplex_bah
ldy #$00
.2 lda Multiplex_bal
sta Multiplex_sortlo,y
lda Multiplex_bah
sta Multiplex_sorthi,y
lda Multiplex_bal
clc
adc #18
sta Multiplex_bal
bcc .over
inc Multiplex_bah
.over iny
cpy #Multiplex_items-1
bne .2
rts
}
;--------------------------------------
!zn {
Multiplex_sort
lda Multiplex_MaxSpr
cmp #$02
bcc .exit
sbc #$02
tay
lda Multiplex_sortlo,y
sta Multiplex_bal
lda Multiplex_sorthi,y
sta Multiplex_bah
ldy #$00
lda #$60
sta (Multiplex_bal),y
jsr .over0
ldy #$00
lda #$a4
sta (Multiplex_bal),y
.exit rts
.over0 ldy Multiplex_indextable+1
.back0 ldx Multiplex_indextable
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over1
stx Multiplex_indextable+1
sty Multiplex_indextable
sortstart
.over1 ldy Multiplex_indextable+2
.back1 ldx Multiplex_indextable+1
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over2
stx Multiplex_indextable+2
sty Multiplex_indextable+1
bcc .back0
.over2 ldy Multiplex_indextable+3
.back2 ldx Multiplex_indextable+2
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over3
stx Multiplex_indextable+3
sty Multiplex_indextable+2
bcc .back1
.over3 ldy Multiplex_indextable+4
.back3 ldx Multiplex_indextable+3
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over4
stx Multiplex_indextable+4
sty Multiplex_indextable+3
bcc .back2
.over4 ldy Multiplex_indextable+5
.back4 ldx Multiplex_indextable+4
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over5
stx Multiplex_indextable+5
sty Multiplex_indextable+4
bcc .back3
.over5 ldy Multiplex_indextable+6
.back5 ldx Multiplex_indextable+5
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over6
stx Multiplex_indextable+6
sty Multiplex_indextable+5
bcc .back4
.over6 ldy Multiplex_indextable+7
.back6 ldx Multiplex_indextable+6
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over7
stx Multiplex_indextable+7
sty Multiplex_indextable+6
bcc .back5
.over7 ldy Multiplex_indextable+8
.back7 ldx Multiplex_indextable+7
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over8
stx Multiplex_indextable+8
sty Multiplex_indextable+7
bcc .back6
.over8 ldy Multiplex_indextable+9
.back8 ldx Multiplex_indextable+8
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over9
stx Multiplex_indextable+9
sty Multiplex_indextable+8
bcc .back7
.over9 ldy Multiplex_indextable+10
.back9 ldx Multiplex_indextable+9
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over10
stx Multiplex_indextable+10
sty Multiplex_indextable+9
bcc .back8
.over10 ldy Multiplex_indextable+11
.back10 ldx Multiplex_indextable+10
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over11
stx Multiplex_indextable+11
sty Multiplex_indextable+10
bcc .back9
;-------------------
.over11 ldy Multiplex_indextable+12
.back11 ldx Multiplex_indextable+11
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over12
stx Multiplex_indextable+12
sty Multiplex_indextable+11
bcc .back10
.over12 ldy Multiplex_indextable+13
.back12 ldx Multiplex_indextable+12
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over13
stx Multiplex_indextable+13
sty Multiplex_indextable+12
bcc .back11
.over13 ldy Multiplex_indextable+14
.back13 ldx Multiplex_indextable+13
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over14
stx Multiplex_indextable+14
sty Multiplex_indextable+13
bcc .back12
.over14 ldy Multiplex_indextable+15
.back14 ldx Multiplex_indextable+14
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over15
stx Multiplex_indextable+15
sty Multiplex_indextable+14
bcc .back13
.over15 ldy Multiplex_indextable+16
.back15 ldx Multiplex_indextable+15
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over16
stx Multiplex_indextable+16
sty Multiplex_indextable+15
bcc .back14
.over16 ldy Multiplex_indextable+17
.back16 ldx Multiplex_indextable+16
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over17
stx Multiplex_indextable+17
sty Multiplex_indextable+16
bcc .back15
.over17 ldy Multiplex_indextable+18
.back17 ldx Multiplex_indextable+17
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over18
stx Multiplex_indextable+18
sty Multiplex_indextable+17
bcc .back16
.over18 ldy Multiplex_indextable+19
.back18 ldx Multiplex_indextable+18
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over19
stx Multiplex_indextable+19
sty Multiplex_indextable+18
bcc .back17
.over19 ldy Multiplex_indextable+20
.back19 ldx Multiplex_indextable+19
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over20
stx Multiplex_indextable+20
sty Multiplex_indextable+19
bcc .back18
.over20 ldy Multiplex_indextable+21
.back20 ldx Multiplex_indextable+20
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over21
stx Multiplex_indextable+21
sty Multiplex_indextable+20
bcc .back19
;-------------------
.over21 ldy Multiplex_indextable+22
.back21 ldx Multiplex_indextable+21
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over22
stx Multiplex_indextable+22
sty Multiplex_indextable+21
bcc .back20
.over22 ldy Multiplex_indextable+23
.back22 ldx Multiplex_indextable+22
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over23
stx Multiplex_indextable+23
sty Multiplex_indextable+22
bcc .back21
.over23 ldy Multiplex_indextable+24
.back23 ldx Multiplex_indextable+23
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over24
stx Multiplex_indextable+24
sty Multiplex_indextable+23
bcc .back22
.over24 ldy Multiplex_indextable+25
.back24 ldx Multiplex_indextable+24
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over25
stx Multiplex_indextable+25
sty Multiplex_indextable+24
bcc .back23
.over25 ldy Multiplex_indextable+26
.back25 ldx Multiplex_indextable+25
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over26
stx Multiplex_indextable+26
sty Multiplex_indextable+25
bcc .back24
.over26 ldy Multiplex_indextable+27
.back26 ldx Multiplex_indextable+26
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over27
stx Multiplex_indextable+27
sty Multiplex_indextable+26
bcc .back25
.over27 ldy Multiplex_indextable+28
.back27 ldx Multiplex_indextable+27
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over28
stx Multiplex_indextable+28
sty Multiplex_indextable+27
bcc .back26
.over28 ldy Multiplex_indextable+29
.back28 ldx Multiplex_indextable+28
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over29
stx Multiplex_indextable+29
sty Multiplex_indextable+28
bcc .back27
.over29 ldy Multiplex_indextable+30
.back29 ldx Multiplex_indextable+29
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over30
stx Multiplex_indextable+30
sty Multiplex_indextable+29
bcc .back28
.over30 ldy Multiplex_indextable+31
.back30 ldx Multiplex_indextable+30
lda Multiplex_YTable,y
cmp Multiplex_YTable,x
bcs .over31
stx Multiplex_indextable+31
sty Multiplex_indextable+30
bcc .back29
.over31 ldy Multiplex_indextable
rts
}
!align 255, 0
;--------------------------------------
Multiplex_YTable
!by $34,$38,$3c,$40,$44,$48,$4c,$50
!by $74,$78,$7c,$80,$84,$88,$8c,$90
!by $60,$60,$75,$75,$a4,$a8,$ac,$b0
!by $54,$58,$5c,$60,$64,$68,$6c,$70
Multiplex_XPosLo
!by $20,$40,$60,$80,$a0,$c0,$e0,$ff
!by $20,$40,$60,$80,$a0,$c0,$e0,$ff
!by $80,$98,$80,$98,$a0,$c0,$e0,$ff
!by $20,$40,$60,$80,$a0,$c0,$e0,$ff
Multiplex_XPosHi
!by $00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00
Multiplex_Colour
!by $01,$02,$03,$04,$05,$06,$07,$08
!by $09,$0a,$0b,$0c,$0d,$0e,$0f,$01
!by $01,$02,$03,$04,$05,$06,$07,$08
!by $09,$0a,$0b,$0c,$0d,$0e,$0f,$01
Multiplex_SpriteFrame
!by $ff,$fe,$fd,$fc,$ff,$fe,$fd,$fc
!by $ff,$fe,$fd,$fc,$ff,$fe,$fd,$fc
!by $ff,$fe,$fd,$fc,$ff,$fe,$fd,$fc
!by $ff,$fe,$fd,$fc,$ff,$fe,$fd,$fc
Multiplex_sortlo !fill Multiplex_items-1
Multiplex_sorthi !fill Multiplex_items-1
!align 255, 0
;--------------------------------------
!zn {
move
ldy Multiplex_MaxSpr
dey
bmi .exit
.1 lda Multiplex_counterx2
clc
adc Multiplex_xdif
sta Multiplex_counterx2
clc
adc Multiplex_counterx1
tax
lda sinx,x
sta Multiplex_XPosLo,y
lda sinxhi,x
sta Multiplex_XPosHi,y
lda Multiplex_countery2
clc
adc Multiplex_ydif
sta Multiplex_countery2
clc
adc Multiplex_countery1
tax
lda siny,x
sta Multiplex_YTable,y
dey
bpl .1
.exit
; MPi: When uncommented this demonstrates that when a sprite has a Y coord of $ff then the multiplexor will sort them to the end of the list and will stop plotting sprites.
; lda #$ff
; sta Multiplex_YTable + 7
; sta Multiplex_YTable + 17
; sta Multiplex_YTable + 27
; sta Multiplex_YTable + 18
; sta Multiplex_YTable + 19
; sta Multiplex_YTable + 20
; sta Multiplex_YTable + 21
; sta Multiplex_YTable + 22
; sta Multiplex_YTable + 23
; MPi: When uncommented demonstrate how only modifying some sprite Y values each frame and keeping others constant results in a faster sort time.
; lda #50
; sta Multiplex_YTable + 4
; sta Multiplex_YTable + 5
; sta Multiplex_YTable + 6
; sta Multiplex_YTable + 7
; lda #80
; sta Multiplex_YTable + 16
; sta Multiplex_YTable + 17
; sta Multiplex_YTable + 18
; sta Multiplex_YTable + 19
; lda #110
; sta Multiplex_YTable + 20
; sta Multiplex_YTable + 21
; sta Multiplex_YTable + 22
; sta Multiplex_YTable + 23
; lda #140
; sta Multiplex_YTable + 24
; sta Multiplex_YTable + 25
; sta Multiplex_YTable + 26
; sta Multiplex_YTable + 27
; lda #170
; sta Multiplex_YTable + 0
; sta Multiplex_YTable + 1
; sta Multiplex_YTable + 2
; sta Multiplex_YTable + 3
; lda #200
; sta Multiplex_YTable + 8
; sta Multiplex_YTable + 9
; sta Multiplex_YTable + 10
; sta Multiplex_YTable + 11
; lda #230
; sta Multiplex_YTable + 12
; sta Multiplex_YTable + 13
; sta Multiplex_YTable + 14
; sta Multiplex_YTable + 15
lda Multiplex_xoffset
sta Multiplex_counterx2
lda Multiplex_yoffset
sta Multiplex_countery2
lda Multiplex_counterx1
clc
adc Multiplex_xspeed
sta Multiplex_counterx1
lda Multiplex_countery1
clc
adc Multiplex_yspeed
sta Multiplex_countery1
rts
}
!align 255, 0
sinx
!by $af,$b2,$b6,$b9,$bd,$c1,$c4,$c8,$cb,$cf,$d2,$d6,$d9,$dd,$e0,$e3
!by $e7,$ea,$ed,$f1,$f4,$f7,$fa,$fd,$00,$03,$06,$09,$0b,$0e,$11,$13
!by $16,$18,$1b,$1d,$1f,$21,$24,$26,$28,$2a,$2b,$2d,$2f,$30,$32,$33
!by $35,$36,$37,$38,$39,$3a,$3b,$3c,$3c,$3d,$3d,$3e,$3e,$3e,$3e,$3e
!by $3e,$3e,$3e,$3e,$3d,$3d,$3c,$3c,$3b,$3a,$39,$38,$37,$36,$35,$33
!by $32,$30,$2f,$2d,$2b,$2a,$28,$26,$24,$21,$1f,$1d,$1b,$18,$16,$13
!by $11,$0e,$0b,$09,$06,$03,$00,$fd,$fa,$f7,$f4,$f1,$ed,$ea,$e7,$e3
!by $e0,$dd,$d9,$d6,$d2,$cf,$cb,$c8,$c4,$c1,$bd,$b9,$b6,$b2,$af,$ab
!by $a7,$a4,$a0,$9d,$99,$95,$92,$8e,$8b,$87,$84,$80,$7d,$79,$76,$73
!by $6f,$6c,$69,$65,$62,$5f,$5c,$59,$56,$53,$50,$4d,$4b,$48,$45,$43
!by $40,$3e,$3b,$39,$37,$35,$32,$30,$2e,$2c,$2b,$29,$27,$26,$24,$23
!by $21,$20,$1f,$1e,$1d,$1c,$1b,$1a,$1a,$19,$19,$18,$18,$18,$18,$18
!by $18,$18,$18,$18,$19,$19,$1a,$1a,$1b,$1c,$1d,$1e,$1f,$20,$21,$23
!by $24,$26,$27,$29,$2b,$2c,$2e,$30,$32,$35,$37,$39,$3b,$3e,$40,$43
!by $45,$48,$4b,$4d,$50,$53,$56,$59,$5c,$5f,$62,$65,$69,$6c,$6f,$73
!by $76,$79,$7d,$80,$84,$87,$8b,$8e,$92,$95,$99,$9d,$a0,$a4,$a7,$ab
sinxhi
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$01,$01,$01,$01,$01,$01,$01,$01
!by $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
!by $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
!by $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
!by $01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01
!by $01,$01,$01,$01,$01,$01,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
!by $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
siny
!by $8d,$8f,$92,$94,$96,$98,$9a,$9c,$9f,$a1,$a3,$a5,$a7,$a9,$ab,$ad
!by $af,$b1,$b3,$b5,$b7,$b9,$bb,$bc,$be,$c0,$c2,$c3,$c5,$c7,$c8,$ca
!by $cb,$cd,$ce,$d0,$d1,$d2,$d4,$d5,$d6,$d7,$d8,$d9,$da,$db,$dc,$dd
!by $de,$df,$e0,$e0,$e1,$e1,$e2,$e2,$e3,$e3,$e3,$e4,$e4,$e4,$e4,$e4
!by $e4,$e4,$e4,$e4,$e3,$e3,$e3,$e2,$e2,$e1,$e1,$e0,$e0,$df,$de,$dd
!by $dc,$db,$da,$d9,$d8,$d7,$d6,$d5,$d4,$d2,$d1,$d0,$ce,$cd,$cb,$ca
!by $c8,$c7,$c5,$c3,$c2,$c0,$be,$bc,$bb,$b9,$b7,$b5,$b3,$b1,$af,$ad
!by $ab,$a9,$a7,$a5,$a3,$a1,$9f,$9c,$9a,$98,$96,$94,$92,$8f,$8d,$8b
!by $89,$87,$84,$82,$80,$7e,$7c,$7a,$77,$75,$73,$71,$6f,$6d,$6b,$69
!by $67,$65,$63,$61,$5f,$5d,$5b,$5a,$58,$56,$54,$53,$51,$4f,$4e,$4c
!by $4b,$49,$48,$46,$45,$44,$42,$41,$40,$3f,$3e,$3d,$3c,$3b,$3a,$39
!by $38,$37,$36,$36,$35,$35,$34,$34,$33,$33,$33,$32,$32,$32,$32,$32
!by $32,$32,$32,$32,$33,$33,$33,$34,$34,$35,$35,$36,$36,$37,$38,$39
!by $3a,$3b,$3c,$3d,$3e,$3f,$40,$41,$42,$44,$45,$46,$48,$49,$4b,$4c
!by $4e,$4f,$51,$53,$54,$56,$58,$5a,$5b,$5d,$5f,$61,$63,$65,$67,$69
!by $6b,$6d,$6f,$71,$73,$75,$77,$7a,$7c,$7e,$80,$82,$84,$87,$89,$8b
!by $ff
=== Create as "stdlib.asm" ===
; Zero page
; Each enabled bit sets read and write on the processor port (ZPProcessorPort) otherwise the value can just be read.
; Default: $2F, %101111
ZPProcessorPortDDR = $00
; Bits 0-2: Configuration for memory areas $A000-$BFFF, $D000-$DFFF and $E000-$FFFF. Values:
; %x00: RAM visible in all three areas.
; %x01: RAM visible at $A000-$BFFF and $E000-$FFFF.
; %x10: RAM visible at $A000-$BFFF; KERNAL ROM visible at $E000-$FFFF.
; %x11: BASIC ROM visible at $A000-$BFFF; KERNAL ROM visible at $E000-$FFFF.
; %0xx: Character ROM visible at $D000-$DFFF. (Except for the value %000, see above.)
; %1xx: I/O area visible at $D000-$DFFF. (Except for the value %100, see above.)
; Bit 3: Datasette output signal level.
; Bit 4: Datasette button status; 0 = One or more of PLAY, RECORD, F.FWD or REW pressed; 1 = No button is pressed.
; Bit 5: Datasette motor control; 0 = On; 1 = Off.
; Default: $37, %110111
ZPProcessorPort = $01
; $02 - $06 are unused (apparently).
; $07 - $2a are only really used during BASIC execution.
; By default contains $0801
ZPStartBasicLo = $2b
ZPStartBasicHi = $2c
ZPStartVariableLo = $2d
ZPStartVariableHi = $2e
ZPStartArrayVariableLo = $2f
ZPStartArrayVariableHi = $30
ZPEndArrayVariableLo = $31
ZPEndArrayVariableHi = $32
ZPStartStringVariableLo = $33
ZPStartStringVariableHi = $34
ZPCurrentStringVariableLo = $35
ZPCurrentStringVariableHi = $36
ZPEndBasicLo = $37
ZPEndBasicHi = $38
; $39 - $72 are only really used during BASIC execution.
; $73 - $8a
ZPChrGet = $73
; $8b - $8f are only really used during BASIC execution.
; Also used for datasette status
ZPSTVariable = $90
ZPStopKeyIndicator = $91
ZPDatasetteTiming = $92
ZPLoadVerify = $93
ZPSerialBusCacheStatus = $94
ZPSerialBusCache = $95
ZPDatasetteEndOfTape = $96
ZPRS232XYTemp = $97
ZPNumFilesOpen = $98
ZPCurrentInputDevice = $99
ZPCurrentOutputDevice = $9a
ZPDatasetteParity = $9b
ZPDatasetteByteReady = $9c
ZPDisplaySystemErrorSwitch = $9d
ZPRS232OutByte = $9e
ZPDatasetteNameWriteCount = $9f
ZPTimeOfDay = $a0 ; $a0 - a2
ZPEOISerialBusSwitch = $a3
ZPSerialBusBuffer = $a4
ZPSerialBusBitCounter = $a5
ZPDatasetteBufferOffset = $a6
ZPRS232BusBuffer = $a7
ZPRS232BusBitCounter = $a8
ZPRS232StopBitSwitch = $a9
ZPRS232ByteBuffer = $aa
ZPRS232Parity = $ab
ZPAddressToSave = $ac ; $ac - ad
ZPAddressToLoad = $ae ; $ae - af
; $b0 - $b1 unknown
ZPDatasetteBufferLo = $b2
ZPDatasetteBufferHo = $b3
ZPRS232BitCounter = $b4
ZPRS232BitBuffer = $b5
; $b7 - $c4 Various file operation working area
ZPPrevKeyPressed = $c5
ZPKeyBufferLength = $c6
; $c7 - $ca Various cursor operations
ZPCurrentKeyPressed = $cb
; $cc - $f6 Various cursor, screen and keyboard conversion tables
; $f7 - $fa RS232 input and output buffers
; $fb - $fe unused
ProcessorStack = $0100 ; $0100 - $01ff
; $0200 - $0292 Various keyboard buffers and buffers used by BASIC
; $0293 - $02ff RS232 and datasette control and buffers
; $0300 - $0312 Used by BASIC
; $0313 unused
DefaultIRQServiceRoutine = $ea31
MinimalIRQServiceRoutine = $ea81
IRQServiceRoutineLo = $0314
IRQServiceRoutineHi = $0315
; Default = $fe66
BRKServiceRoutineLo = $0316
BRKServiceRoutineHi = $0317
DefaultNMIServiceRoutine = $fe47
NMIServiceRoutineLo = $0318
NMIServiceRoutineHo = $0319
; $031a - $0333 Various vectors for standard routines like open, close, load, save etc
; Default $f4a5
LoadRoutineLo = $0330
LoadRoutineHi = $0331
; Default $f5ed
SaveRoutineLo = $0332
SaveRoutineHi = $0333
; $0334 - $033b unused
; $033c - $03fb Datasette buffer
; $03fc - $03ff unused
; Special memory sections
BASICSTART= $0801 ; Default is memory PEEK(43) = 1 and PEEK(44) = 8
SCREENRAM = $0400
SPRITEFRAME = $07f8
BASICROM = $A000
VIC = $D000
SID = $D400
COLORRAM = $D800
COLOURRAM = $D800
CIA1 = $DC00
CIA2 = $DD00
KERNALROM = $E000
; KERNAL routines
ACPTR = $FFA5
CHKIN = $FFC6
CHKOUT = $FFC9
CHRIN = $FFCF
CHROUT = $FFD2
CIOUT = $FFA8
CINT = $FF81
CLALL = $FFE7
CLOSE = $FFC3
CLRCHN = $FFCC
GETIN = $FFE4
IOBASE = $FFF3
IOINIT = $FF84
LISTEN = $FFB1
LOAD = $FFD5
MEMBOT = $FF9C
MEMTOP = $FF99
OPEN = $FFC0
PLOT = $FFF0
RAMTAS = $FF87
RDTIM = $FFDE
READST = $FFB7
RESTOR = $FF8A
SAVE = $FFD8
SCNKEY = $FF9F
SCREEN = $FFED
SECOND = $FF93
SETLFS = $FFBA
SETMSG = $FF90
SETNAM = $FFBD
SETTIM = $FFDB
SETTMO = $FFA2
STOP = $FFE1
TALK = $FFB4
TKSA = $FF96
UDTIM = $FFEA
UNLSN = $FFAE
UNTLK = $FFAB
VECTOR = $FF8D
; KERNAL Vectors
; Default = $fe43
KERNALNMIServiceRoutineLo = $fffa
KERNALNMIServiceRoutineHo = $fffb
; Default = $fce2
KERNALColdStartResetLo = $fffc
KERNALColdStartResetHi = $fffd
; Default = $ff48
KERNALIRQServiceRoutineLo = $fffe
KERNALIRQServiceRoutineHi = $ffff
; Specific locations within the custom chips
; VIC II Video chip
VIC2Sprite0X = $d000
VIC2Sprite0Y = $d001
VIC2Sprite1X = $d002
VIC2Sprite1Y = $d003
VIC2Sprite2X = $d004
VIC2Sprite2Y = $d005
VIC2Sprite3X = $d006
VIC2Sprite3Y = $d007
VIC2Sprite4X = $d008
VIC2Sprite4Y = $d009
VIC2Sprite5X = $d00a
VIC2Sprite5Y = $d00b
VIC2Sprite6X = $d00c
VIC2Sprite6Y = $d00d
VIC2Sprite7X = $d00e
VIC2Sprite7Y = $d00f
; Each bit is the X MSB for each sprite.
VIC2SpriteXMSB = $d010
; Bits 0-2 Vertical scroll.
; 3 Screen height 0 = 24 rows last line 246 (f6) : 1 = 25 rows last line $fa (250)
; 4 0 = Screen off 1 = Screen on
; 5 0 = Text mode 1 = Bitmap mode
; 6 1 = Extended background mode on
; 7 Read: Current raster line position bit 9. Write: Bit 9 of raster line position to generate next interrupt.
; Default: $1b, %00011011
VIC2ScreenControlV = $d011
; Read: Current raster line position.
; Write: Raster line position to generate next interrupt.
VIC2Raster = $d012
VIC2LightPenX = $d013
VIC2LightPenY = $d014
VIC2SpriteEnable = $d015
; Bits 0-2 Horizontal scroll.
; 3 Screen width 0 = 38 columns 1 = 40 columns
; 4 1 = Multicolour on
; 5-7 Unused
; Default: $c8, %11001000
VIC2ScreenControlH = $d016
; Each bit sets the double height enable for each sprite.
VIC2SpriteDoubleHeight = $d017
; In text mode:
; Bits 1-3 Character memory location * $0800 (2048) inside current VIC bank selected by $dd00.
; In VIC bank 0 and 2 bits %010 and %011 select character ROM except in ULTIMAX mode.
; In bitmap mode:
; Bit 3 Bitmap memory location * $2000 (8192) inside current VIC bank selected by $dd00.
; Bits 4-7 Screen memory location * $1000 (1024) inside current VIC bank selected by $dd00.
VIC2MemorySetup = $d018
; Read:
; Bit 0: 1 = Current raster line is equal to the raster line which is set to generate an interrupt.
; Bit 1: 1 = Sprite-background collision event.
; Bit 2: 1 = Sprite-sprite collision event.
; Bit 3: 1 = Light pen signal received.
; Bit 7: 1 = An event that might generate an interrupt happened.
; Write:
; Bit 0: 0 = Ack raster interrupt.
; Bit 1: 0 = Ack sprite-background collision interrupt.
; Bit 2: 0 = Ack sprite-sprite collision interrupt.
; Bit 3: 0 = Ack light pen signal interrupt.
VIC2InteruptStatus = $d019
; Bit 0: 1 = Raster interrupt enabled.
; Bit 1: 1 = Sprite-background interrupt enabled.
; Bit 2: 1 = Sprite-sprite interrupt enabled.
; Bit 3: 1 = Light pen interrupt enabled.
VIC2InteruptControl = $d01a
; Each bit sets the sprite background priority for each sprite.
; 0 = Sprite drawn in front of screen contents.
; 1 = Sprite drawn behind of screen contents.
VIC2SpritePriority = $d01b
; Each bit sets multicolour for each sprite.
; 0 = Sprite is single colour.
; 1 = Sprite is multicolour.
VIC2SpriteMulticolour = $d01c
; Each bit sets the double width enable for each sprite.
VIC2SpriteDoubleWidth = $d01d
; Read: For each set bit X the sprite X collided with another sprite.
; Write: For each set bit X allow further sprite-sprite collisions.
VIC2SpriteSpriteCollision = $d01e
; Read: For each set bit X the sprite X collided with the background.
; Write: For each set bit X allow further sprite-background collisions.
VIC2SpriteBackgroundCollision = $d01f
VIC2BorderColour = $d020
VIC2ScreenColour = $d021
VIC2ExtraBackgroundColour1 = $d022
VIC2ExtraBackgroundColour2 = $d023
VIC2ExtraBackgroundColour3 = $d024
VIC2ExtraSpriteColour1 = $d025
VIC2ExtraSpriteColour2 = $d025
VIC2Sprite0Colour = $d027
VIC2Sprite1Colour = $d028
VIC2Sprite2Colour = $d029
VIC2Sprite3Colour = $d02a
VIC2Sprite4Colour = $d02b
VIC2Sprite5Colour = $d02c
VIC2Sprite6Colour = $d02d
VIC2Sprite7Colour = $d02e
; SID Audio chip
SIDVoice1FreqLo = $d400 ; Write only
SIDVoice1FreqHi = $d401 ; Write only
SIDVoice1PulseWidthLo = $d402 ; Write only
SIDVoice1PulseWidthHi = $d403 ; Write only
; Bit 0: 0 = Voice off, release cycle. 1 = Voice on do attack-decay-sustain.
; Bit 1: 1 = Synchronization enable.
; Bit 2: 1 = Ting modulation enable.
; Bit 3: 1 = Disable voice.
; Bit 4: 1 = Triangle waveform enable.
; Bit 5: 1 = Saw waveform enable.
; Bit 6: 1 = Rectangle waveform enable.
; Bit 7: 1 = Noise waveform enable.
SIDVoice1Control = $d404 ; Write only
; Bits 0-3 Decay length:
; %0000, 0: 6 ms.
; %0001, 1: 24 ms.
; %0010, 2: 48 ms.
; %0011, 3: 72 ms.
; %0100, 4: 114 ms.
; %0101, 5: 168 ms.
; %0110, 6: 204 ms.
; %0111, 7: 240 ms.
; %1000, 8: 300 ms.
; %1001, 9: 750 ms.
; %1010, 10: 1.5 s.
; %1011, 11: 2.4 s.
; %1100, 12: 3 s.
; %1101, 13: 9 s.
; %1110, 14: 15 s.
; %1111, 15: 24 s.
; Bits 4-7 Decay length:
; %0000, 0: 2 ms.
; %0001, 1: 8 ms.
; %0010, 2: 16 ms.
; %0011, 3: 24 ms.
; %0100, 4: 38 ms.
; %0101, 5: 56 ms.
; %0110, 6: 68 ms.
; %0111, 7: 80 ms.
; %1000, 8: 100 ms.
; %1001, 9: 250 ms.
; %1010, 10: 500 ms.
; %1011, 11: 800 ms.
; %1100, 12: 1 s.
; %1101, 13: 3 s.
; %1110, 14: 5 s.
; %1111, 15: 8 s.
SIDVoice1AttackDecay = $d405 ; Write only
; Bits 0-3 Release length.
; %0000, 0: 6 ms.
; %0001, 1: 24 ms.
; %0010, 2: 48 ms.
; %0011, 3: 72 ms.
; %0100, 4: 114 ms.
; %0101, 5: 168 ms.
; %0110, 6: 204 ms.
; %0111, 7: 240 ms.
; %1000, 8: 300 ms.
; %1001, 9: 750 ms.
; %1010, 10: 1.5 s.
; %1011, 11: 2.4 s.
; %1100, 12: 3 s.
; %1101, 13: 9 s.
; %1110, 14: 15 s.
; %1111, 15: 24 s.
; Bits #4-#7: Sustain volume.
SIDVoice1SustainRelease = $d406 ; Write only
SIDVoice2FreqLo = $d407 ; Write only
SIDVoice2FreqHi = $d408 ; Write only
SIDVoice2PulseWidthLo = $d409 ; Write only
SIDVoice2PulseWidthHi = $d40a ; Write only
SIDVoice2Control = $d40b ; Write only
SIDVoice2AttackDecay = $d40c ; Write only
SIDVoice2SustainRelease = $d40d ; Write only
SIDVoice3FreqLo = $d40e ; Write only
SIDVoice3FreqHi = $d40f ; Write only
SIDVoice3PulseWidthLo = $d410 ; Write only
SIDVoice3PulseWidthHi = $d411 ; Write only
SIDVoice3Control = $d412 ; Write only
SIDVoice3AttackDecay = $d413 ; Write only
SIDVoice3SustainRelease = $d414 ; Write only
SIDFilterCutoffFreqLo = $d415 ; Write only
SIDFilterCutoffFreqHi = $d416 ; Write only
; Bit 0: 1 = Voice #1 filtered.
; Bit 1: 1 = Voice #2 filtered.
; Bit 2: 1 = Voice #3 filtered.
; Bit 3: 1 = External voice filtered.
; Bits 4-7: Filter resonance.
SIDFilterControl = $d417 ; Write only
; Bits 0-3: Volume.
; Bit 4: 1 = Low pass filter enabled.
; Bit 5: 1 = Band pass filter enabled.
; Bit 6: 1 = High pass filter enabled.
; Bit 7: 1 = Voice #3 disabled.
SIDVolumeFilter = $d418 ; Write only
; Paddle is selected by memory address $dd00
SIDPaddleX = $d419 ; Read only
; Paddle is selected by memory address $dd00
SIDPaddleY = $d41a ; Read only
SIDVoice3WaveformOutput = $d41b ; Read only
SIDVoice3ADSROutput = $d41c ; Read only
; CIA1
; Port A read:
; Bit 0: 0 = Port 2 joystick up pressed.
; Bit 1: 0 = Port 2 joystick down pressed.
; Bit 2: 0 = Port 2 joystick right pressed.
; Bit 3: 0 = Port 2 joystick left pressed.
; Bit 4: 0 = Port 2 joystick fire pressed.
; Write:
; Bit x: 0 = Select keyboard matrix column x.
; Bits 6-7: Paddle selection; %01 = Paddle #1; %10 = Paddle #2.
CIA1KeyboardColumnJoystickA = $dc00
; Port B, keyboard matrix rows and joystick #1. Bits:
; Bit x: 0 = A key is currently being pressed in keyboard matrix row #x, in the column selected at memory address $DC00.
; Bit 0: 0 = Port 1 joystick up pressed.
; Bit 1: 0 = Port 1 joystick down pressed.
; Bit 2: 0 = Port 1 joystick right pressed.
; Bit 3: 0 = Port 1 joystick left pressed.
; Bit 4: 0 = Port 1 joystick fire pressed.
CIA1KeyboardRowsJoystickB = $dc01
; Each enabled bit sets read and write on CIA1KeyboardColumnJoystickA otherwise the value can just be read.
CIA1PortADDR = $dc02
; Each enabled bit sets read and write on CIA1KeyboardRowsJoystickB otherwise the value can just be read.
CIA1PortBDDR = $dc03
CIA1TimerALo = $dc04
CIA1TimerAHi = $dc05
CIA1TimerBLo = $dc06
CIA1TimerBHi = $dc07
CIA1ToD10thSecsBCD = $dc08
CIA1ToDSecsBCD = $dc09
CIA1ToDMinsBCD = $dc0a
CIA1ToDHoursBCD = $dc0b
CIA1SerialShift = $dc0c
; Interrupt control and status register.
; Read bits:
; Bit 0: 1 = Timer A underflow occurred.
; Bit 1: 1 = Timer B underflow occurred.
; Bit 2: 1 = TOD is equal to alarm time.
; Bit 3: 1 = A complete byte has been received into or sent from serial shift register.
; Bit 4: Signal level on FLAG pin, datasette input.
; Bit 7: An interrupt has been generated.
; Write bits:
; Bit 0: 1 = Enable interrupts generated by timer A underflow.
; Bit 1: 1 = Enable interrupts generated by timer B underflow.
; Bit 2: 1 = Enable TOD alarm interrupt.
; Bit 3: 1 = Enable interrupts generated by a byte having been received/sent via serial shift register.
; Bit 4: 1 = Enable interrupts generated by positive edge on FLAG pin.
; Bit 7: Fill bit; bits 0-6, that are set to 1, get their values from this bit; bits 0-6, that are set to 0, are left unchanged.
CIA1InterruptControl = $dc0d
; Timer A control register. Bits:
; Bit 0: 0 = Stop timer; 1 = Start timer.
; Bit 1: 1 = Indicate timer underflow on port B bit 6.
; Bit 2: 0 = Upon timer underflow, invert port B bit 6; 1 = upon timer underflow, generate a positive edge on port B bit 6 for 1 system cycle.
; Bit 3: 0 = Timer restarts upon underflow; 1 = Timer stops upon underflow.
; Bit 4: 1 = Load start value into timer.
; Bit 5: 0 = Timer counts system cycles; 1 = Timer counts positive edges on CNT pin.
; Bit 6: Serial shift register direction; 0 = Input, read; 1 = Output, write.
; Bit 7: TOD speed; 0 = 60 Hz; 1 = 50 Hz.
CIA1TimerAControl = $dc0e
; Timer B control register. Bits:
; Bit 0: 0 = Stop timer; 1 = Start timer.
; Bit 1: 1 = Indicate timer underflow on port B bit 7.
; Bit 2: 0 = Upon timer underflow, invert port B bit 7; 1 = upon timer underflow, generate a positive edge on port B bit 7 for 1 system cycle.
; Bit 3: 0 = Timer restarts upon underflow; 1 = Timer stops upon underflow.
; Bit 4: 1 = Load start value into timer.
; Bits 5-6: %00 = Timer counts system cycles; %01 = Timer counts positive edges on CNT pin; %10 = Timer counts underflows of timer A; %11 = Timer counts underflows of timer A occurring along with a positive edge on CNT pin.
; Bit 7: 0 = Writing into TOD registers sets TOD; 1 = Writing into TOD registers sets alarm time.
CIA1TimerBControl = $dc0f
; CIA2. Mostly the same as CIA1 except for VIC bank, no datasette, RS232 and generates NMI instead of IRQ.
; Bits 0-1: VIC bank. Values:
; %00, 0: Bank 3, $C000-$FFFF, 49152-65535.
; %01, 1: Bank 2, $8000-$BFFF, 32768-49151.
; %10, 2: Bank 1, $4000-$7FFF, 16384-32767.
; %11, 3: Bank 0, $0000-$3FFF, 0-16383.
; Bit 2: RS232 TXD line, output bit.
; Bit 3: Serial bus ATN OUT; 0 = High; 1 = Low.
; Bit 4: Serial bus CLOCK OUT; 0 = High; 1 = Low.
; Bit 5: Serial bus DATA OUT; 0 = High; 1 = Low.
; Bit 6: Serial bus CLOCK IN; 0 = High; 1 = Low.
; Bit 7: Serial bus DATA IN; 0 = High; 1 = Low.
CIA2PortASerialBusVICBank = $dd00
; Read bits:
; Bit 0: RS232 RXD line, input bit.
; Bit 3: RS232 RI line.
; Bit 4: RS232 DCD line.
; Bit 5: User port H pin.
; Bit 6: RS232 CTS line; 1 = Sender is ready to send.
; Bit 7: RS232 DSR line; 1 = Receiver is ready to receive.
; Write bits:
; Bit 1: RS232 RTS line. 1 = Sender is ready to send.
; Bit 2: RS232 DTR line. 1 = Receiver is ready to receive.
; Bit 3: RS232 RI line.
; Bit 4: RS232 DCD line.
; Bit 5: User port H pin.
CIA2PortBRS232 = $dd01
; Each enabled bit sets read and write on CIA2PortASerialBusVICBank otherwise the value can just be read.
CIA2PortADDR = $dd02
; Each enabled bit sets read and write on CIA2PortBRS232 otherwise the value can just be read.
CIA2PortBDDR = $dd03
CIA2TimerALo = $dd04
CIA2TimerAHi = $dd05
CIA2TimerBLo = $dd06
CIA2TimerBHi = $dd07
CIA2ToD10thSecsBCD = $dd08
CIA2ToDSecsBCD = $dd09
CIA2ToDMinsBCD = $dd0a
CIA2ToDHoursBCD = $dd0b
CIA2SerialShift = $dd0c
; Non-maskable interrupt control and status register.
; Read bits:
; Bit 0: 1 = Timer A underflow occurred.
; Bit 1: 1 = Timer B underflow occurred.
; Bit 2: 1 = TOD is equal to alarm time.
; Bit 3: 1 = A complete byte has been received into or sent from serial shift register.
; Bit 4: Signal level on FLAG pin.
; Bit 7: An non-maskable interrupt has been generated.
; Write bits:
; Bit 0: 1 = Enable non-maskable interrupts generated by timer A underflow.
; Bit 1: 1 = Enable non-maskable interrupts generated by timer B underflow.
; Bit 2: 1 = Enable TOD alarm non-maskable interrupt.
; Bit 3: 1 = Enable non-maskable interrupts generated by a byte having been received/sent via serial shift register.
; Bit 4: 1 = Enable non-maskable interrupts generated by positive edge on FLAG pin.
; Bit 7: Fill bit; bits 0-6, that are set to 1, get their values from this bit; bits 0-6, that are set to 0, are left unchanged.
CIA2InterruptControl = $dd0d
; Timer A control register. Bits:
; Bit 0: 0 = Stop timer; 1 = Start timer.
; Bit 1: 1 = Indicate timer underflow on port B bit 6.
; Bit 2: 0 = Upon timer underflow, invert port B bit 6; 1 = upon timer underflow, generate a positive edge on port B bit 6 for 1 system cycle.
; Bit 3: 0 = Timer restarts upon underflow; 1 = Timer stops upon underflow.
; Bit 4: 1 = Load start value into timer.
; Bit 5: 0 = Timer counts system cycles; 1 = Timer counts positive edges on CNT pin.
; Bit 6: Serial shift register direction; 0 = Input, read; 1 = Output, write.
; Bit 7: TOD speed; 0 = 60 Hz; 1 = 50 Hz.
CIA2TimerAControl = $dd0e
; Timer B control register. Bits:
; Bit 0: 0 = Stop timer; 1 = Start timer.
; Bit 1: 1 = Indicate timer underflow on port B bit 7.
; Bit 2: 0 = Upon timer underflow, invert port B bit 7; 1 = upon timer underflow, generate a positive edge on port B bit 7 for 1 system cycle.
; Bit 3: 0 = Timer restarts upon underflow; 1 = Timer stops upon underflow.
; Bit 4: 1 = Load start value into timer.
; Bits 5-6: %00 = Timer counts system cycles; %01 = Timer counts positive edges on CNT pin; %10 = Timer counts underflows of timer A; %11 = Timer counts underflows of timer A occurring along with a positive edge on CNT pin.
; Bit 7: 0 = Writing into TOD registers sets TOD; 1 = Writing into TOD registers sets alarm time.
CIA2TimerBControl = $dd0f