===== 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