├── .gitignore ├── include_me_memory.asm ├── include_me_sprites.asm ├── include_me_timers.asm ├── include_me_sprites_r.asm ├── include_me_full.asm ├── include_me_full_r.asm ├── include_me_min.asm ├── variables.asm ├── registers.asm ├── sid.asm ├── joystick.asm ├── cia.asm ├── Makefile ├── constants.asm ├── memory.asm ├── pseudocommands.asm ├── kernal.asm ├── timers.asm ├── sprites.asm ├── README.md ├── vic.asm ├── basic.asm ├── macros.asm ├── tests_r.yaml ├── tests.6502 ├── keyboard.asm └── tests.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | kernal.rom 2 | basic.rom 3 | character.rom 4 | ByteDump.txt 5 | *.prg 6 | *.sym 7 | -------------------------------------------------------------------------------- /include_me_memory.asm: -------------------------------------------------------------------------------- 1 | // Include this file to pull in the entire library 2 | #import "include_me_min.asm" 3 | #import "memory.asm" 4 | -------------------------------------------------------------------------------- /include_me_sprites.asm: -------------------------------------------------------------------------------- 1 | // Include this file to pull in the entire library 2 | #import "include_me_min.asm" 3 | #import "sprites.asm" 4 | -------------------------------------------------------------------------------- /include_me_timers.asm: -------------------------------------------------------------------------------- 1 | // Include this file to pull in the entire library 2 | #import "include_me_min.asm" 3 | #import "memory.asm" 4 | #import "timers.asm" 5 | -------------------------------------------------------------------------------- /include_me_sprites_r.asm: -------------------------------------------------------------------------------- 1 | // Include this file to pull in the entire library 2 | #import "include_me_min.asm" 3 | #define REGISTER_MODE 4 | #import "sprites.asm" 5 | -------------------------------------------------------------------------------- /include_me_full.asm: -------------------------------------------------------------------------------- 1 | // Include this file to pull in the entire library 2 | #import "include_me_min.asm" 3 | #import "sprites.asm" 4 | #import "timers.asm" 5 | #import "memory.asm" 6 | #import "joystick.asm" 7 | #import "keyboard.asm" 8 | -------------------------------------------------------------------------------- /include_me_full_r.asm: -------------------------------------------------------------------------------- 1 | // Include this file to pull in the entire library 2 | #define REGISTER_MODE 3 | #import "include_me_min.asm" 4 | #import "sprites.asm" 5 | #import "timers.asm" 6 | #import "memory.asm" 7 | #import "joystick.asm" 8 | #import "keyboard.asm" 9 | -------------------------------------------------------------------------------- /include_me_min.asm: -------------------------------------------------------------------------------- 1 | // Include this file just to pull in equates, macros and pseudocommands 2 | #import "basic.asm" 3 | #import "kernal.asm" 4 | #import "vic.asm" 5 | #import "cia.asm" 6 | #import "sid.asm" 7 | #import "macros.asm" 8 | #import "pseudocommands.asm" 9 | #import "registers.asm" 10 | #import "constants.asm" 11 | #import "variables.asm" 12 | -------------------------------------------------------------------------------- /variables.asm: -------------------------------------------------------------------------------- 1 | // 8 timers of 8 bytes each 2 | // struct is as follows: 3 | // 0 - enabled/disabled 4 | // 1 - type: oneshot or continuous 5 | // 2,3 - current value 6 | // 4,5 - frequency 7 | // 6,7 - call location on fire 8 | .label c64lib_timers = kernal.TBUFFER 9 | 10 | // Joystick storage 11 | .label c64lib_joy_btn = $6d 12 | .label c64lib_joy_up = $6f 13 | .label c64lib_joy_down = $71 14 | .label c64lib_joy_left = $73 15 | .label c64lib_joy_right = $75 16 | -------------------------------------------------------------------------------- /registers.asm: -------------------------------------------------------------------------------- 1 | #importonce 2 | 3 | // 4 | // This library makes heavy use of these 8 16-bit pseudo registers stored in ZP 5 | // 6 | 7 | // 8 16-bit registers in ZP 8 | .label r0 = $fb 9 | .label r0L = $fb 10 | .label r0H = $fc 11 | .label r1 = $fd 12 | .label r1L = $fd 13 | .label r1H = $fe 14 | .label r2 = $61 15 | .label r2L = $61 16 | .label r2H = $62 17 | .label r3 = $63 18 | .label r3L = $63 19 | .label r3H = $64 20 | .label r4 = $65 21 | .label r4L = $65 22 | .label r4H = $66 23 | .label r5 = $67 24 | .label r5L = $67 25 | .label r5H = $68 26 | .label r6 = $69 27 | .label r6L = $69 28 | .label r6H = $6a 29 | .label r7 = $6b 30 | .label r7L = $6b 31 | .label r7H = $6c 32 | -------------------------------------------------------------------------------- /sid.asm: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Constants for the C64's Sound Interface Device (SID) 4 | 5 | */ 6 | .namespace sid { 7 | .label FRELO1 = $d400 8 | .label FREHI1 = $d401 9 | .label PWLO1 = $d402 10 | .label PWHI1 = $d403 11 | .label VCREG1 = $d404 12 | .label ATDCY1 = $d405 13 | .label SUREL1 = $d406 14 | .label FRELO2 = $d407 15 | .label FREHI2 = $d408 16 | .label PWLO2 = $d409 17 | .label PWHI2 = $d40a 18 | .label VCREG2 = $d40b 19 | .label ATDCY2 = $d40c 20 | .label SUREL2 = $d40d 21 | .label FRELO3 = $d40e 22 | .label FREHI3 = $d40f 23 | .label PWLO3 = $d410 24 | .label PWHI3 = $d411 25 | .label VCREG3 = $d412 26 | .label ATDCY3 = $d413 27 | .label SUREL3 = $d414 28 | .label CUTLO = $d415 29 | .label CUTHI = $d416 30 | .label RESON = $d417 31 | .label SIGVOL = $d418 32 | .label POTX = $d419 33 | .label POTY = $d41a 34 | .label RANDOM = $d41b 35 | .label ENV3 = $d41c 36 | } 37 | -------------------------------------------------------------------------------- /joystick.asm: -------------------------------------------------------------------------------- 1 | *=* "Joystick Routine" 2 | 3 | // 4 | // Read both joysticks and store the results 5 | // 6 | ReadJoysticks: 7 | ldx #$00 8 | jsr ReadJoystick 9 | inx 10 | jsr ReadJoystick 11 | rts 12 | 13 | // 14 | // Read the joystick position and button status and store the state 15 | // 16 | ReadJoystick: 17 | lda #DISABLE 18 | sta c64lib_joy_btn, x 19 | sta c64lib_joy_left, x 20 | sta c64lib_joy_right, x 21 | sta c64lib_joy_up, x 22 | sta c64lib_joy_down, x 23 | 24 | lda cia.CIAPRA, x 25 | and #JOY_BUTTON 26 | beq !set_button+ 27 | and #JOY_LEFT 28 | beq !set_left+ 29 | and #JOY_RIGHT 30 | beq !set_right+ 31 | and #JOY_UP 32 | beq !set_up+ 33 | and #JOY_DOWN 34 | beq !set_down+ 35 | 36 | jmp !return+ 37 | 38 | !set_button: 39 | stb #ENABLE:c64lib_joy_btn, x 40 | jmp !return+ 41 | 42 | !set_left: 43 | stb #ENABLE:c64lib_joy_left, x 44 | jmp !return+ 45 | 46 | !set_right: 47 | stb #ENABLE:c64lib_joy_right, x 48 | jmp !return+ 49 | 50 | !set_up: 51 | stb #ENABLE:c64lib_joy_up, x 52 | jmp !return+ 53 | 54 | !set_down: 55 | stb #ENABLE:c64lib_joy_down, x 56 | 57 | !return: 58 | rts 59 | -------------------------------------------------------------------------------- /cia.asm: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Constants for the C64's 2 Complex Interface Adapters (CIA) 4 | 5 | */ 6 | .namespace cia { 7 | .label VIC_BANK_MASK = %00000011 8 | .label VIC_BANK_REVERSE_MASK = %11111100 9 | 10 | .label BANK_0_MASK = %11 11 | .label BANK_1_MASK = %10 12 | .label BANK_2_MASK = %01 13 | .label BANK_3_MASK = %00 14 | 15 | .label CIAPRA = $dc00 16 | .label CIAPRB = $dc01 17 | .label CIDDRA = $dc02 18 | .label CIDDRB = $dc03 19 | .label TIMALO = $dc04 20 | .label TIMAHI = $dc05 21 | .label TIMBLO = $dc06 22 | .label TIMBHI = $dc07 23 | .label TODTEN = $dc08 24 | .label TODSEC = $dc09 25 | .label TODMIN = $dc0a 26 | .label TODHRS = $dc0b 27 | .label CIASDR = $dc0c 28 | .label CIAICR = $dc0d 29 | .label CIACRA = $dc0e 30 | .label CIACRB = $dc0f 31 | 32 | .label CI2PRA = $dd00 33 | .label CI2PRB = $dd01 34 | .label C2DDRA = $dd02 35 | .label C2DDRB = $dd03 36 | .label TI2ALO = $dd04 37 | .label TI2AHI = $dd05 38 | .label TI2BLO = $dd06 39 | .label TI2BHI = $dd07 40 | .label TO2TEN = $dd08 41 | .label TO2SEC = $dd09 42 | .label TO2MIN = $dd0a 43 | .label TO2HRS = $dd0b 44 | .label CI2SDR = $dd0c 45 | .label CI2ICR = $dd0d 46 | .label CI2CRA = $dd0e 47 | .label CI2CRB = $dd0f 48 | } 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | clean: 2 | rm -f *.sym 3 | rm -f *.prg 4 | rm -f ByteDump.txt 5 | 6 | build: clean build_full build_min build_memory build_sprites build_timers 7 | 8 | build_min: 9 | @echo Building minimum library 10 | @docker run -v ${PWD}:/code barrywalker71/kickassembler:latest -time -bytedump /code/include_me_min.asm 11 | 12 | build_full: 13 | @echo Building full library 14 | @docker run -v ${PWD}:/code barrywalker71/kickassembler:latest -define MEMORY -define SPRITES -define TIMERS -time -bytedump /code/include_me_full.asm 15 | @docker run -v ${PWD}:/code barrywalker71/kickassembler:latest -define MEMORY -define SPRITES -define TIMERS -time -bytedump /code/include_me_full_r.asm 16 | 17 | build_sprites: 18 | @echo Building sprite library 19 | @docker run -v ${PWD}:/code barrywalker71/kickassembler:latest -define SPRITES -time -symbolfile -bytedump /code/include_me_sprites.asm 20 | @docker run -v ${PWD}:/code barrywalker71/kickassembler:latest -define SPRITES -time -symbolfile -bytedump /code/include_me_sprites_r.asm 21 | 22 | build_memory: 23 | @echo Building memory library 24 | @docker run -v ${PWD}:/code barrywalker71/kickassembler:latest -define MEMORY -time -bytedump /code/include_me_memory.asm 25 | 26 | build_timers: 27 | @echo Building timer library 28 | @docker run -v ${PWD}:/code barrywalker71/kickassembler:latest -define TIMERS -define MEMORY -time -bytedump /code/include_me_timers.asm 29 | 30 | test: build 31 | @docker pull barrywalker71/sim6502cli:latest 32 | @docker run -v ${PWD}:/code -it barrywalker71/sim6502cli:latest -s /code/tests.6502 -t 33 | -------------------------------------------------------------------------------- /constants.asm: -------------------------------------------------------------------------------- 1 | #importonce 2 | 3 | // 4 | // All of the constants referenced in the rest of the library 5 | // 6 | 7 | // Enable/Disable flags 8 | .label ENABLE = $80 9 | .label DISABLE = $00 10 | .label TRUE = ENABLE 11 | .label FALSE = DISABLE 12 | 13 | // Timer constants 14 | .label TIMER_SINGLE = $00 15 | .label TIMER_CONTINUOUS = $01 16 | .label TIMER_HALF_SECOND = $1e 17 | .label TIMER_ONE_SECOND = $3c 18 | .label TIMER_TWO_SECONDS = $78 19 | .label TIMER_THREE_SECONDS = $b4 20 | .label TIMER_FOUR_SECONDS = $f0 21 | .label TIMER_STRUCT_BYTES = $40 22 | 23 | // The bank the VIC-II chip will be in 24 | .label BANK = $00 25 | 26 | // The start of physical RAM the VIC-II will see 27 | .label VIC_START = (BANK * $4000) 28 | 29 | // Offsets for start of VIC memory for each sprite attribute 30 | .label SPR_VISIBLE = vic.SPENA - vic.SP0X 31 | .label SPR_X_EXPAND = vic.XXPAND - vic.SP0X 32 | .label SPR_Y_EXPAND = vic.YXPAND - vic.SP0X 33 | .label SPR_HMC = vic.SPMC - vic.SP0X 34 | .label SPR_PRIORITY = vic.SPBGPR - vic.SP0X 35 | 36 | // Flags for various sprite settings 37 | .label SPR_HIDE = DISABLE 38 | .label SPR_SHOW = ENABLE 39 | .label SPR_NORMAL = DISABLE 40 | .label SPR_EXPAND = ENABLE 41 | .label SPR_FG = DISABLE 42 | .label SPR_BG = ENABLE 43 | .label SPR_HIRES = DISABLE 44 | .label SPR_MULTICOLOR = ENABLE 45 | 46 | // Joystick constants 47 | .label JOY_BUTTON = %00010000 48 | .label JOY_UP = %00000001 49 | .label JOY_DOWN = %00000010 50 | .label JOY_LEFT = %00000100 51 | .label JOY_RIGHT = %00001000 52 | -------------------------------------------------------------------------------- /memory.asm: -------------------------------------------------------------------------------- 1 | *=* "Memory Routines" 2 | 3 | // 4 | // These are generic memory copy and fill routines. 5 | // 6 | 7 | // 8 | // Fill a chunk of memory with a byte value. 9 | // 10 | // r0L value to store 11 | // r1 target memory 12 | // r2 number of bytes to store 13 | // 14 | FillMemory: 15 | ldy #$00 16 | !l1: 17 | lda r0L 18 | sta (r1), y 19 | Dec16(r2) 20 | CmpWI(r2, $0000) 21 | beq !end+ 22 | iny 23 | bne !l1- 24 | inc r1H 25 | jmp !l1- 26 | 27 | !end: 28 | rts 29 | 30 | // 31 | // Copy a chunk of memory 32 | // 33 | // r0 source memory 34 | // r1 target memory 35 | // r2 number of bytes to transfer 36 | // 37 | // Borrowed from https://github.com/mist64/geos/blob/2090bca64fc2627beef5c8232aafaec61f1f5a53/kernal/memory/memory2.s#L123 38 | // 39 | CopyMemory: 40 | lda r2L 41 | ora r2H 42 | beq !l7+ 43 | PushW(r0) 44 | PushB(r1H) 45 | PushB(r2H) 46 | PushB(r3L) 47 | !l1: 48 | CmpW(r0, r1) 49 | !l2: 50 | bcs !l3+ 51 | bcc !l8+ 52 | !l3: 53 | ldy #0 54 | lda r2H 55 | beq !l5+ 56 | !l4: 57 | lda (r0), y 58 | sta (r1), y 59 | iny 60 | bne !l4- 61 | inc r0H 62 | inc r1H 63 | dec r2H 64 | bne !l4- 65 | !l5: 66 | cpy r2L 67 | beq !l6+ 68 | lda (r0), y 69 | sta (r1), y 70 | iny 71 | bra !l5- 72 | !l6: 73 | PopB(r3L) 74 | PopB(r2H) 75 | PopB(r1H) 76 | PopW(r0) 77 | !l7: 78 | rts 79 | 80 | !l8: 81 | clc 82 | lda r2H 83 | adc r0H 84 | sta r0H 85 | clc 86 | lda r2H 87 | adc r1H 88 | sta r1H 89 | ldy r2L 90 | beq !lA+ 91 | !l9: 92 | dey 93 | lda (r0), y 94 | sta (r1), y 95 | tya 96 | bne !l9- 97 | !lA: 98 | dec r0H 99 | dec r1H 100 | lda r2H 101 | beq !l6- 102 | !lB: 103 | dey 104 | lda (r0), y 105 | sta (r1), y 106 | tya 107 | bne !lB- 108 | dec r2H 109 | bra !lA- 110 | -------------------------------------------------------------------------------- /pseudocommands.asm: -------------------------------------------------------------------------------- 1 | #importonce 2 | 3 | /* 4 | 5 | KickAssembler has a cool feature called 'pseudocommands' that let you build 6 | your own pseudo instructions. They're similar to macros, but the calling 7 | syntax very much resembles standard 6502 instructions. 8 | 9 | */ 10 | 11 | 12 | /* BFS: branch if flag set */ 13 | .pseudocommand bfs flag:location { 14 | lda flag 15 | bmi location 16 | } 17 | 18 | /* BFC: branch if flag clear */ 19 | .pseudocommand bfc flag:location { 20 | lda flag 21 | bpl location 22 | } 23 | 24 | /* CLF: clear flag */ 25 | .pseudocommand clf flag { 26 | Disable(flag) 27 | } 28 | 29 | /* SEF: set flag */ 30 | .pseudocommand sef flag { 31 | Enable(flag) 32 | } 33 | 34 | /* TGF: toggle flag */ 35 | .pseudocommand tgf flag { 36 | Toggle(flag) 37 | } 38 | 39 | /* WFS: wait for flag set */ 40 | .pseudocommand wfs flag { 41 | !wait: 42 | bfc flag:!wait- 43 | } 44 | 45 | /* WFC: wait for flag clear */ 46 | .pseudocommand wfc flag { 47 | !wait: 48 | bfs flag:!wait- 49 | } 50 | 51 | /* WFV: wait for value */ 52 | .pseudocommand wfv address:value { 53 | !wait: 54 | jne address:value:!wait- 55 | } 56 | 57 | /* JNE: jump if not equal */ 58 | .pseudocommand jne address:value:location { 59 | lda address 60 | cmp value 61 | bne location 62 | } 63 | 64 | /* JEQ: jump if equal */ 65 | .pseudocommand jeq address:value:location { 66 | lda address 67 | cmp value 68 | beq location 69 | } 70 | 71 | /* STB: store byte */ 72 | .pseudocommand stb value:address { 73 | lda value 74 | sta address 75 | } 76 | 77 | /* MULT2: multiply the accumulator by 2 */ 78 | .pseudocommand mult2 { 79 | asl 80 | } 81 | 82 | /* DIV2: divide the accumulator by 2 */ 83 | .pseudocommand div2 { 84 | lsr 85 | } 86 | 87 | /* MULT4: multiply the accumulator by 4 */ 88 | .pseudocommand mult4 { 89 | mult2 90 | mult2 91 | } 92 | 93 | /* DIV4: divide the accumulator by 4 */ 94 | .pseudocommand div4 { 95 | div2 96 | div2 97 | } 98 | 99 | /* MULT8: multiply the accumulator by 8 */ 100 | .pseudocommand mult8 { 101 | asl 102 | asl 103 | asl 104 | } 105 | 106 | /* MULT16: multiply the accumulator by 16 */ 107 | .pseudocommand mult16 { 108 | mult8 109 | mult8 110 | } 111 | 112 | /* BRA: branch always */ 113 | .pseudocommand bra address { 114 | clv 115 | bvc address 116 | } 117 | -------------------------------------------------------------------------------- /kernal.asm: -------------------------------------------------------------------------------- 1 | .namespace kernal { 2 | .label LSTX = $c5 3 | .label CINV = $0314 4 | .label IRQVEC = $0314 5 | 6 | .label CBINV = $0316 7 | .label NMINV = $0318 8 | 9 | // KERNAL vectors 10 | .label IOPEN = $031a 11 | .label ICLOSE = $031c 12 | .label ICHKIN = $031e 13 | .label ICKOUT = $0320 14 | .label ICLRCH = $0322 15 | .label IBASIN = $0324 16 | .label IBSOUT = $0326 17 | .label ISTOP = $0328 18 | .label IGETIN = $032a 19 | .label ICLALL = $032c 20 | .label USRCMD = $032e 21 | .label ILOAD = $0330 22 | .label ISAVE = $0332 23 | 24 | .label FREE1 = $0334 // 8 free bytes (0334 - 033b) 25 | .label TBUFFER= $033c // cassette buffer (033c - 03fb) 26 | .label FREE2 = $03fc // 4 free bytes (03fc - 03ff) 27 | 28 | .label POLY1 = $e043 29 | .label POLY2 = $e059 30 | .label RMULC = $e08d 31 | .label RADDC = $e092 32 | .label RND = $e097 33 | .label SYS = $e12a 34 | .label SAVE = $e156 35 | .label VERIFY = $e165 36 | .label LOAD = $e168 37 | .label OPEN = $e1be 38 | .label CLOSE = $e1c7 39 | 40 | .label CLRSCR = $e544 41 | .label HOME = $e566 42 | 43 | .label IRQNOR = $ea31 44 | 45 | // KERNAL jump Table 46 | .label VEC_CINT = $ff81 47 | .label VEC_IOINIT = $ff84 48 | .label VEC_RAMTAS = $ff87 49 | .label VEC_RESTOR = $ff8a 50 | .label VEC_VECTOR = $ff8d 51 | .label VEC_SETMSG = $ff90 52 | .label VEC_SECOND = $ff93 53 | .label VEC_TKSA = $ff96 54 | .label VEC_MEMBOT = $ff99 55 | .label VEC_MEMTOP = $ff9c 56 | .label VEC_SCNKEY = $ff9f 57 | .label VEC_SETTMO = $ffa2 58 | .label VEC_ACPTR = $ffa5 59 | .label VEC_CIOUT = $ffa8 60 | .label VEC_UNTLK = $ffab 61 | .label VEC_UNLSN = $ffae 62 | .label VEC_LISTEN = $ffb1 63 | .label VEC_TALK = $ffb4 64 | .label VEC_READST = $ffb7 65 | .label VEC_SETLFS = $ffba 66 | .label VEC_SETNAM = $ffbd 67 | .label VEC_OPEN = $ffc0 68 | .label VEC_CLOSE = $ffc3 69 | .label VEC_CHKIN = $ffc6 70 | .label VEC_CHKOUT = $ffc9 71 | .label VEC_CLRCHN = $ffcc 72 | .label VEC_CHRIN = $ffcf 73 | .label VEC_CHROUT = $ffd2 // https://www.c64-wiki.com/wiki/CHROUT 74 | .label VEC_LOAD = $ffd5 75 | .label VEC_SAVE = $ffd8 76 | .label VEC_SETTIM = $ffdb 77 | .label VEC_RDTIM = $ffde 78 | .label VEC_STOP = $ffe1 79 | .label VEC_GETIN = $ffe4 80 | .label VEC_CLALL = $ffe7 81 | .label VEC_UDTIM = $ffea 82 | .label VEC_SCREEN = $ffed 83 | .label VEC_PLOT = $fff0 // https://www.c64-wiki.com/wiki/PLOT_(KERNAL) 84 | .label VEC_IOBASE = $fff3 85 | } 86 | -------------------------------------------------------------------------------- /timers.asm: -------------------------------------------------------------------------------- 1 | *=* "Timer Routines" 2 | 3 | // 4 | // These routines can be used to create and fire single-shot and continuous timers. The UpdateTimers 5 | // routine should be called 60 times a second. 6 | // 7 | // Each timer is comprised of 8 bytes: 8 | // 9 | // 0 - enabled/disabled (1 = enabled) 10 | // 1 - single-shot/continuous (0 = single-shot) 11 | // 2,3 - the timer's current value 12 | // 4,5 - the timer's frequency (60 = 1 second) 13 | // 6,7 - the address of the routine to call when the timer fires 14 | // 15 | // The 8 timers are stored in memory 'c64lib_timers' 16 | // 17 | 18 | // 19 | // Clear memory used for timers 20 | // 21 | ClearTimers: 22 | FillMI($00, c64lib_timers, TIMER_STRUCT_BYTES) 23 | rts 24 | 25 | // 26 | // Create a timer that fires either on a schedule or as a single shot 27 | // 28 | // r0 frequency in 60ths of a second (60 = wait 1 second) 29 | // r1 location to call when timer fires 30 | // r2L single shot or continuous (0 = single shot, 1 = continuous) 31 | // r2H timer number (0 - 7) 32 | // r3L enabled or disabled (0 = disabled, 1 = enabled) 33 | // 34 | CreateTimer: 35 | lda r2H 36 | mult8 37 | tax 38 | stb r3L:c64lib_timers, x // enabled 39 | stb r2L:c64lib_timers + $01, x // type 40 | stb r0L:c64lib_timers + $02, x // current value 41 | stb r0H:c64lib_timers + $03, x 42 | stb r0L:c64lib_timers + $04, x // frequency 43 | stb r0H:c64lib_timers + $05, x 44 | stb r1L:c64lib_timers + $06, x // address 45 | stb r1H:c64lib_timers + $07, x 46 | 47 | rts 48 | 49 | 50 | // 51 | // Should be called every 1/60th of a second and will update timers and call timer targets 52 | // if required. 53 | // 54 | UpdateTimers: 55 | ldy #$00 56 | !loop: 57 | tya 58 | mult8 59 | sta r0L 60 | tax 61 | // Is the timer enabled? 62 | jne c64lib_timers, x:#ENABLE:!continue+ 63 | inx 64 | stb c64lib_timers, x:r3H 65 | inx 66 | stx r1H 67 | lda c64lib_timers, x // current value 68 | bne !return+ 69 | dec c64lib_timers + $01, x 70 | !return: 71 | dec c64lib_timers, x 72 | 73 | lda c64lib_timers, x 74 | bne !continue+ 75 | lda c64lib_timers + $01, x 76 | bne !continue+ 77 | 78 | // Counter has hit 0 - reset it 79 | inx 80 | inx 81 | stb c64lib_timers, x:r2L 82 | inx 83 | stb c64lib_timers, x:r2H 84 | 85 | lda r1H 86 | tax 87 | 88 | stb r2L:c64lib_timers, x 89 | inx 90 | stb r2H:c64lib_timers, x 91 | 92 | inx 93 | inx 94 | inx 95 | tya 96 | pha 97 | 98 | // Set up the vector to point at the timer's routine 99 | stb c64lib_timers, x:r2L 100 | inx 101 | stb c64lib_timers, x:r2H 102 | 103 | // Call the timer's routine 104 | jsr !dispatch+ 105 | jmp !done_dispatch+ 106 | 107 | !dispatch: 108 | jmp (r2) 109 | 110 | !done_dispatch: 111 | pla 112 | tay 113 | 114 | // If the timer is continuous, skip the disabling of it 115 | jeq r3H:#TIMER_CONTINUOUS:!continue+ 116 | 117 | // For single shot timers, disable it after the first execution 118 | ldx r0L 119 | stb #DISABLE:c64lib_timers, x 120 | 121 | !continue: 122 | iny 123 | cpy #$07 124 | bne !loop- 125 | 126 | rts 127 | 128 | // 129 | // Enable or disable a timer 130 | // 131 | // r2H the timer to control 132 | // r3L enable or disable (0 = disable, 1 = enable) 133 | // 134 | EnDisTimer: 135 | lda r2H 136 | mult8 137 | tax 138 | stb r3L:c64lib_timers, x 139 | rts 140 | -------------------------------------------------------------------------------- /sprites.asm: -------------------------------------------------------------------------------- 1 | *=* "Sprite Routines" 2 | 3 | // 4 | // These routines can be used to move and manipulate the sprites on the 64. 5 | // 6 | 7 | #if REGISTER_MODE 8 | 9 | // 10 | // Position a sprite anywhere on the screen 11 | // This variant uses hardware registers instead of pseudo registers 12 | // 13 | // X sprite number (0-7) 14 | // A,Y x pos (0-319) 15 | // $02 y pos (0-199) 16 | // 17 | PositionSprite: 18 | pha 19 | tya 20 | pha 21 | txa 22 | mult2 23 | tay 24 | lda $02 25 | sta vic.SP0Y, y 26 | pla 27 | sta $02 28 | pla 29 | sta r6L 30 | lda $02 31 | adc #$00 32 | sta r6H 33 | stb r6L:vic.SP0X, y 34 | ldx r3L 35 | lda BitMaskPow2, x 36 | eor #$ff 37 | and vic.MSIGX 38 | tay 39 | lda #$01 40 | and r6H 41 | beq !no_msb+ 42 | tya 43 | ora BitMaskPow2, x 44 | tay 45 | 46 | !no_msb: 47 | sty vic.MSIGX 48 | 49 | !return: 50 | rts 51 | 52 | // 53 | // Change one of a sprite's attributes 54 | // This variant uses hardware registers instead of pseudo registers 55 | // 56 | // X sprite number (0-7) 57 | // Y attribute to change (SPR_VISIBLE, SPR_X_EXPAND, SPR_Y_EXPAND, SPR_HMC, SPR_PRIORITY) 58 | // A value for the attribute 59 | // - Sprite visibility (SPR_SHOW, SPR_HIDE) 60 | // - X/Y expansion (SPR_NORMAL, SPR_EXPAND) 61 | // - Priority (SPR_FG, SPR_BG) 62 | // - Hires or Multicolor (SPR_HIRES, SPR_MULTICOLOR) 63 | // 64 | ChangeSpriteAttribute: 65 | pha 66 | lda BitMaskPow2, x 67 | sta $02 68 | pla 69 | tax 70 | lda $02 71 | cpx #ENABLE 72 | beq !set_flag+ 73 | eor #$ff 74 | and vic.SP0X, y 75 | jmp !clear_flag+ 76 | !set_flag: 77 | ora vic.SP0X, y 78 | !clear_flag: 79 | sta vic.SP0X, y 80 | 81 | rts 82 | 83 | #else 84 | 85 | // 86 | // Position a sprite anywhere on the screen 87 | // This variant uses the zeropage pseudo registers, which may not be compatible with BASIC or the Kernal 88 | // 89 | // r3L sprite number (0-7) 90 | // r4 x pos (0-319) 91 | // r5L y pos (0-199) 92 | // 93 | PositionSprite: 94 | lda r3L 95 | mult2 96 | tay 97 | stb r5L:vic.SP0Y, y 98 | stb r4L:r6L 99 | lda r4H 100 | adc #$00 101 | sta r6H 102 | stb r6L:vic.SP0X, y 103 | ldx r3L 104 | lda BitMaskPow2, x 105 | eor #$ff 106 | and vic.MSIGX 107 | tay 108 | lda #$01 109 | and r6H 110 | beq !no_msb+ 111 | tya 112 | ora BitMaskPow2, x 113 | tay 114 | 115 | !no_msb: 116 | sty vic.MSIGX 117 | 118 | !return: 119 | rts 120 | 121 | // 122 | // Change one of a sprite's attributes 123 | // This variant uses the zeropage pseudo registers, which may not be compatible with BASIC or the Kernal 124 | // 125 | // r3L sprite number (0-7) 126 | // r3H attribute to change (SPR_VISIBLE, SPR_X_EXPAND, SPR_Y_EXPAND, SPR_HMC, SPR_PRIORITY) 127 | // r4L value for the attribute 128 | // - Sprite visibility (SPR_SHOW, SPR_HIDE) 129 | // - X/Y expansion (SPR_NORMAL, SPR_EXPAND) 130 | // - Priority (SPR_FG, SPR_BG) 131 | // - Hires or Multicolor (SPR_HIRES, SPR_MULTICOLOR) 132 | // 133 | ChangeSpriteAttribute: 134 | ldy r3H 135 | ldx r3L 136 | lda BitMaskPow2, x 137 | ldx r4L 138 | cpx #ENABLE 139 | beq !set_flag+ 140 | eor #$ff 141 | and vic.SP0X, y 142 | jmp !clear_flag+ 143 | !set_flag: 144 | ora vic.SP0X, y 145 | !clear_flag: 146 | sta vic.SP0X, y 147 | 148 | rts 149 | 150 | #endif 151 | 152 | // 153 | // This allows us to translate sprite numbers into bit masks 154 | // 155 | BitMaskPow2: 156 | .byte %00000001 157 | .byte %00000010 158 | .byte %00000100 159 | .byte %00001000 160 | .byte %00010000 161 | .byte %00100000 162 | .byte %01000000 163 | .byte %10000000 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | _____ __ _ _ _ _ _ 2 | / ____|/ /| || | | | (_) | 3 | | | / /_| || |_| | _| |__ 4 | | | | '_ \__ _| | | | '_ \ 5 | | |___| (_) | | | | |____| | |_) | 6 | \_____\___/ |_| |______|_|_.__/ 7 | 8 | 9 | 10 | #### Introduction 11 | 12 | This is a collection of libraries for making C64 development more enjoyable using KickAssembler [http://theweb.dk/KickAssembler/Main.html#frontpage] 13 | 14 | It contains pseudocommands, macros, constants and functions. 15 | 16 | #### Using C64Lib 17 | 18 | To use from your own project, clone this repository and reference the cloned directory using the `-libdir` KickAssembler command line parameter. 19 | 20 | ```bash 21 | java –jar kickass.jar your_main.asm -libdir ~/Git/c64lib/ 22 | ``` 23 | 24 | You can also use my KickAssembler docker container: 25 | 26 | ```bash 27 | docker run -v ${PWD}:/workspace barrywalker71/kickassembler:latest /workspace/your_main.asm 28 | ``` 29 | 30 | Specify the location of the cloned directory in the `-libdir` parameter. In this example, it's `~/Git/c64lib`, but use whatever location you cloned the directory to. 31 | 32 | 33 | In your `your_main.asm` file, include either the `include_me_full.asm` or the `include_me_min.asm` files: 34 | 35 | ```asm 36 | #include "include_me_full.asm" 37 | ``` 38 | 39 | or 40 | 41 | ```asm 42 | #include "include_me_min.asm" 43 | ``` 44 | 45 | The `include_me_min.asm` file only brings in the pseudocommands, macros and constants. Inclding `include_me_full.asm` will also bring in sprites, timer and memory routines. 46 | 47 | You can also include individual features: 48 | 49 | - include_me_memory.asm 50 | - include_me_sprites.asm 51 | - include_me_timers.asm 52 | 53 | #### What's included? 54 | 55 | Bringing in the `include_me_min.asm` file gives you access to: 56 | 57 | - All of the Kernal constants. (`kernal` namespace) 58 | - All of the BASIC constants. (`basic` namespace) 59 | - All of the VIC-II constants. (`vic` namespace) 60 | - The constants for the 32 addresses of the twin CIAs (CIA1 and CIA2). (`cia` namespace) 61 | - 29 constants for the SID chip. (`sid` namespace) 62 | - A bunch of pseudocommands to make your code easier to write and more consistent. 63 | - A bunch of macros to further ease development. 64 | - Constants used by the library that can also be used in your own code. 65 | - 8 16-bit zeropage pseudo registers modeled after the way the GEOS guys did it (r0, r0L, r0H, etc). NOTE: If you're using BASIC, these registers will conflict with addresses in use by it. 66 | 67 | Using `include_me_full.asm` brings in these additional features: 68 | 69 | - Sprite routines 70 | - Memory fill and copy routines 71 | - Timer routines for single-shot and continuous timers 72 | 73 | __NOTE__ There is a hardware register version of the sprite routines if you can't spare, or can't use ZP memory. You can include `include_me_sprites_r.asm` to use it. The sprite macros below will automatically adapt and are called the same way as the pseudo register versions. 74 | 75 | I will work on making hardware register versions of the other routines as well. 76 | 77 | #### What's available for sprites? 78 | 79 | Start by including `include_me_sprites.asm` in your project. If you need access to other routines, include `include_me_full.asm` 80 | 81 | ##### Enabling a sprite 82 | 83 | ```asm 84 | SpriteEnable(0, TRUE) 85 | ``` 86 | 87 | or 88 | 89 | ```asm 90 | SpriteEnable(0, FALSE) 91 | ``` 92 | 93 | ##### Setting a sprite's priority 94 | 95 | ```asm 96 | SpritePriority(0, SPR_BG) 97 | ``` 98 | 99 | or 100 | 101 | ```asm 102 | SpritePriority(0, SPR_FG) 103 | ``` 104 | 105 | ##### Setting a sprite's X/Y expand 106 | 107 | ```asm 108 | SpriteXExpand(0, SPR_NORMAL) 109 | ``` 110 | 111 | or 112 | 113 | ```asm 114 | SpriteYExpand(0, SPR_EXPAND) 115 | ``` 116 | 117 | ##### Setting a sprite's color mode 118 | 119 | ```asm 120 | SpriteColorMode(0, SPR_HIRES) 121 | ``` 122 | 123 | or 124 | 125 | ```asm 126 | SpriteColorMode(0, SPR_MULTICOLOR) 127 | ``` 128 | 129 | ##### To move a sprite 130 | 131 | ```asm 132 | PositionSprite(0, spr_x_pos, spr_y_pos) 133 | ``` 134 | 135 | Store the sprite's x position (0-319) in the word at `spr_x_pos` and the sprite's y position (0-255) in the byte at `spr_y_pos`. 136 | 137 | This routine automatically handles the sprite's MSB when the x position is > 255 138 | -------------------------------------------------------------------------------- /vic.asm: -------------------------------------------------------------------------------- 1 | *=* "VIC Routines" 2 | 3 | // 4 | // These routines can be used to manage the current VIC bank as well as character 5 | // memory & screen memory 6 | // 7 | 8 | #if REGISTER_MODE 9 | 10 | // 11 | // Set the VIC bank, screen memory and character memory 12 | // This is the hadrware register variant 13 | // 14 | // X bank number (0-3) (multiples of 16k) 15 | // A character memory within the bank (0-7) (multiples of 2k) 16 | // Y screen memory within the bank (0-15) (multiples of 1k) 17 | // 18 | SetVICBank: 19 | mult2 20 | sta $02 21 | lda cia.CI2PRA 22 | and #cia.VIC_BANK_REVERSE_MASK 23 | ora BankLookup, x 24 | sta cia.CI2PRA 25 | lda vic.VMCSB 26 | and #vic.CHAR_MEM_REV_POINTER_MASK 27 | ora $02 28 | sta vic.VMCSB 29 | tya 30 | mult4 31 | mult4 32 | tay 33 | sty $02 34 | lda vic.VMCSB 35 | and #vic.SCREEN_MEM_REV_POINTER_MASK 36 | ora $02 37 | sta vic.VMCSB 38 | jmp UpdateBaseLocations 39 | 40 | #else 41 | 42 | // 43 | // Set the VIC bank, screen memory and character memory 44 | // This is the pseudo register variant 45 | // 46 | // r0L bank number (0-3) (multiples of 16k) 47 | // r0H character memory within the bank (0-7) (multiples of 2k) 48 | // r1L screen memory within the bank (0-15) (multiples of 1k) 49 | // 50 | SetVICBank: 51 | asl r0H 52 | lda r0L 53 | tax 54 | lda cia.CI2PRA 55 | and #cia.VIC_BANK_REVERSE_MASK 56 | ora BankLookup, x 57 | sta cia.CI2PRA 58 | lda vic.VMCSB 59 | and #vic.CHAR_MEM_REV_POINTER_MASK 60 | ora r0H 61 | sta vic.VMCSB 62 | asl r1L 63 | asl r1L 64 | asl r1L 65 | asl r1L 66 | lda vic.VMCSB 67 | and #vic.SCREEN_MEM_REV_POINTER_MASK 68 | ora r1L 69 | sta vic.VMCSB 70 | jmp UpdateBaseLocations 71 | 72 | #endif 73 | 74 | // 75 | // Updates the 4 base locations below based on how the VIC bank, 76 | // screen memory and character memory are configured. 77 | // 78 | UpdateBaseLocations: 79 | lda cia.CI2PRA 80 | and #cia.VIC_BANK_MASK 81 | eor #cia.VIC_BANK_MASK 82 | mult16 83 | sta vic.BankMemoryBase + $01 84 | lda vic.VMCSB 85 | pha 86 | and #vic.SCREEN_MEM_POINTER_MASK 87 | div4 88 | clc 89 | adc vic.BankMemoryBase + $01 90 | sta vic.ScreenMemoryBase + $01 91 | clc 92 | adc #$03 93 | sta vic.SpritePointerBase + $01 94 | pla 95 | and #vic.CHAR_MEM_POINTER_MASK 96 | mult4 97 | clc 98 | adc vic.BankMemoryBase + $01 99 | sta vic.CharacterMemoryBase + $01 100 | 101 | rts 102 | 103 | BankLookup: 104 | .byte cia.BANK_0_MASK 105 | .byte cia.BANK_1_MASK 106 | .byte cia.BANK_2_MASK 107 | .byte cia.BANK_3_MASK 108 | 109 | /* 110 | 111 | Constants for the C64's Video Interface Chip (VIC-II) 112 | 113 | */ 114 | .namespace vic { 115 | // 116 | // 16-bit pointer to the start of the VIC's memory in the current bank. 117 | // Since the VIC can only see 16k, there are 4 possible banks in the C64. 118 | // 119 | // $0000 = BANK 0 120 | // $4000 = BANK 1 121 | // $8000 = BANK 2 122 | // $C000 = BANK 3 123 | // 124 | BankMemoryBase: 125 | .word DEFAULT_VIC_BANK 126 | 127 | // 128 | // This location contains a 16-bit pointer to the start of screen memory 129 | // in the current bank. It defaults to $0400, which is the C64's power 130 | // on default in bank 0. When you call SetVICBank, it updates this pointer. 131 | // 132 | ScreenMemoryBase: 133 | .word DEFAULT_SCREEN_BASE 134 | 135 | // 136 | // 16-bit pointer to the start of the sprite pointers. This is always 1016 137 | // bytes after the start of screen memory. 138 | // 139 | SpritePointerBase: 140 | .word DEFAULT_SPRITE_POINTER_BASE 141 | 142 | // 143 | // This location contains a 16-bit pointer to the start of character memory 144 | // in the current bank. It defaults to $1000, which is the C64's power 145 | // on default in bank 0. When you call SetVICBank, it updates this pointer. 146 | CharacterMemoryBase: 147 | .word DEFAULT_CHAR_BASE 148 | 149 | .label SCREEN_MEM_POINTER_MASK = %11110000 150 | .label SCREEN_MEM_REV_POINTER_MASK = %00001111 151 | .label CHAR_MEM_POINTER_MASK = %00001110 152 | .label CHAR_MEM_REV_POINTER_MASK = %11110001 153 | 154 | .label DEFAULT_VIC_BANK = $0000 155 | .label DEFAULT_SCREEN_BASE = $0400 156 | .label DEFAULT_CHAR_BASE = $1000 157 | .label DEFAULT_SPRITE_POINTER_BASE = $07f8 158 | 159 | // The 47 VIC-II registers 160 | .label SP0X = $d000 161 | .label SP0Y = $d001 162 | .label SP1X = $d002 163 | .label SP1Y = $d003 164 | .label SP2X = $d004 165 | .label SP2Y = $d005 166 | .label SP3X = $d006 167 | .label SP3Y = $d007 168 | .label SP4X = $d008 169 | .label SP4Y = $d009 170 | .label SP5X = $d00a 171 | .label SP5Y = $d00b 172 | .label SP6X = $d00c 173 | .label SP6Y = $d00d 174 | .label SP7X = $d00e 175 | .label SP7Y = $d00f 176 | .label MSIGX = $d010 // MSB of sprite x positions 177 | .label SCROLY = $d011 178 | .label RASTER = $d012 179 | .label LPENX = $d013 180 | .label LPENY = $d014 181 | .label SPENA = $d015 // Sprite enable 182 | .label SCROLX = $d016 183 | .label YXPAND = $d017 184 | .label VMCSB = $d018 185 | .label VICIRQ = $d019 186 | .label IRQMSK = $d01a 187 | .label SPBGPR = $d01b 188 | .label SPMC = $d01c 189 | .label XXPAND = $d01d 190 | .label SPSPCL = $d01e 191 | .label SPBGCL = $d01f 192 | .label EXTCOL = $d020 193 | .label BGCOL0 = $d021 194 | .label BGCOL1 = $d022 195 | .label BGCOL2 = $d023 196 | .label BGCOL3 = $d024 197 | .label SPMC0 = $d025 198 | .label SPMC1 = $d026 199 | .label SP0COL = $d027 200 | .label SP1COL = $d028 201 | .label SP2COL = $d029 202 | .label SP3COL = $d02a 203 | .label SP4COL = $d02b 204 | .label SP5COL = $d02c 205 | .label SP6COL = $d02d 206 | .label SP7COL = $d02e 207 | 208 | .label COLCLK = $d81a 209 | } 210 | -------------------------------------------------------------------------------- /basic.asm: -------------------------------------------------------------------------------- 1 | .namespace basic { 2 | // BASIC keyword tokens 3 | .label TOK_END = $80 4 | .label TOK_FOR = $81 5 | .label TOK_NEXT = $82 6 | .label TOK_DATA = $83 7 | .label TOK_INPUTN = $84 8 | .label TOK_INPUT = $85 9 | .label TOK_DIM = $86 10 | .label TOK_READ = $87 11 | .label TOK_LET = $88 12 | .label TOK_GOTO = $89 13 | .label TOK_RUN = $8a 14 | .label TOK_IF = $8b 15 | .label TOK_RESTORE = $8c 16 | .label TOK_GOSUB = $8d 17 | .label TOK_RETURN = $8e 18 | .label TOK_REM = $8f 19 | .label TOK_STOP = $90 20 | .label TOK_ON = $91 21 | .label TOK_WAIT = $92 22 | .label TOK_LOAD = $93 23 | .label TOK_SAVE = $94 24 | .label TOK_VERIFY = $95 25 | .label TOK_DEF = $96 26 | .label TOK_POKE = $97 27 | .label TOK_PRINTN = $98 28 | .label TOK_PRINT = $99 29 | .label TOK_CONT = $9a 30 | .label TOK_LIST = $9b 31 | .label TOK_CLR = $9c 32 | .label TOK_CMD = $9d 33 | .label TOK_SYS = $9e 34 | .label TOK_OPEN = $9f 35 | .label TOK_CLOSE = $a0 36 | .label TOK_GET = $a1 37 | .label TOK_NEW = $a2 38 | 39 | // BASIC function tokens 40 | .label TOK_SGN = $b4 41 | .label TOK_INT = $b5 42 | .label TOK_ABS = $b6 43 | .label TOK_USR = $b7 44 | .label TOK_FRE = $b8 45 | .label TOK_POS = $b9 46 | .label TOK_SQR = $ba 47 | .label TOK_RND = $bb 48 | .label TOK_LOG = $bc 49 | .label TOK_EXP = $bd 50 | .label TOK_COS = $be 51 | .label TOK_SIN = $bf 52 | .label TOK_TAN = $c0 53 | .label TOK_ATN = $c1 54 | .label TOK_PEEK = $c2 55 | .label TOK_LEN = $c3 56 | .label TOK_STR = $c4 57 | .label TOK_VAL = $c5 58 | .label TOK_ASC = $c6 59 | .label TOK_CHR = $c7 60 | .label TOK_LEFT = $c8 61 | .label TOK_RIGHT = $c9 62 | .label TOK_MID = $ca 63 | 64 | // BASIC math operator tokens 65 | .label TOK_ADD = $aa 66 | .label TOK_SUB = $ab 67 | .label TOK_MULT = $ac 68 | .label TOK_DIV = $ad 69 | .label TOK_EXPN = $ae 70 | .label TOK_AND = $af 71 | .label TOK_OR = $b0 72 | .label TOK_GT = $b1 73 | .label TOK_EQ = $b2 74 | .label TOK_LT = $b3 75 | 76 | // BASIC misc tokens 77 | .label TOK_TAB = $a3 78 | .label TOK_TO = $a4 79 | .label TOK_FN = $a5 80 | .label TOK_SPC = $a6 81 | .label TOK_THEN = $a7 82 | .label TOK_NOT = $a8 83 | .label TOK_STEP = $a9 84 | 85 | .label CBMBASIC = $a004 86 | .label STMDSP = $a00c 87 | .label OPTAB = $a080 88 | .label RESLST = $a09e 89 | .label ERRTAB = $a19e 90 | .label FNDFOR = $a38a 91 | .label BLTU = $a3b8 92 | .label GETSTK = $a3fb 93 | .label REASON = $a408 94 | .label OMERR = $a435 95 | .label ERROR = $a437 96 | .label READY = $a474 97 | .label MAIN = $a480 98 | .label MAIN1 = $a49c 99 | .label LINKPRG = $a533 100 | .label INLIN = $a560 101 | .label CRUNCH = $a579 102 | .label FINDLN = $a613 103 | .label SCRTCH = $a642 104 | .label CLEAR = $a65e 105 | .label RUNC = $a68e 106 | .label LIST = $a69c 107 | .label QPLOP = $a717 108 | .label FOR = $a742 109 | .label NEWSTT = $a7ae 110 | .label GONE = $a7e4 111 | .label RESTOR = $a81d 112 | .label END = $a831 113 | .label CONT = $a857 114 | .label RUN = $a871 115 | .label GOSUB = $a883 116 | .label GOTO = $a8a0 117 | .label RETURN = $a8d2 118 | .label DATA = $a8f8 119 | .label DATAN = $a906 120 | .label IF = $a928 121 | .label REM = $a93b 122 | .label ONGOTO = $a94b 123 | .label LINGET = $a96b 124 | .label LET = $a9a5 125 | .label PRINTN = $aa80 126 | .label CMD = $aa86 127 | .label PRINT = $aaa0 128 | .label STROUT = $ab1e 129 | .label DOAGIN = $ab4d 130 | .label GET = $ab7b 131 | .label INPUTN = $aba5 132 | .label INPUT = $abbf 133 | .label READ = $ac06 134 | .label EXIGNT = $acfc 135 | .label NEXT = $ad1e 136 | .label FRMNUM = $ad8a 137 | .label FRMEVAL = $ad9e 138 | .label EVAL = $ae83 139 | .label PIVAL = $aea8 140 | .label PARCHK = $aef1 141 | .label CHKCLS = $aef7 142 | .label CHKOPN = $aefa 143 | .label CHKCOM = $aeff 144 | .label SNERR = $af08 145 | .label ISVAR = $af2b 146 | .label ISFUN = $afa7 147 | .label OROP = $afe6 148 | .label ANDOP = $afe9 149 | .label DORE1 = $b016 150 | .label DIM = $b018 151 | .label PTRGET = $b08b 152 | .label NOTFNS = $b11d 153 | .label FINPTR = $b185 154 | .label ARYGET = $b194 155 | .label N32768 = $b1a5 156 | .label INTIDX = $b1b2 157 | .label AYINT = $b1bf 158 | .label ISARY = $b1d1 159 | .label BSERR = $b245 160 | .label FCERR = $b248 161 | .label UMULT = $b34c 162 | .label FRE = $b37d 163 | .label GIVAYF = $b391 164 | .label POS = $b39e 165 | .label ERRDIR = $b3a6 166 | .label DEF = $b3b3 167 | .label GETFNM = $b3e1 168 | .label FNDOER = $b3f4 169 | .label STRD = $b465 170 | .label STRLIT = $b487 171 | .label GETSPA = $b4f4 172 | .label GARBAG = $b526 173 | .label CAT = $b63d 174 | .label MOVINS = $b67a 175 | .label FRESTR = $b6a3 176 | .label FRETMS = $b6db 177 | .label CHRD = $b6ec 178 | .label LEFTD = $b700 179 | .label RIGHTD = $b72c 180 | .label MIDD = $b737 181 | .label PREAM = $b761 182 | .label LEN = $b77c 183 | .label ASC = $b78b 184 | .label GETBYTC = $b79b 185 | .label VAL = $b7ad 186 | .label GETNUM = $b7eb 187 | .label GETADR = $b7f7 188 | .label PEEK = $b80d 189 | .label POKE = $b824 190 | .label FUWAIT = $b82d 191 | .label FADDH = $b849 192 | .label FSUB = $b850 193 | .label FSUBT = $b853 194 | .label FADD = $b867 195 | .label FADDT = $b86a 196 | .label FADD4 = $b8a7 197 | .label NORMAL = $b8fe 198 | .label NEGFAC = $b947 199 | .label OVERR = $b97e 200 | .label MULSHF = $b983 201 | .label FONE = $b9bc 202 | .label LOGCN2 = $b9c1 203 | .label LOG = $b9ea 204 | .label FMULT = $ba28 205 | .label MLTPLY = $ba59 206 | .label CONUPK = $ba8c 207 | .label MULDIV = $bab7 208 | .label MLDVEX = $bad4 209 | .label MUL10 = $bae2 210 | .label TENC = $baf9 211 | .label DIV10 = $bafe 212 | .label FDIV = $bb0f 213 | .label FDIVT = $bb12 214 | .label MOVFM = $bba2 215 | .label MOV2F = $bbc7 216 | .label MOVFA = $bbfc 217 | .label MOVAF = $bc0c 218 | .label MOVEF = $bc0f 219 | .label ROUND = $bc1b 220 | .label SIGN = $bc2b 221 | .label SGN = $bc39 222 | .label ABS = $bc58 223 | .label FCOMP = $bc5b 224 | .label QINT = $bc9b 225 | .label INT = $bccc 226 | .label FIN = $bcf3 227 | .label FINLOG = $bd7e 228 | .label NO999 = $bdb3 229 | .label INPRT = $bdc0 230 | .label LINPRT = $bdcd 231 | .label FOUT = $bddd 232 | .label FHALF = $bf11 233 | .label FOUTBL = $bf1c 234 | .label FDCEND = $bf3a 235 | .label SQR = $bf71 236 | .label FPWRT = $bf7b 237 | .label NEGOP = $bfb4 238 | .label EXPCON = $bfbf 239 | .label EXP = $bfed 240 | } 241 | -------------------------------------------------------------------------------- /macros.asm: -------------------------------------------------------------------------------- 1 | #importonce 2 | 3 | /* 4 | Push everything onto the stack. This lets us do whatever we want with the 5 | registers and put it back the way it was before returning. This is mostly 6 | used by the raster interrupt routine, but can be used anywhere. 7 | */ 8 | .macro PushStack() { 9 | php 10 | pha 11 | txa 12 | pha 13 | tya 14 | pha 15 | } 16 | 17 | /* 18 | Sets the registers and processor status back to the way they were 19 | */ 20 | .macro PopStack() { 21 | pla 22 | tay 23 | pla 24 | tax 25 | pla 26 | plp 27 | } 28 | 29 | /* 30 | Toggle a flag 31 | */ 32 | .macro Toggle(address) { 33 | lda address 34 | eor #ENABLE 35 | sta address 36 | } 37 | 38 | /* 39 | Disable a flag 40 | */ 41 | .macro Disable(address) { 42 | stb #DISABLE:address 43 | } 44 | 45 | /* 46 | Enable a flag 47 | */ 48 | .macro Enable(address) { 49 | stb #ENABLE:address 50 | } 51 | 52 | /* 53 | Copy the contents of the source address to the target address 54 | */ 55 | .macro CpyW(source, target) { 56 | stb source:target 57 | stb source + $01:target + $01 58 | } 59 | 60 | /* 61 | Load a word into a target address 62 | */ 63 | .macro CpyWI(word, target) { 64 | stb #word:target + $01 66 | } 67 | 68 | #if MEMORY 69 | 70 | /* 71 | Copy a block of memory 72 | */ 73 | .macro CpyM(source, target, length) { 74 | CpyWI(source, r0) 75 | CpyWI(target, r1) 76 | CpyWI(length, r2) 77 | 78 | jsr CopyMemory 79 | } 80 | 81 | /* 82 | Fill a chunk of memory with an immediate value 83 | */ 84 | .macro FillMI(value, target, length) { 85 | stb #value:r0L 86 | CpyWI(target, r1) 87 | CpyWI(length, r2) 88 | 89 | jsr FillMemory 90 | } 91 | 92 | /* 93 | Fill a chunk of memory with a value from memory 94 | */ 95 | .macro FillM(value, target, length) { 96 | stb value:r0L 97 | CpyWI(target, r1) 98 | CpyWI(length, r2) 99 | 100 | jsr FillMemory 101 | } 102 | 103 | #endif 104 | 105 | /* 106 | Do a 16-bit increment of a memory location 107 | */ 108 | .macro Inc16(word) { 109 | inc word 110 | bne !return+ 111 | inc word + $01 112 | !return: 113 | } 114 | 115 | /* 116 | Do a 16-bit decrement of a memory location 117 | */ 118 | .macro Dec16(word) { 119 | lda word 120 | bne !return+ 121 | dec word + $01 122 | !return: 123 | dec word 124 | } 125 | 126 | /* 127 | Compare 2 bytes 128 | */ 129 | .macro CmpB(byte1, byte2) { 130 | lda byte1 131 | cmp byte2 132 | } 133 | 134 | /* 135 | Compare a byte in memory with an immediate value 136 | */ 137 | .macro CmpBI(byte1, byte2) { 138 | lda byte1 139 | cmp #byte2 140 | } 141 | 142 | /* 143 | Compare a word in memory to an immediate word value 144 | */ 145 | .macro CmpWI(word1, word2) { 146 | CmpBI(word1 + $01, >word2) 147 | bne !return+ 148 | CmpBI(word1 + $00, No keyboard activity is detected. 100 | A = #$02 => Control Port #1 Activity is detected. 101 | A = #$03 => Key Shadowing / Ghosting is detected. 102 | A = #$04 => 2 or 3 new keys is detected within one scan 103 | A = #$05 => Awaiting "No Activity" state 104 | - Clear = Valid input 105 | A = #$ff => No new Alphanumeric Keys detected (some key(s) being held down AND/OR some Non-Alphanumeric key is causing valid return). 106 | A <> #$ff => New Alphanumeric Key returned. Non-Alphanumeric keys may also be returned in X or Y Register 107 | 108 | Issues/ToDo: 109 | ~~~~~~~~~~~~ 110 | - None 111 | 112 | 113 | Improvements: 114 | ~~~~~~~~~~~~~ 115 | - Replace the subroutine with a pseudocommand and account for speedcode parameter (Memory vs. Cycles). 116 | - Shorten the routine / Optimize if possible. 117 | 118 | 119 | History: 120 | ~~~~~~~~ 121 | V2.5 - New test tool. 122 | Added return of error codes. 123 | Fixed a bug causing Buffer Overflow. 124 | Fixed a bug in Non Alphanumerical Flags from 2.0. 125 | V2.1 - Shortened the source by adding .for loops & Updated the header and some comments. 126 | Added "simultaneous keypress" check. 127 | V2.0 - Added return of non-Alphanumeric keys into X & Y-Registers. 128 | Small optimizations here and there. 129 | V1.1 - Unrolled code to make it faster and optimized other parts of it. 130 | Removed SHIFT LOCK scanning. 131 | V1.0 - First Working Version along with test tool. 132 | 133 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 134 | 135 | .pc = * "Keyboard Scan Routine" 136 | 137 | 138 | // ZERO PAGE Varibles 139 | .const ScanResult = $50 // 8 bytes 140 | .const BufferNew = $58 // 3 bytes 141 | .const KeyQuantity = $5b // 1 byte 142 | .const NonAlphaFlagX = $5c // 1 byte 143 | .const NonAlphaFlagY = $5d // 1 byte 144 | .const TempZP = $5e // 1 byte 145 | .const SimultaneousKeys = $5f // 1 byte 146 | 147 | // Operational Variables 148 | .var MaxKeyRollover = 3 149 | 150 | Keyboard: 151 | { 152 | jmp Main 153 | 154 | 155 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 156 | // Routine for Scanning a Matrix Row 157 | 158 | KeyInRow: 159 | asl 160 | bcs *+5 161 | jsr KeyFound 162 | .for (var i = 0 ; i < 7 ; i++) { 163 | inx 164 | asl 165 | bcs *+5 166 | jsr KeyFound 167 | } 168 | rts 169 | 170 | 171 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 172 | // Routine for handling: Key Found 173 | 174 | KeyFound: 175 | stx TempZP 176 | dec KeyQuantity 177 | bmi OverFlow 178 | ldy KeyTable,x 179 | ldx KeyQuantity 180 | sty BufferNew,x 181 | ldx TempZP 182 | rts 183 | 184 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 185 | // Routine for handling: Overflow 186 | 187 | OverFlow: 188 | pla // Dirty hack to handle 2 layers of JSR 189 | pla 190 | pla 191 | pla 192 | // Don't manipulate last legal buffer as the routine will fix itself once it gets valid input again. 193 | lda #$03 194 | sec 195 | rts 196 | 197 | 198 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 199 | // Exit Routine for: No Activity 200 | 201 | NoActivityDetected: 202 | // Exit With A = #$01, Carry Set & Reset BufferOld. 203 | lda #$00 204 | sta SimultaneousAlphanumericKeysFlag // Clear the too many keys flag once a "no activity" state is detected. 205 | stx BufferOld 206 | stx BufferOld+1 207 | stx BufferOld+2 208 | sec 209 | lda #$01 210 | rts 211 | 212 | 213 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 214 | // Exit Routine for Control Port Activity 215 | 216 | ControlPort: 217 | // Exit with A = #$02, Carry Set. Keep BufferOld to verify input after Control Port activity ceases 218 | sec 219 | lda #$02 220 | rts 221 | 222 | 223 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 224 | // Configure Data Direction Registers 225 | Main: 226 | ldx #$ff 227 | stx $dc02 // Port A - Output 228 | ldy #$00 229 | sty $dc03 // Port B - Input 230 | 231 | 232 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 233 | // Check for Port Activity 234 | 235 | sty $dc00 // Connect all Keyboard Rows 236 | cpx $dc01 237 | beq NoActivityDetected 238 | 239 | lda SimultaneousAlphanumericKeysFlag 240 | beq !+ 241 | // Waiting for all keys to be released before accepting new input. 242 | lda #$05 243 | sec 244 | rts 245 | !: 246 | 247 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 248 | // Check for Control Port #1 Activity 249 | 250 | stx $dc00 // Disconnect all Keyboard Rows 251 | cpx $dc01 // Only Control Port activity will be detected 252 | bne ControlPort 253 | 254 | 255 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 256 | // Scan Keyboard Matrix 257 | 258 | lda #%11111110 259 | sta $dc00 260 | ldy $dc01 261 | sty ScanResult+7 262 | sec 263 | .for (var i = 6 ; i > -1 ; i--) { 264 | rol 265 | sta $dc00 266 | ldy $dc01 267 | sty ScanResult+i 268 | } 269 | 270 | 271 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 272 | // Check for Control Port #1 Activity (again) 273 | 274 | stx $dc00 // Disconnect all Keyboard Rows 275 | cpx $dc01 // Only Control Port activity will be detected 276 | bne ControlPort 277 | 278 | 279 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 280 | // Initialize Buffer, Flags and Max Keys 281 | 282 | // Reset current read buffer 283 | stx BufferNew 284 | stx BufferNew+1 285 | stx BufferNew+2 286 | 287 | // Reset Non-AlphaNumeric Flag 288 | inx 289 | stx NonAlphaFlagY 290 | 291 | // Set max keys allowed before ignoring result 292 | lda #MaxKeyRollover 293 | sta KeyQuantity 294 | 295 | // Counter to check for simultaneous alphanumeric key-presses 296 | lda #$fe 297 | sta SimultaneousKeys 298 | 299 | 300 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 301 | // Check and flag Non Alphanumeric Keys 302 | 303 | lda ScanResult+6 304 | eor #$ff 305 | and #%10000000 // Left Shift 306 | lsr 307 | sta NonAlphaFlagY 308 | lda ScanResult+0 309 | eor #$ff 310 | and #%10100100 // RUN STOP - C= - CTRL 311 | ora NonAlphaFlagY 312 | sta NonAlphaFlagY 313 | lda ScanResult+1 314 | eor #$ff 315 | and #%00011000 // Right SHIFT - CLR HOME 316 | ora NonAlphaFlagY 317 | sta NonAlphaFlagY 318 | 319 | lda ScanResult+7 // The rest 320 | eor #$ff 321 | sta NonAlphaFlagX 322 | 323 | 324 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 325 | // Check for pressed key(s) 326 | 327 | lda ScanResult+7 328 | cmp #$ff 329 | beq *+5 330 | jsr KeyInRow 331 | .for (var i = 6 ; i > -1 ; i--) { 332 | ldx #[7-i]*8 333 | lda ScanResult+i 334 | beq *+5 335 | jsr KeyInRow 336 | } 337 | 338 | 339 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 340 | // Key Scan Completed 341 | 342 | // Put any new key (not in old scan) into buffer 343 | ldx #MaxKeyRollover-1 344 | !: lda BufferNew,x 345 | cmp #$ff 346 | beq Exist // Handle 'null' values 347 | cmp BufferOld 348 | beq Exist 349 | cmp BufferOld+1 350 | beq Exist 351 | cmp BufferOld+2 352 | beq Exist 353 | // New Key Detected 354 | inc BufferQuantity 355 | ldy BufferQuantity 356 | sta Buffer,y 357 | // Keep track of how many new Alphanumeric keys are detected 358 | inc SimultaneousKeys 359 | beq TooManyNewKeys 360 | Exist: 361 | dex 362 | bpl !- 363 | 364 | // Anything in Buffer? 365 | ldy BufferQuantity 366 | bmi BufferEmpty 367 | // Yes: Then return it and tidy up the buffer 368 | dec BufferQuantity 369 | lda Buffer 370 | ldx Buffer+1 371 | stx Buffer 372 | ldx Buffer+2 373 | stx Buffer+1 374 | jmp Return 375 | 376 | BufferEmpty: // No new Alphanumeric keys to handle. 377 | lda #$ff 378 | 379 | Return: // A is preset 380 | clc 381 | // Copy BufferNew to BufferOld 382 | ldx BufferNew 383 | stx BufferOld 384 | ldx BufferNew+1 385 | stx BufferOld+1 386 | ldx BufferNew+2 387 | stx BufferOld+2 388 | // Handle Non Alphanumeric Keys 389 | ldx NonAlphaFlagX 390 | ldy NonAlphaFlagY 391 | rts 392 | 393 | TooManyNewKeys: 394 | sec 395 | lda #$ff 396 | sta BufferQuantity 397 | sta SimultaneousAlphanumericKeysFlag 398 | lda #$04 399 | rts 400 | 401 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 402 | KeyTable: 403 | .byte $ff, $ff, $ff, $ff, $ff, $ff, $ff, $ff // CRSR DOWN, F5, F3, F1, F7, CRSR RIGHT, RETURN, INST DEL 404 | .byte $ff, $05, $13, $1a, $34, $01, $17, $33 // LEFT SHIFT, "E", "S", "Z", "4", "A", "W", "3" 405 | .byte $18, $14, $06, $03, $36, $04, $12, $35 // "X", "T", "F", "C", "6", "D", "R", "5" 406 | .byte $16, $15, $08, $02, $38, $07, $19, $37 // "V", "U", "H", "B", "8", "G", "Y", "7" 407 | .byte $0e, $0f, $0b, $0d, $30, $0a, $09, $39 // "N", "O" (Oscar), "K", "M", "0" (Zero), "J", "I", "9" 408 | .byte $2c, $00, $3a, $2e, $2d, $0c, $10, $2b // ",", "@", ":", ".", "-", "L", "P", "+" 409 | .byte $2f, $1e, $3d, $ff, $ff, $3b, $2a, $1c // "/", "^", "=", RIGHT SHIFT, HOME, ";", "*", "£" 410 | .byte $ff, $11, $ff, $20, $32, $ff, $1f, $31 // RUN STOP, "Q", "C=" (CMD), " " (SPC), "2", "CTRL", "<-", "1" 411 | 412 | BufferOld: 413 | .byte $ff, $ff, $ff 414 | 415 | Buffer: 416 | .byte $ff, $ff, $ff, $ff 417 | 418 | BufferQuantity: 419 | .byte $ff 420 | 421 | SimultaneousAlphanumericKeysFlag: 422 | .byte $00 423 | } 424 | -------------------------------------------------------------------------------- /tests.yaml: -------------------------------------------------------------------------------- 1 | init: 2 | load: 3 | unit_tests: 4 | program: /code/include_me_full.prg 5 | tests: 6 | - name: memory-fill 7 | description: Fill an uneven block of memory 8 | set_memory: 9 | - description: Store $BD which is a weird number 10 | address: "{r0L}" 11 | byte_value: "$bd" 12 | - description: Target memory 13 | address: "{r1}" 14 | word_value: "$1234" 15 | - description: Store 300 bytes 16 | address: "{r2}" 17 | word_value: "$12c" 18 | jump_address: "{FillMemory}" 19 | assert: 20 | - description: Memory filled correctly 21 | type: memory_block 22 | address: "$1234" 23 | byte_value: "$bd" 24 | byte_count: "$12c" 25 | 26 | - name: memory-copy-kernal-to-userspace 27 | description: Copy kernal to lower memory 28 | set_memory: 29 | - description: Source Address 30 | address: "{r0}" 31 | word_value: "$e000" 32 | - description: Target Address 33 | address: "{r1}" 34 | word_value: "$4000" 35 | - description: 8k 36 | address: "{r2}" 37 | word_value: "$2000" 38 | jump_address: "{CopyMemory}" 39 | assert: 40 | - description: Memory copied successfully 41 | type: memory_block_compare 42 | address: "$e000" 43 | target: "$4000" 44 | byte_count: "$2000" 45 | - description: Make sure performance is acceptable 46 | type: cycle_count 47 | cycle_count: 131721 48 | op: lt 49 | 50 | - name: sprites-enable-x-expand-correctly 51 | description: Enable horizontal expansion of a sprite 52 | set_memory: 53 | - description: Sprite 1 54 | address: "{r3L}" 55 | byte_value: "$01" 56 | - description: The horizontal expansion attribute 57 | address: "{r3H}" 58 | byte_value: "{SPR_X_EXPAND}" 59 | - description: Set to True 60 | address: "{r4L}" 61 | byte_value: "{SPR_EXPAND}" 62 | - description: Enable X expansion for Sprite 7 63 | address: "{vic.XXPAND}" 64 | byte_value: "$80" 65 | jump_address: "{ChangeSpriteAttribute}" 66 | assert: 67 | - description: Sprite 1's horizontal expansion is set to True 68 | type: memory_test 69 | address: "{vic.XXPAND}" 70 | op: eq 71 | byte_value: "$82" 72 | 73 | - name: sprites-disable-x-expand-correctly 74 | description: Disable horizontal expansion of a sprite 75 | set_memory: 76 | - description: Sprite 5 77 | address: "{r3L}" 78 | byte_value: "$05" 79 | - description: The horizontal expansion attribute 80 | address: "{r3H}" 81 | byte_value: "{SPR_X_EXPAND}" 82 | - description: Set to False 83 | address: "{r4L}" 84 | byte_value: "{SPR_NORMAL}" 85 | - description: Enable X expansion for Sprite 5 & 6 86 | address: "{vic.XXPAND}" 87 | byte_value: "$60" 88 | jump_address: "{ChangeSpriteAttribute}" 89 | assert: 90 | - description: Sprite 5's horizontal expansion is set to False 91 | type: memory_test 92 | address: "{vic.XXPAND}" 93 | op: eq 94 | byte_value: "$40" 95 | 96 | - name: sprites-enable-sprite-correctly 97 | description: Enable a sprite 98 | set_memory: 99 | - description: Sprite 7 100 | address: "{r3L}" 101 | byte_value: "$07" 102 | - description: The enable/disable attribute 103 | address: "{r3H}" 104 | byte_value: "{SPR_VISIBLE}" 105 | - description: Set to True 106 | address: "{r4L}" 107 | byte_value: "{SPR_SHOW}" 108 | - description: Start with sprite 0 enabled 109 | address: "{vic.SPENA}" 110 | byte_value: "$01" 111 | jump_address: "{ChangeSpriteAttribute}" 112 | assert: 113 | - description: Sprite 7 and 0 are now enabled 114 | type: memory_test 115 | address: "{vic.SPENA}" 116 | op: eq 117 | byte_value: "$81" 118 | 119 | - name: sprites-disable-sprite-correctly 120 | description: Disable a sprite 121 | set_memory: 122 | - description: Sprite 3 123 | address: "{r3L}" 124 | byte_value: "$03" 125 | - description: The enable/disable attribute 126 | address: "{r3H}" 127 | byte_value: "{SPR_VISIBLE}" 128 | - description: Set to True 129 | address: "{r4L}" 130 | byte_value: "{SPR_HIDE}" 131 | - description: Start with sprite 1 & 3 enabled 132 | address: "{vic.SPENA}" 133 | byte_value: "$0a" 134 | jump_address: "{ChangeSpriteAttribute}" 135 | assert: 136 | - description: Sprite 3 is disabled and sprite 1 is still enabled 137 | type: memory_test 138 | address: "{vic.SPENA}" 139 | op: eq 140 | byte_value: "$02" 141 | 142 | - name: sprites-positions-correctly-without-msb 143 | description: Sprite X pos < 256 sets MSB to 0 144 | set_memory: 145 | - description: MSB for all sprites to 0 146 | address: "{vic.MSIGX}" 147 | byte_value: "$00" 148 | - description: Sprite 0 149 | address: "{r3L}" 150 | byte_value: "$00" 151 | - description: X pos at 255 152 | address: "{r4}" 153 | word_value: "$00ff" 154 | - description: Y pos at 64 155 | address: "{r5L}" 156 | byte_value: "$40" 157 | jump_address: "{PositionSprite}" 158 | assert: 159 | - description: Sprite 0's X pos is at ff 160 | type: memory_test 161 | address: "{vic.SP0X}" 162 | op: eq 163 | byte_value: "$ff" 164 | - description: And sprite 0's MSB is set to 0 165 | type: memory_test 166 | address: "{vic.MSIGX}" 167 | op: eq 168 | byte_value: "$00" 169 | 170 | - name: sprites-sets-msb-properly 171 | description: Moving a sprite horizontally sets the MSB properly 172 | set_memory: 173 | - description: MSB for all sprites to 0 174 | address: "{vic.MSIGX}" 175 | byte_value: "$00" 176 | - description: Sprite 0 177 | address: "{r3L}" 178 | byte_value: "$00" 179 | - description: X pos at 256 180 | address: "{r4}" 181 | word_value: "$0100" 182 | - description: Y pos at 64 183 | address: "{r5L}" 184 | byte_value: "$40" 185 | jump_address: "{PositionSprite}" 186 | assert: 187 | - description: Sprite 0's X pos is at 0 188 | type: memory_test 189 | address: "{vic.SP0X}" 190 | op: eq 191 | byte_value: "$00" 192 | - description: But sprite 0's MSB is set to 1 193 | type: memory_test 194 | address: "{vic.MSIGX}" 195 | op: eq 196 | byte_value: "$01" 197 | 198 | - name: timer-single-disables-on-update 199 | description: Single shot timer disables when it hits 0 200 | set_memory: 201 | - description: Enable timer 0 202 | address: "{c64lib_timers}" 203 | byte_value: "{ENABLE}" 204 | - description: Single shot 205 | address: "{c64lib_timers} + 1" 206 | byte_value: "{TIMER_SINGLE}" 207 | - description: Current value 208 | address: "{c64lib_timers} + 2" 209 | word_value: "$01" 210 | - description: Frequency 211 | address: "{c64lib_timers} + 4" 212 | word_value: "{TIMER_ONE_SECOND}" 213 | - description: Timer call address 214 | address: "{c64lib_timers} + 6" 215 | word_value: "{ReadJoysticks}" 216 | jump_address: "{UpdateTimers}" 217 | assert: 218 | - description: Timer is disabled 219 | type: memory_test 220 | address: "{c64lib_timers}" 221 | op: eq 222 | byte_value: "{DISABLE}" 223 | 224 | - name: timer-continuous-will-reset-on-update 225 | description: Continuous timer resets when it hits 0 226 | set_memory: 227 | - description: Enable timer 0 228 | address: "{c64lib_timers}" 229 | byte_value: "{ENABLE}" 230 | - description: Continuous 231 | address: "{c64lib_timers} + 1" 232 | byte_value: "{TIMER_CONTINUOUS}" 233 | - description: Current value 234 | address: "{c64lib_timers} + 2" 235 | word_value: "$01" 236 | - description: Frequency 237 | address: "{c64lib_timers} + 4" 238 | word_value: "{TIMER_ONE_SECOND}" 239 | - description: Timer call address 240 | address: "{c64lib_timers} + 6" 241 | word_value: "{ReadJoysticks}" 242 | jump_address: "{UpdateTimers}" 243 | assert: 244 | - description: Timer is still enabled 245 | type: memory_test 246 | address: "{c64lib_timers}" 247 | op: eq 248 | byte_value: "{ENABLE}" 249 | - description: Current value has been reset 250 | type: memory_test 251 | address: "{c64lib_timers} + 2" 252 | op: eq 253 | word_value: "{TIMER_ONE_SECOND}" 254 | 255 | - name: timer-enable 256 | description: Enable a disabled timer 257 | set_memory: 258 | - description: Disable timer 0 259 | address: "{c64lib_timers}" 260 | byte_value: "{DISABLE}" 261 | - description: Timer number 0 262 | address: "{r2H}" 263 | byte_value: "$00" 264 | - description: Enable the timer 265 | address: "{r3L}" 266 | byte_value: "{ENABLE}" 267 | jump_address: "{EnDisTimer}" 268 | assert: 269 | - description: Timer has been enabled 270 | type: memory_test 271 | address: "{c64lib_timers}" 272 | op: eq 273 | byte_value: "{ENABLE}" 274 | 275 | - name: timer-disable 276 | description: Disable an enabled timer 277 | set_memory: 278 | - description: Enable timer 0 279 | address: "{c64lib_timers}" 280 | byte_value: "{ENABLE}" 281 | - description: Timer number 0 282 | address: "{r2H}" 283 | byte_value: "$00" 284 | - description: Disable the timer 285 | address: "{r3L}" 286 | byte_value: "{DISABLE}" 287 | jump_address: "{EnDisTimer}" 288 | assert: 289 | - description: Timer has been disabled 290 | type: memory_test 291 | address: "{c64lib_timers}" 292 | op: eq 293 | byte_value: "{DISABLE}" 294 | 295 | - name: timer-init-timer-memory 296 | description: Make sure initializing the timers clears timer memory 297 | jump_address: "{ClearTimers}" 298 | assert: 299 | - description: Timer memory is cleared 300 | type: memory_block 301 | address: "{c64lib_timers}" 302 | byte_count: "{TIMER_STRUCT_BYTES}" 303 | byte_value: "$0" 304 | 305 | - name: timer-create-single-shot 306 | description: Make sure we can create a single shot timer 307 | set_memory: 308 | - description: Frequency 309 | address: "{r0}" 310 | word_value: "$0001" 311 | - description: Call address 312 | address: "{r1}" 313 | word_value: "{ReadJoysticks}" 314 | - description: Single or continuous 315 | address: "{r2L}" 316 | byte_value: "{TIMER_SINGLE}" 317 | - description: Timer number 318 | address: "{r2H}" 319 | byte_value: "$00" 320 | - description: Enabled 321 | address: "{r3L}" 322 | byte_value: "{ENABLE}" 323 | jump_address: "{CreateTimer}" 324 | assert: 325 | - description: Enabled 326 | type: memory_test 327 | address: "{c64lib_timers} + 0 + peekbyte({r2H}) * 8" 328 | op: eq 329 | byte_value: "{ENABLE}" 330 | - description: Single or continuous 331 | type: memory_test 332 | address: "{c64lib_timers} + 1 + peekbyte({r2H}) * 8" 333 | op: eq 334 | byte_value: "{TIMER_SINGLE}" 335 | - description: Current value 336 | type: memory_test 337 | address: "{c64lib_timers} + 2 + peekbyte({r2H}) * 8" 338 | op: eq 339 | word_value: "$01" 340 | - description: Frequency 341 | type: memory_test 342 | address: "{c64lib_timers} + 4 + peekbyte({r2H}) * 8" 343 | op: eq 344 | word_value: "$01" 345 | - description: Call address 346 | type: memory_test 347 | address: "{c64lib_timers} + 6 + peekbyte({r2H}) * 8" 348 | op: eq 349 | word_value: "{ReadJoysticks}" 350 | - description: Make sure we're executing within 85 cycles 351 | type: cycle_count 352 | op: lt 353 | cycle_count: 85 354 | 355 | - name: vic-set-bank-chars-and-screen-i 356 | description: Set VIC bank to 0, screen to 1024 and chars to 12288 357 | set_memory: 358 | - description: r0L is bank 0 359 | address: "{r0L}" 360 | byte_value: "$00" 361 | - description: r0H is character memory 362 | address: "{r0H}" 363 | byte_value: "$06" 364 | - description: r1L is screen memory 365 | address: "{r1L}" 366 | byte_value: "$01" 367 | jump_address: "{SetVICBank}" 368 | assert: 369 | - description: CIA 2 is set properly 370 | type: memory_test 371 | address: "{cia.CI2PRA}" 372 | op: eq 373 | byte_value: "$03" 374 | - description: VIC is set properly 375 | type: memory_test 376 | address: "{vic.VMCSB}" 377 | op: eq 378 | byte_value: "$1c" 379 | - description: Bank Memory Base is set properly 380 | type: memory_test 381 | address: "{vic.BankMemoryBase}" 382 | op: eq 383 | word_value: "$0000" 384 | - description: Screen Memory Base is set properly 385 | type: memory_test 386 | address: "{vic.ScreenMemoryBase}" 387 | op: eq 388 | word_value: "$0400" 389 | - description: Character Memory Base is set properly 390 | type: memory_test 391 | address: "{vic.CharacterMemoryBase}" 392 | op: eq 393 | word_value: "$3000" 394 | - description: Sprite Pointer Base is set properly 395 | type: memory_test 396 | address: "{vic.SpritePointerBase}" 397 | op: eq 398 | word_value: "$07f8" 399 | - description: Make sure we're executing within 170 cycles 400 | type: cycle_count 401 | op: lt 402 | cycle_count: 170 403 | 404 | - name: vic-set-bank-chars-and-screen-ii 405 | description: Set VIC bank to 3, screen to 4096 and chars to 8192 406 | set_memory: 407 | - description: r0L is bank 0 408 | address: "{r0L}" 409 | byte_value: "$03" 410 | - description: r0H is character memory 411 | address: "{r0H}" 412 | byte_value: "$04" 413 | - description: r1L is screen memory 414 | address: "{r1L}" 415 | byte_value: "$04" 416 | jump_address: "{SetVICBank}" 417 | assert: 418 | - description: CIA 2 is set properly 419 | type: memory_test 420 | address: "{cia.CI2PRA}" 421 | op: eq 422 | byte_value: "$00" 423 | - description: VIC is set properly 424 | type: memory_test 425 | address: "{vic.VMCSB}" 426 | op: eq 427 | byte_value: "$48" 428 | - description: Bank Memory Base is set properly 429 | type: memory_test 430 | address: "{vic.BankMemoryBase}" 431 | op: eq 432 | word_value: "$c000" 433 | - description: Screen Memory Base is set properly 434 | type: memory_test 435 | address: "{vic.ScreenMemoryBase}" 436 | op: eq 437 | word_value: "$d000" 438 | - description: Character Memory Base is set properly 439 | type: memory_test 440 | address: "{vic.CharacterMemoryBase}" 441 | op: eq 442 | word_value: "$e000" 443 | - description: Sprite Pointer Base is set properly 444 | type: memory_test 445 | address: "{vic.SpritePointerBase}" 446 | op: eq 447 | word_value: "$d3f8" 448 | - description: Make sure we're executing within 170 cycles 449 | type: cycle_count 450 | op: lt 451 | cycle_count: 170 452 | 453 | - name: vic-set-bank-chars-and-screen-iii 454 | description: Set VIC bank to 2, screen to 8192 and chars to 0 455 | set_memory: 456 | - description: r0L is bank 0 457 | address: "{r0L}" 458 | byte_value: "$02" 459 | - description: r0H is character memory 460 | address: "{r0H}" 461 | byte_value: "$00" 462 | - description: r1L is screen memory 463 | address: "{r1L}" 464 | byte_value: "$08" 465 | jump_address: "{SetVICBank}" 466 | assert: 467 | - description: CIA 2 is set properly 468 | type: memory_test 469 | address: "{cia.CI2PRA}" 470 | op: eq 471 | byte_value: "$01" 472 | - description: VIC is set properly 473 | type: memory_test 474 | address: "{vic.VMCSB}" 475 | op: eq 476 | byte_value: "$80" 477 | - description: Bank Memory Base is set properly 478 | type: memory_test 479 | address: "{vic.BankMemoryBase}" 480 | op: eq 481 | word_value: "$8000" 482 | - description: Screen Memory Base is set properly 483 | type: memory_test 484 | address: "{vic.ScreenMemoryBase}" 485 | op: eq 486 | word_value: "$a000" 487 | - description: Character Memory Base is set properly 488 | type: memory_test 489 | address: "{vic.CharacterMemoryBase}" 490 | op: eq 491 | word_value: "$8000" 492 | - description: Sprite Pointer Base is set properly 493 | type: memory_test 494 | address: "{vic.SpritePointerBase}" 495 | op: eq 496 | word_value: "$a3f8" 497 | - description: Make sure we're executing within 170 cycles 498 | type: cycle_count 499 | op: lt 500 | cycle_count: 170 501 | --------------------------------------------------------------------------------