├── .gitignore ├── Makefile ├── README.md ├── basic.asm ├── cia.asm ├── constants.asm ├── include_me_full.asm ├── include_me_full_r.asm ├── include_me_memory.asm ├── include_me_min.asm ├── include_me_sprites.asm ├── include_me_sprites_r.asm ├── include_me_timers.asm ├── joystick.asm ├── kernal.asm ├── keyboard.asm ├── macros.asm ├── memory.asm ├── pseudocommands.asm ├── registers.asm ├── sid.asm ├── sprites.asm ├── tests.6502 ├── tests.yaml ├── tests_r.yaml ├── timers.asm ├── variables.asm └── vic.asm /.gitignore: -------------------------------------------------------------------------------- 1 | kernal.rom 2 | basic.rom 3 | character.rom 4 | ByteDump.txt 5 | *.prg 6 | *.sym 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_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_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 | -------------------------------------------------------------------------------- /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_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_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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /keyboard.asm: -------------------------------------------------------------------------------- 1 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | Keyboard IO Routine 3 | ~~~~~~~~~~~~~~~~~~~ 4 | By: TWW/CTR 5 | 6 | 7 | Preparatory Settings 8 | ~~~~~~~~~~~~~~~~~~~~ 9 | None 10 | 11 | 12 | Destroys 13 | ~~~~~~~~ 14 | Accumulator 15 | X-Register 16 | Y-Register 17 | Carry / Zero / Negative 18 | $dc00 19 | $dc01 20 | $50-$5f 21 | 22 | 23 | Footprint 24 | ~~~~~~~~~ 25 | #$206 Bytes 26 | 27 | 28 | Information 29 | ~~~~~~~~~~~ 30 | The routine uses "2 Key rollower" or up to 3 if the key-combination doesen't induce shadowing. 31 | If 2 or 3 keys are pressed simultaneously (within 1 scan) a "No Activity" state has to occur before new valid keys are returned. 32 | RESTORE is not detectable and must be handled by NMI IRQ. 33 | SHIFT LOCK is not detected due to unreliability. 34 | 35 | 36 | Usage 37 | ~~~~~ 38 | Example Code: 39 | 40 | jsr Keyboard 41 | bcs NoValidInput 42 | stx TempX 43 | sty TempY 44 | cmp #$ff 45 | beq NoNewAphanumericKey 46 | // Check A for Alphanumeric keys 47 | sta $0400 48 | NoNewAphanumericKey: 49 | // Check X & Y for Non-Alphanumeric Keys 50 | ldx TempX 51 | ldy TempY 52 | stx $0401 53 | sty $0402 54 | NoValidInput: // This may be substituted for an errorhandler if needed. 55 | 56 | 57 | Returned 58 | ~~~~~~~~ 59 | 60 | +=================================================+ 61 | | Returned in Accumulator | 62 | +===========+===========+=============+===========+ 63 | | $00 - @ | $10 - p | $20 - SPC | $30 - 0 | 64 | | $01 - a | $11 - q | $21 - | $31 - 1 | 65 | | $02 - b | $12 - r | $22 - | $32 - 2 | 66 | | $03 - c | $13 - s | $23 - | $33 - 3 | 67 | | $04 - d | $14 - t | $24 - | $34 - 4 | 68 | | $05 - e | $15 - u | $25 - | $35 - 5 | 69 | | $06 - f | $16 - v | $26 - | $36 - 6 | 70 | | $07 - g | $17 - w | $27 - | $37 - 7 | 71 | | $08 - h | $18 - x | $28 - | $38 - 8 | 72 | | $09 - i | $19 - y | $29 - | $39 - 9 | 73 | | $0a - j | $1a - z | $2a - * | $3a - : | 74 | | $0b - k | $1b - | $2b - + | $3b - ; | 75 | | $0c - l | $1c - £ | $2c - , | $3c - | 76 | | $0d - m | $1d - | $2d - - | $3d - = | 77 | | $0e - n | $1e - ^ | $2e - . | $3e - | 78 | | $0f - o | $1f - <- | $2f - / | $3f - | 79 | +-----------+-----------+-------------+-----------+ 80 | 81 | +================================================================================ 82 | | Return in X-Register | 83 | +=========+=========+=========+=========+=========+=========+=========+=========+ 84 | | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | 85 | +---------+---------+---------+---------+---------+---------+---------+---------+ 86 | | CRSR UD | F5 | F3 | F1 | F7 | CRSR RL | RETURN |INST/DEL | 87 | +---------+---------+---------+---------+---------+---------+---------+---------+ 88 | 89 | +================================================================================ 90 | | Return in Y-Register | 91 | +=========+=========+=========+=========+=========+=========+=========+=========+ 92 | | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | 93 | +---------+---------+---------+---------+---------+---------+---------+---------+ 94 | |RUN STOP | L-SHIFT | C= | R-SHIFT |CLR/HOME | CTRL | | | 95 | +---------+---------+---------+---------+---------+---------+---------+---------+ 96 | 97 | CARRY: 98 | - Set = Error Condition (Check A for code): 99 | A = #$01 => 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 | -------------------------------------------------------------------------------- /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,