├── .gitattributes ├── .gitignore └── scroll4x4 └── scroller_4x4.asm /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | scroll4x4/resources/scrollerchars.raw 49 | *.prg 50 | scroll4x4/build/scroller_4x4_build.log 51 | scroll4x4/build/scroller_4x4_settings.lua 52 | scroll4x4/resources/scrollerchars.asm 53 | -------------------------------------------------------------------------------- /scroll4x4/scroller_4x4.asm: -------------------------------------------------------------------------------- 1 | ; Dynamic 4x4 scroller 2 | ; 3 | ; Platform: C64 4 | ; Code: Jesder / 0xc64 5 | ; Font: Unknown 6 | ; Compiler: win2c64 (http://www.aartbik.com) 7 | ; Website: http://www.0xc64.com 8 | ; Notes: Builds a 4x4 scroller font based on a 1x1 font 9 | ; 10 | 11 | ; zero page register 12 | 13 | REG_ZERO_FD .equ $fd 14 | REG_ZERO_FE .equ $fe 15 | 16 | ; common register definitions 17 | 18 | REG_INTSERVICE_LOW .equ $0314 ; interrupt service routine low byte 19 | REG_INTSERVICE_HIGH .equ $0315 ; interrupt service routine high byte 20 | REG_SCREENCTL_1 .equ $d011 ; screen control register #1 21 | REG_RASTERLINE .equ $d012 ; raster line position 22 | REG_SCREENCTL_2 .equ $d016 ; screen control register #2 23 | REG_MEMSETUP .equ $d018 ; memory setup register 24 | REG_INTFLAG .equ $d019 ; interrupt flag register 25 | REG_INTCONTROL .equ $d01a ; interrupt control register 26 | REG_BORCOLOUR .equ $d020 ; border colour register 27 | REG_BGCOLOUR .equ $d021 ; background colour register 28 | REG_INTSTATUS_1 .equ $dc0d ; interrupt control and status register #1 29 | REG_INTSTATUS_2 .equ $dd0d ; interrupt control and status register #2 30 | 31 | 32 | ; constants 33 | 34 | C_SCREEN_RAM .equ $0400 35 | C_CHARSET .equ $3000 36 | C_CHARSET_HIGH .equ $3100 37 | C_COLOUR_RAM .equ $d800 38 | 39 | 40 | ; program start 41 | 42 | .org $0801 43 | 44 | .byte $0b, $08, $01, $00, $9e, $32, $30, $36 45 | .byte $31, $00, $00, $00 ; auto run 46 | 47 | 48 | ; register first interrupt 49 | 50 | sei 51 | lda #$7f 52 | sta REG_INTSTATUS_1 ; turn off the CIA interrupts 53 | sta REG_INTSTATUS_2 54 | and REG_SCREENCTL_1 ; clear high bit of raster line 55 | sta REG_SCREENCTL_1 56 | 57 | ldy #000 58 | sty REG_RASTERLINE 59 | lda #init_routine 61 | sta REG_INTSERVICE_LOW 62 | stx REG_INTSERVICE_HIGH 63 | 64 | lda #$01 ; enable raster interrupts 65 | sta REG_INTCONTROL 66 | cli 67 | 68 | forever jmp forever 69 | 70 | 71 | ; helper routines -------------------------------------------------------------------------------------------------] 72 | ; -----------------------------------------------------------------------------------------------------------------] 73 | 74 | apply_interrupt sta REG_RASTERLINE 75 | stx REG_INTSERVICE_LOW 76 | sty REG_INTSERVICE_HIGH 77 | apply_interrupt_repeat inc REG_INTFLAG 78 | jmp $ea81 79 | 80 | 81 | clear_screen_ramroutine lda #032 82 | ldx #000 ; clear screen ram routine 83 | clear_screen_ram_loop sta C_SCREEN_RAM, x 84 | sta C_SCREEN_RAM + $100, x 85 | sta C_SCREEN_RAM + $200, x 86 | sta C_SCREEN_RAM + $2e8, x 87 | inx 88 | bne clear_screen_ram_loop 89 | rts 90 | 91 | 92 | clear_colour_ramroutine lda #000 93 | ldx #000 ; clear colour ram routine 94 | clear_colour_ram_loop sta C_COLOUR_RAM, x 95 | sta C_COLOUR_RAM + $100, x 96 | sta C_COLOUR_RAM + $200, x 97 | sta C_COLOUR_RAM + $2e8, x 98 | inx 99 | bne clear_colour_ram_loop 100 | rts 101 | 102 | 103 | ; init routine ----------------------------------------------------------------------------------------------------] 104 | ; -----------------------------------------------------------------------------------------------------------------] 105 | 106 | init_routine jsr clear_screen_ramroutine ; initialise screen 107 | 108 | jsr clear_colour_ramroutine 109 | 110 | ldx #000 ; relocate character set data 111 | relocate_font_data lda font_data, x 112 | sta C_CHARSET, x 113 | lda font_data + $100, x 114 | sta C_CHARSET + $100, x 115 | lda font_data + $180, x 116 | sta C_CHARSET + $180, x 117 | inx 118 | bne relocate_font_data 119 | 120 | ldx #160 ; init colour ram for scroller 121 | lda #001 122 | set_scroller_colour sta C_COLOUR_RAM + 759, x 123 | dex 124 | bne set_scroller_colour 125 | 126 | ldx #019 ; render short message in 1x1 font to screen 127 | init_short_message lda short_message, x 128 | sta C_SCREEN_RAM + 610, x 129 | lda #014 130 | sta C_COLOUR_RAM + 610, x 131 | dex 132 | bpl init_short_message 133 | 134 | lda #029 ; switch to character set 135 | sta REG_MEMSETUP 136 | 137 | lda #000 ; init screen and border colours 138 | sta REG_BORCOLOUR 139 | sta REG_BGCOLOUR 140 | 141 | jmp hook_update_scroller 142 | 143 | 144 | ; update & render scroller ----------------------------------------------------------------------------------------] 145 | ; -----------------------------------------------------------------------------------------------------------------] 146 | 147 | hook_update_scroller lda #085 148 | ldx #update_scroller 150 | jmp apply_interrupt 151 | 152 | 153 | update_scroller ldx scroller_amount + 1 154 | dex ; advance hardware scroll 155 | dex 156 | dex 157 | bmi shift_scroller_data ; detect if time to shift screen ram on the scroller 158 | stx scroller_amount + 1 ; not time to advance scroller, so just update the hardware scroll value 159 | jmp update_scroller_done ; we're done for now 160 | 161 | shift_scroller_data stx REG_ZERO_FE ; cache scroll amount for later use 162 | 163 | ldy #000 ; shift screen ram to the left 164 | scroller_shift_loop lda C_SCREEN_RAM + $2f9, y ; shift all 4 rows 165 | sta C_SCREEN_RAM + $2f8, y 166 | lda C_SCREEN_RAM + $321, y 167 | sta C_SCREEN_RAM + $320, y 168 | lda C_SCREEN_RAM + $349, y 169 | sta C_SCREEN_RAM + $348, y 170 | lda C_SCREEN_RAM + $371, y 171 | sta C_SCREEN_RAM + $370, y 172 | iny 173 | cpy #039 174 | bne scroller_shift_loop 175 | 176 | ldx scroller_char_step + 1 ; grab step into rendering current scroller character 177 | bpl render_next_scroll_colm ; detect if we need to render a new character, or are still rendering current character (each letter is 4 chars wide) 178 | 179 | scroller_message_index ldx #000 ; time to render a new character, so set up some which character to render and the bit mask 180 | read_next_scroller_char lda scroller_message, x ; grab next character to render 181 | bpl advance_scroller_index ; detect end of message control character 182 | lda scroller_message 183 | ldx #001 ; reset index - set to 1 since this update will use first char in message 184 | ldy #>scroller_message ; reset scroller message read source 185 | sty read_next_scroller_char + 2 186 | jmp save_scroller_index 187 | 188 | advance_scroller_index inx ; advance scroller message index 189 | bne save_scroller_index ; detect if reached 256 offset 190 | inc read_next_scroller_char + 2 ; advance high byte for reading message 191 | save_scroller_index stx scroller_message_index + 1 192 | 193 | ldy #>C_CHARSET ; determine if character is in the low/high section of the charset 194 | cmp #031 195 | bcc calc_scrollchar_src_low 196 | ldy #>C_CHARSET_HIGH 197 | 198 | calc_scrollchar_src_low and #031 ; calculate offset into char set for character bytes 199 | asl 200 | asl 201 | asl 202 | 203 | sty render_scroller_column + 2 ; store character high/low pointers for rendering 204 | sty render_scroller_column2 + 2 205 | sta render_scroller_column + 1 206 | sta render_scroller_column2 + 1 207 | 208 | lda #192 ; reset the scroller character mask 209 | sta scroller_character_mask + 1 210 | lda #003 ; reset step into new character mask 211 | sta scroller_char_step + 1 212 | 213 | render_next_scroll_colm clc 214 | lda REG_ZERO_FE ; reset the hardware scroll value 215 | adc #008 216 | tax 217 | stx scroller_amount + 1 ; save hardware scroll index 218 | 219 | ldx #000 ; init character byte loop counter 220 | stx REG_ZERO_FD ; reset screen rendering offset and cache on zero page 221 | render_scroller_column lda C_CHARSET, x ; load byte from character ram 222 | scroller_character_mask and #192 ; apply current mask 223 | scroller_char_step ldy #255 224 | beq skip_shift_1 ; dont shift if we are already masking bits 0 and 1 225 | shift_scroll_mask_loop1 lsr ; shift down until bits 0 and 1 are occupied 226 | lsr 227 | dey 228 | bne shift_scroll_mask_loop1 229 | skip_shift_1 asl ; multiply by 4 as a look up into our character matrix 230 | asl 231 | sta REG_ZERO_FE ; cache on zero page to recall shortly 232 | 233 | inx ; advance to next byte in character ram 234 | render_scroller_column2 lda C_CHARSET, x 235 | and scroller_character_mask + 1 ; apply current mask 236 | ldy scroller_char_step + 1 237 | beq skip_shift_2 ; dont shift if we are already masking bits 0 and 1 238 | shift_scroll_mask_loop2 lsr ; shift down until bits 0 and 1 are occupied 239 | lsr 240 | dey 241 | bne shift_scroll_mask_loop2 242 | 243 | skip_shift_2 clc ; calculate characater code to use for this 2x2 block 244 | adc REG_ZERO_FE ; grab offset calculated earlier 245 | adc #064 ; add offset to the character matrix 246 | 247 | scroller_render_offset ldy REG_ZERO_FD 248 | sta C_SCREEN_RAM + $31f, y ; render character to screen 249 | tya 250 | adc #040 ; advance rendering offset for next pass of the loop 251 | sta REG_ZERO_FD 252 | 253 | inx ; advance to next byte in character ram 254 | cpx #008 ; detect if entire column now rendered 255 | bne render_scroller_column 256 | 257 | dec scroller_char_step + 1 ; advance scroller character step 258 | lda scroller_character_mask + 1 ; advance scroller character mask for next update 259 | lsr 260 | lsr 261 | sta scroller_character_mask + 1 262 | 263 | update_scroller_done jmp hook_apply_hw_scroll 264 | 265 | 266 | ; apply hardware scroll -------------------------------------------------------------------------------------------] 267 | ; -----------------------------------------------------------------------------------------------------------------] 268 | 269 | hook_apply_hw_scroll lda #201 270 | ldx #apply_hardware_scroll 272 | jmp apply_interrupt 273 | 274 | 275 | apply_hardware_scroll lda #$c0 ; 38 column 276 | scroller_amount ora #007 ; add hardware scroll - ready to apply 277 | 278 | ldy #202 ; wait for scan line scroller starts on 279 | wait_scroller_start cpy REG_RASTERLINE 280 | bne wait_scroller_start 281 | 282 | sta REG_SCREENCTL_2 ; apply hardware scroll value 283 | 284 | jmp hook_reset_hw_scroll 285 | 286 | 287 | ; reset hardware scroll -------------------------------------------------------------------------------------------] 288 | ; -----------------------------------------------------------------------------------------------------------------] 289 | 290 | hook_reset_hw_scroll lda #234 291 | ldx #reset_hardware_scroll 293 | jmp apply_interrupt 294 | 295 | 296 | reset_hardware_scroll lda #$c8 ; 40 column mode + no scroll 297 | 298 | ldy #235 ; wait for scan line scroller end on 299 | wait_scroller_end cpy REG_RASTERLINE 300 | bne wait_scroller_end 301 | 302 | sta REG_SCREENCTL_2 ; apply reset to scroll & column mode 303 | 304 | jmp hook_update_scroller 305 | 306 | 307 | ; data & variables ------------------------------------------------------------------------------------------------] 308 | ; -----------------------------------------------------------------------------------------------------------------] 309 | 310 | scroller_message .byte 020, 008, 009, 019, 032, 009, 019, 032, 001, 032, 020, 005, 019, 020, 032, 013 311 | .byte 005, 019, 019, 001, 007, 005, 032, 006, 015, 018, 032, 020, 008, 005, 032, 052 312 | .byte 024, 052, 032, 019, 003, 018, 015, 012, 012, 005, 018, 032, 018, 015, 021, 020 313 | .byte 009, 014, 005, 046, 032, 014, 005, 005, 004, 032, 020, 015, 032, 005, 014, 019 314 | .byte 021, 018, 005, 032, 009, 020, 032, 012, 015, 015, 016, 019, 032, 001, 018, 015 315 | .byte 021, 014, 004, 032, 001, 020, 032, 050, 053, 053, 032, 003, 008, 001, 018, 001 316 | .byte 003, 020, 005, 018, 019, 046, 032, 015, 014, 003, 005, 032, 020, 008, 001, 020 317 | .byte 039, 019, 032, 023, 015, 018, 011, 009, 014, 007, 032, 009, 032, 023, 009, 012 318 | .byte 012, 032, 014, 005, 005, 004, 032, 020, 015, 032, 001, 004, 004, 032, 019, 015 319 | .byte 013, 005, 032, 003, 015, 012, 015, 021, 018, 032, 020, 015, 032, 013, 001, 011 320 | .byte 005, 032, 009, 020, 032, 016, 018, 005, 020, 020, 025, 046, 046, 046, 032, 032 321 | .byte 032, 032, 032, 019, 020, 009, 012, 012, 032, 014, 005, 005, 004, 032, 020, 015 322 | .byte 032, 001, 004, 004, 032, 013, 015, 018, 005, 032, 020, 005, 024, 020, 032, 020 323 | .byte 015, 032, 006, 009, 012, 012, 032, 015, 021, 020, 032, 020, 008, 005, 032, 050 324 | .byte 053, 053, 032, 003, 008, 001, 018, 001, 003, 020, 005, 018, 019, 046, 046, 032 325 | .byte 032, 057, 056, 055, 054, 053, 052, 051, 050, 049, 048, 032, 001, 014, 004, 032 326 | .byte 012, 015, 015, 016, 045, 255 327 | 328 | short_message .byte 020, 008, 009, 019, 032, 009, 019, 032, 020, 008, 005, 032, 049, 024, 049, 032 329 | .byte 006, 015, 014, 020 330 | 331 | font_data .byte $00, $00, $00, $00, $00, $00, $00, $00, $7c, $c6, $de, $c6, $c6, $c6, $c6, $00 332 | .byte $fc, $c6, $dc, $c6, $c6, $c6, $fc, $00, $7c, $c6, $c0, $c0, $c0, $c6, $7c, $00 333 | .byte $fc, $c6, $c6, $c6, $c6, $c6, $fc, $00, $7c, $c6, $f8, $c0, $c0, $c6, $7c, $00 334 | .byte $7c, $c6, $f8, $c0, $c0, $c0, $c0, $c0, $7c, $c0, $ce, $c6, $c6, $c6, $7c, $00 335 | .byte $c6, $c6, $de, $c6, $c6, $c6, $c6, $c0, $18, $18, $18, $18, $18, $18, $18, $00 336 | .byte $06, $06, $06, $06, $06, $06, $c6, $7c, $c6, $c6, $dc, $c6, $c6, $c6, $c6, $00 337 | .byte $c0, $c0, $c0, $c0, $c0, $c6, $7c, $00, $6c, $fe, $c6, $c6, $c6, $c6, $c6, $00 338 | .byte $7c, $c6, $c6, $c6, $c6, $c6, $c6, $00, $7c, $c6, $c6, $c6, $c6, $c6, $7c, $00 339 | .byte $fc, $c6, $dc, $c0, $c0, $c0, $c0, $c0, $7c, $c6, $c6, $c6, $c6, $c6, $7c, $06 340 | .byte $fc, $c6, $dc, $c6, $c6, $c6, $c6, $00, $7c, $c0, $fc, $06, $06, $c6, $7c, $00 341 | .byte $7e, $18, $18, $18, $18, $18, $18, $00, $c6, $c6, $c6, $c6, $c6, $c6, $7c, $00 342 | .byte $c6, $c6, $c6, $c6, $c6, $6c, $38, $00, $c6, $c6, $c6, $c6, $c6, $fe, $6c, $00 343 | .byte $c6, $c6, $7c, $1c, $c6, $c6, $c6, $00, $c6, $c6, $7c, $38, $38, $38, $38, $00 344 | .byte $fe, $0c, $18, $30, $60, $c0, $fe, $00, $3c, $30, $30, $30, $30, $30, $3c, $00 345 | .byte $00, $00, $00, $00, $00, $00, $00, $00, $3c, $0c, $0c, $0c, $0c, $0c, $3c, $00 346 | .byte $00, $18, $3c, $7e, $18, $18, $18, $18, $00, $10, $30, $7f, $7f, $30, $10, $00 347 | .byte $00, $00, $00, $00, $00, $00, $00, $00, $18, $18, $18, $18, $00, $00, $18, $00 348 | .byte $66, $66, $66, $00, $00, $00, $00, $00, $66, $66, $ff, $66, $ff, $66, $66, $00 349 | .byte $18, $3e, $60, $3c, $06, $7c, $18, $00, $62, $66, $0c, $18, $30, $66, $46, $00 350 | .byte $3c, $66, $3c, $38, $67, $66, $3f, $00, $06, $0c, $18, $00, $00, $00, $00, $00 351 | .byte $0c, $18, $30, $30, $30, $18, $0c, $00, $30, $18, $0c, $0c, $0c, $18, $30, $00 352 | .byte $00, $66, $3c, $18, $3c, $66, $00, $00, $00, $18, $18, $7e, $18, $18, $00, $00 353 | .byte $00, $00, $00, $00, $00, $18, $18, $30, $00, $00, $00, $7e, $00, $00, $00, $00 354 | .byte $00, $00, $00, $00, $00, $30, $30, $00, $00, $03, $06, $0c, $18, $30, $60, $00 355 | .byte $7c, $c6, $c6, $c6, $c6, $c6, $7c, $00, $38, $18, $18, $18, $18, $18, $18, $00 356 | .byte $7c, $06, $7c, $c0, $c0, $c6, $7c, $00, $7c, $c6, $3c, $06, $06, $c6, $7c, $00 357 | .byte $c6, $c6, $7e, $06, $06, $06, $06, $00, $fe, $c0, $fc, $06, $06, $c6, $7c, $00 358 | .byte $7c, $c0, $dc, $c6, $c6, $c6, $7c, $00, $7e, $c6, $06, $06, $06, $06, $06, $00 359 | .byte $7c, $c6, $7c, $c6, $c6, $c6, $7c, $00, $7e, $c6, $76, $06, $06, $06, $06, $00 360 | .byte $00, $30, $30, $00, $30, $30, $00, $00, $00, $00, $18, $00, $00, $18, $18, $30 361 | .byte $0e, $18, $30, $60, $30, $18, $0e, $00, $00, $00, $3c, $78, $00, $3c, $78, $00 362 | .byte $70, $18, $0c, $06, $0c, $18, $70, $00, $3c, $66, $06, $0c, $18, $00, $18, $00 363 | 364 | ; scroller characters (16 chars) 365 | .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $0e, $0e, $0e, $00 366 | .byte $00, $00, $00, $00, $e0, $e0, $e0, $00, $00, $00, $00, $00, $ee, $ee, $ee, $00 367 | .byte $0e, $0e, $0e, $00, $00, $00, $00, $00, $0e, $0e, $0e, $00, $0e, $0e, $0e, $00 368 | .byte $0e, $0e, $0e, $00, $e0, $e0, $e0, $00, $0e, $0e, $0e, $00, $ee, $ee, $ee, $00 369 | .byte $e0, $e0, $e0, $00, $00, $00, $00, $00, $e0, $e0, $e0, $00, $0e, $0e, $0e, $00 370 | .byte $e0, $e0, $e0, $00, $e0, $e0, $e0, $00, $e0, $e0, $e0, $00, $ee, $ee, $ee, $00 371 | .byte $ee, $ee, $ee, $00, $00, $00, $00, $00, $ee, $ee, $ee, $00, $0e, $0e, $0e, $00 372 | .byte $ee, $ee, $ee, $00, $e0, $e0, $e0, $00, $ee, $ee, $ee, $00, $ee, $ee, $ee, $00 373 | --------------------------------------------------------------------------------