|
| 1 | +; ----------------------------------------------------------------------------- |
| 2 | +; 64#4 - 4x8 FONT DRIVER FOR 64 COLUMNS (c) 2007, 2011 |
| 3 | +; |
| 4 | +; Original by Andrew Owen (657 bytes) |
| 5 | +; Optimized by Crisis (602 bytes) |
| 6 | +; Reimplemented by Einar Saukas (494 bytes) |
| 7 | +; ----------------------------------------------------------------------------- |
| 8 | + |
| 9 | + org 64874 ; any address will do but this is just before |
| 10 | + ; the UDGs [CHANGE HERE TO CHOOSE ANOTHER |
| 11 | + ; ADDRESS] |
| 12 | + |
| 13 | +STR_NUM EQU 4 ; stream #4 [CHANGE HERE TO CHOOSE ANOTHER |
| 14 | + ; STREAM NUMBER] |
| 15 | + |
| 16 | +; ----------------------------------------------------------------------------- |
| 17 | +; CREATE CHANNEL AND ATTACH STREAM |
| 18 | +; Based on code by Ian Beardsmore from Your Spectrum issue 7, September 1984. |
| 19 | + |
| 20 | + ld hl, (0x5c53) ; store system variable PROG in HL |
| 21 | + dec hl |
| 22 | + ld bc, 5 ; allocate 5 bytes for channel below BASIC area |
| 23 | + push bc |
| 24 | + call 0x1655 ; call the MAKE-ROOM routine |
| 25 | + pop bc |
| 26 | + ld hl, CH_DATA + 4 |
| 27 | + lddr ; copy CH_DATA to new channel space |
| 28 | + ld hl, (0x5c4f) ; store system variable CHANS in HL |
| 29 | + ex de, hl |
| 30 | + inc hl |
| 31 | + inc hl ; now HL = allocated address + 1 |
| 32 | + sbc hl, de ; calculate offset between start of channels |
| 33 | + ; area and start of the new channel space |
| 34 | + ; (notice the carry flag was already cleared |
| 35 | + ; from executing CALL 0x1655 earlier) |
| 36 | + ld (STR_OFF), hl ; attach stream by storing channel address |
| 37 | + ; offset in streams table |
| 38 | + ret |
| 39 | + |
| 40 | +STR_OFF EQU 0x5c10+((STR_NUM+3)*2) ; address of channel offset in streams table |
| 41 | + |
| 42 | +CH_DATA: |
| 43 | + defw CH_ADDR ; address of the PRINT # routine |
| 44 | + defw 0x15c4 ; address of the INPUT # routine |
| 45 | + defb 'S' ; channel type 'S' |
| 46 | + |
| 47 | +; ----------------------------------------------------------------------------- |
| 48 | +; CHANNEL WRAPPER FOR THE 64-COLUMN DISPLAY DRIVER |
| 49 | +; Based on code by Tony Samuels from Your Spectrum issue 20, November 1985. |
| 50 | + |
| 51 | +CH_ADDR: |
| 52 | + ld b, 0 ; save a few bytes later using B instead of 0 |
| 53 | + ld hl, AT_FLAG ; initial address of local variables |
| 54 | + dec (hl) ; check AT_FLAG value by decrementing it |
| 55 | + jp m, CHK_AT ; expecting a regular character? |
| 56 | + jr z, GET_COL ; expecting the AT column? |
| 57 | + |
| 58 | +; ----------------------------------------------------------------------------- |
| 59 | +; UNCOMMENT TO ENABLE STANDARD INVERSE (use INVERSE 1 for inversed characters) |
| 60 | +; |
| 61 | +; #ifdef _STANDARD_INVERSE |
| 62 | +; dec (hl) ; check AT_FLAG value by decrementing it again |
| 63 | +; jr nz, GET_ROW ; expecting the AT row? |
| 64 | +; and a ; check INVERSE parameter |
| 65 | +; jr z, SET_INV ; specified INVERSE zero? |
| 66 | +; ld a, 0x2f ; opcode for 'CPL' |
| 67 | +;SET_INV: |
| 68 | +; ld (INV_C), a ; either 'NOP' or 'CPL' |
| 69 | +; ret |
| 70 | +; #endif _STANDARD_INVERSE |
| 71 | +; ----------------------------------------------------------------------------- |
| 72 | + |
| 73 | +GET_ROW: |
| 74 | + cp 24 ; specified row greater than 23? |
| 75 | + jr nc, ERROR_B ; error if so |
| 76 | + inc hl ; dirty trick to store new row into AT_ROW |
| 77 | +GET_COL: |
| 78 | + cp 64 ; specified column greater than 63? |
| 79 | + jr nc, ERROR_B ; error if so |
| 80 | + inc hl |
| 81 | + ld (hl), a ; store new column into AT_COL |
| 82 | + ret |
| 83 | + |
| 84 | +ERROR_B: |
| 85 | + ld (hl), b ; reset AT_FLAG |
| 86 | + rst 8 ; error "B Integer out of range" |
| 87 | + defb 10 |
| 88 | + |
| 89 | +CHK_AT: |
| 90 | + cp 0x16 ; specified keyword 'AT'? |
| 91 | + |
| 92 | +; ----------------------------------------------------------------------------- |
| 93 | +; UNCOMMENT TO ENABLE STANDARD INVERSE (use INVERSE 1 for inversed characters) |
| 94 | +; |
| 95 | +; #ifdef _STANDARD_INVERSE |
| 96 | +; jr nz, CHK_INV ; continue otherwise |
| 97 | +; ld (hl), 3 ; change AT_FLAG to expect row value next time |
| 98 | +; ret |
| 99 | +;CHK_INV: |
| 100 | +; cp 0x14 ; specified keyword 'INVERSE'? |
| 101 | +; #endif _STANDARD_INVERSE |
| 102 | +; ----------------------------------------------------------------------------- |
| 103 | + |
| 104 | + jr nz, CHK_CR ; continue otherwise |
| 105 | + ld (hl), 2 ; change AT_FLAG to expect row value next time |
| 106 | + ret ; (or to expect INVERSE parameter next time) |
| 107 | + |
| 108 | +CHK_CR: |
| 109 | + inc (hl) ; increment AT_FLAG to restore previous value |
| 110 | + inc hl ; now HL references AT_COL address |
| 111 | + cp 0x0d ; specified carriage return? |
| 112 | + jr z, NEXT_ROW ; change row if so |
| 113 | + |
| 114 | +; ----------------------------------------------------------------------------- |
| 115 | +; UNCOMMENT TO ENABLE FAST COMMA (jump directly to next column multiple of 16) |
| 116 | +; |
| 117 | +; #ifdef _FAST_COMMA |
| 118 | +; cp 0x06 ; specified comma? |
| 119 | +; jr nz, DRIVER ; continue otherwise |
| 120 | +; ld a, (hl) |
| 121 | +; or 0x0f ; change column to destination minus 1 |
| 122 | +; ld (hl),a |
| 123 | +; jr END_LOOP + 1 ; increment column and row if needed |
| 124 | +; #endif _FAST_COMMA |
| 125 | +; ----------------------------------------------------------------------------- |
| 126 | + |
| 127 | +; ----------------------------------------------------------------------------- |
| 128 | +; UNCOMMENT TO ENABLE STANDARD COMMA (print spaces until column multiple of 16) |
| 129 | +; |
| 130 | +; #ifdef _STANDARD_COMMA |
| 131 | +; cp 0x06 ; specified comma? |
| 132 | +; jr nz, DRIVER ; continue otherwise |
| 133 | +;LOOP: ld a, 32 ; print space |
| 134 | +; call DRIVER |
| 135 | +; ret c ; stop if row changed (reached column zero) |
| 136 | +; ld a, (hl) |
| 137 | +; and 0x0f |
| 138 | +; ret z ; stop if reached column 16, 32 or 48 |
| 139 | +; jr LOOP ; repeat otherwise |
| 140 | +; #endif _STANDARD_COMMA |
| 141 | +; ----------------------------------------------------------------------------- |
| 142 | + |
| 143 | +; ----------------------------------------------------------------------------- |
| 144 | +; 64-COLUMN DISPLAY DRIVER |
| 145 | + |
| 146 | +DRIVER: |
| 147 | + |
| 148 | + push hl ; save AT_COL address for later |
| 149 | + ld e, a ; store character value in E |
| 150 | + ld c, (hl) ; store current column in BC |
| 151 | + |
| 152 | +; Check if character font must be rotated, self-modifying the code accordingly |
| 153 | + |
| 154 | + xor c ; compare BIT 0 from character value and column |
| 155 | + rra |
| 156 | + ld a, 256-(END_LOOP-SKIP_RLC) ; instruction DJNZ skipping rotation |
| 157 | + jr nc, NOT_RLC ; decide based on BIT 0 comparison |
| 158 | + ld a, 256-(END_LOOP-INIT_RLC) ; instruction DJNZ using rotation |
| 159 | +NOT_RLC: |
| 160 | + ld (END_LOOP - 1), a ; modify DJNZ instruction directly |
| 161 | + |
| 162 | +; Check the half screen byte to be changed, self-modifying the code accordingly |
| 163 | + |
| 164 | + srl c ; check BIT 0 from current column |
| 165 | + ld a, %00001111 ; mask to change left half of the screen byte |
| 166 | + jr nc, SCR_LEFT ; decide based on odd or even column |
| 167 | + cpl ; mask to change right half of the screen byte |
| 168 | +SCR_LEFT: |
| 169 | + ld (SCR_MASK + 1), a ; modify screen mask value directly |
| 170 | + cpl |
| 171 | + ld (FONT_MASK + 1), a ; modify font mask value directly |
| 172 | + |
| 173 | +; Calculate location of the first byte to be changed on screen |
| 174 | +; The row value is a 5 bits value (0-23), here represented as %000RRrrr |
| 175 | +; The column value is a 6 bits value (0-63), here represented as %00CCCCCc |
| 176 | +; Formula: 0x4000 + ((row & 0x18) << 8) + ((row & 0x07) << 5) + (col >> 1) |
| 177 | + |
| 178 | + inc hl ; now HL references AT_ROW address |
| 179 | + ld a, (hl) ; now A = %000RRrrr |
| 180 | + call 0x0e9e ; now HL = %010RR000rrr00000 |
| 181 | + add hl, bc ; now HL = %010RR000rrrCCCCC |
| 182 | + ex de, hl ; now DE = %010RR000rrrCCCCC |
| 183 | + |
| 184 | +; Calculate location of the character font data in FONT_ADDR |
| 185 | +; Formula: FONT_ADDR + 7 * INT ((char-32)/2) - 1 |
| 186 | + |
| 187 | + ld h, b ; now HL = char |
| 188 | + srl l ; now HL = INT (char/2) |
| 189 | + ld c, l ; now BC = INT (char/2) |
| 190 | + add hl, hl ; now HL = 2 * INT (char/2) |
| 191 | + add hl, hl ; now HL = 4 * INT (char/2) |
| 192 | + add hl, hl ; now HL = 8 * INT (char/2) |
| 193 | + sbc hl, bc ; now HL = 7 * INT (char/2) |
| 194 | + ld bc, FONT_ADDR - 0x71 |
| 195 | + add hl, bc ; now HL = FONT_ADDR + 7 * INT (char/2) - 0x71 |
| 196 | + |
| 197 | +; Main loop to copy 8 font bytes into screen (1 blank + 7 from font data) |
| 198 | + |
| 199 | + xor a ; first font byte is always blank |
| 200 | + ld b, 8 ; execute loop 8 times |
| 201 | +INIT_RLC: |
| 202 | + rlca ; switch position between bits 0-3 and bits 4-7 |
| 203 | + rlca |
| 204 | + rlca |
| 205 | + rlca |
| 206 | +SKIP_RLC: |
| 207 | + |
| 208 | +; ----------------------------------------------------------------------------- |
| 209 | +; UNCOMMENT TO ENABLE STANDARD INVERSE |
| 210 | +; |
| 211 | +; #ifdef _STANDARD_INVERSE |
| 212 | +;INV_C: nop ; either 'NOP' or 'CPL' |
| 213 | +; #endif _STANDARD_INVERSE |
| 214 | +; ----------------------------------------------------------------------------- |
| 215 | + |
| 216 | +FONT_MASK: |
| 217 | + and %11110000 ; mask half of the font byte |
| 218 | + ld c, a ; store half of the font byte in C |
| 219 | + ld a, (de) ; get screen byte |
| 220 | +SCR_MASK: |
| 221 | + and %00001111 ; mask half of the screen byte |
| 222 | + or c ; combine half screen and half font |
| 223 | + ld (de), a ; write result back to screen |
| 224 | + inc d ; next screen location |
| 225 | + inc hl ; next font data location |
| 226 | + ld a, (hl) ; store next font byte in A |
| 227 | + djnz INIT_RLC ; repeat loop 8 times |
| 228 | +END_LOOP: |
| 229 | + |
| 230 | + pop hl ; restore AT_COL address |
| 231 | + inc (hl) ; next column |
| 232 | + bit 6, (hl) ; column lower than 64? |
| 233 | + ret z ; return if so |
| 234 | +NEXT_ROW: |
| 235 | + ld (hl), b ; reset AT_COL |
| 236 | + inc hl ; store AT_ROW address in HL |
| 237 | + inc (hl) ; next row |
| 238 | + ld a, (hl) |
| 239 | + cp 24 ; row lower than 23? |
| 240 | + ret c ; return if so |
| 241 | + ld (hl), b ; reset AT_ROW |
| 242 | + ret ; done! |
| 243 | + |
| 244 | +; ----------------------------------------------------------------------------- |
| 245 | +; LOCAL VARIABLES |
| 246 | + |
| 247 | +AT_FLAG: |
| 248 | + defb 0 ; flag to control processing keyword 'AT' |
| 249 | + ; value 2 if received 'AT', expecting row |
| 250 | + ; value 1 if received row, expecting column |
| 251 | + ; value 0 if expecting regular character |
| 252 | +AT_COL: |
| 253 | + defb 0 ; current column position (0-31) |
| 254 | +AT_ROW: |
| 255 | + defb 0 ; current row position (0-23) |
| 256 | + |
| 257 | +; ----------------------------------------------------------------------------- |
| 258 | +; HALF WIDTH 4x8 FONT designed by Andrew Owen |
| 259 | +; Top row is always zero and not stored (96 chars x 7 / 2 = 336 bytes) |
| 260 | + |
| 261 | +FONT_ADDR: |
| 262 | + defb 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x00 ; ! |
| 263 | + defb 0x52, 0x57, 0x02, 0x02, 0x07, 0x02, 0x00 ;"# |
| 264 | + defb 0x25, 0x71, 0x62, 0x32, 0x74, 0x25, 0x00 ;$% |
| 265 | + defb 0x22, 0x42, 0x20, 0x40, 0x50, 0x30, 0x00 ;&' |
| 266 | + defb 0x14, 0x22, 0x41, 0x41, 0x41, 0x22, 0x14 ;() |
| 267 | + defb 0x20, 0x70, 0x22, 0x57, 0x02, 0x00, 0x00 ;*+ |
| 268 | + defb 0x00, 0x00, 0x00, 0x07, 0x00, 0x20, 0x20 ;,- |
| 269 | + defb 0x01, 0x01, 0x02, 0x02, 0x04, 0x24, 0x00 ;./ |
| 270 | + defb 0x22, 0x56, 0x52, 0x52, 0x52, 0x27, 0x00 ;01 |
| 271 | + defb 0x27, 0x51, 0x12, 0x21, 0x45, 0x72, 0x00 ;23 |
| 272 | + defb 0x57, 0x54, 0x56, 0x71, 0x15, 0x12, 0x00 ;45 |
| 273 | + defb 0x17, 0x21, 0x61, 0x52, 0x52, 0x22, 0x00 ;67 |
| 274 | + defb 0x22, 0x55, 0x25, 0x53, 0x52, 0x24, 0x00 ;89 |
| 275 | + defb 0x00, 0x00, 0x22, 0x00, 0x00, 0x22, 0x02 ;:; |
| 276 | + defb 0x00, 0x10, 0x27, 0x40, 0x27, 0x10, 0x00 ;<= |
| 277 | + defb 0x02, 0x45, 0x21, 0x12, 0x20, 0x42, 0x00 ;>? |
| 278 | + defb 0x23, 0x55, 0x75, 0x77, 0x45, 0x35, 0x00 ;@A |
| 279 | + defb 0x63, 0x54, 0x64, 0x54, 0x54, 0x63, 0x00 ;BC |
| 280 | + defb 0x67, 0x54, 0x56, 0x54, 0x54, 0x67, 0x00 ;DE |
| 281 | + defb 0x73, 0x44, 0x64, 0x45, 0x45, 0x43, 0x00 ;FG |
| 282 | + defb 0x57, 0x52, 0x72, 0x52, 0x52, 0x57, 0x00 ;HI |
| 283 | + defb 0x35, 0x15, 0x16, 0x55, 0x55, 0x25, 0x00 ;JK |
| 284 | + defb 0x45, 0x47, 0x45, 0x45, 0x45, 0x75, 0x00 ;LM |
| 285 | + defb 0x62, 0x55, 0x55, 0x55, 0x55, 0x52, 0x00 ;NO |
| 286 | + defb 0x62, 0x55, 0x55, 0x65, 0x45, 0x43, 0x00 ;PQ |
| 287 | + defb 0x63, 0x54, 0x52, 0x61, 0x55, 0x52, 0x00 ;RS |
| 288 | + defb 0x75, 0x25, 0x25, 0x25, 0x25, 0x22, 0x00 ;TU |
| 289 | + defb 0x55, 0x55, 0x55, 0x55, 0x27, 0x25, 0x00 ;VW |
| 290 | + defb 0x55, 0x55, 0x25, 0x22, 0x52, 0x52, 0x00 ;XY |
| 291 | + defb 0x73, 0x12, 0x22, 0x22, 0x42, 0x72, 0x03 ;Z[ |
| 292 | + defb 0x46, 0x42, 0x22, 0x22, 0x12, 0x12, 0x06 ;\] |
| 293 | + defb 0x20, 0x50, 0x00, 0x00, 0x00, 0x00, 0x0f ;^_ |
| 294 | + defb 0x20, 0x10, 0x03, 0x05, 0x05, 0x03, 0x00 ;�a |
| 295 | + defb 0x40, 0x40, 0x63, 0x54, 0x54, 0x63, 0x00 ;bc |
| 296 | + defb 0x10, 0x10, 0x32, 0x55, 0x56, 0x33, 0x00 ;de |
| 297 | + defb 0x10, 0x20, 0x73, 0x25, 0x25, 0x43, 0x06 ;fg |
| 298 | + defb 0x42, 0x40, 0x66, 0x52, 0x52, 0x57, 0x00 ;hi |
| 299 | + defb 0x14, 0x04, 0x35, 0x16, 0x15, 0x55, 0x20 ;jk |
| 300 | + defb 0x60, 0x20, 0x25, 0x27, 0x25, 0x75, 0x00 ;lm |
| 301 | + defb 0x00, 0x00, 0x62, 0x55, 0x55, 0x52, 0x00 ;no |
| 302 | + defb 0x00, 0x00, 0x63, 0x55, 0x55, 0x63, 0x41 ;pq |
| 303 | + defb 0x00, 0x00, 0x53, 0x66, 0x43, 0x46, 0x00 ;rs |
| 304 | + defb 0x00, 0x20, 0x75, 0x25, 0x25, 0x12, 0x00 ;tu |
| 305 | + defb 0x00, 0x00, 0x55, 0x55, 0x27, 0x25, 0x00 ;vw |
| 306 | + defb 0x00, 0x00, 0x55, 0x25, 0x25, 0x53, 0x06 ;xy |
| 307 | + defb 0x01, 0x02, 0x72, 0x34, 0x62, 0x72, 0x01 ;z{ |
| 308 | + defb 0x24, 0x22, 0x22, 0x21, 0x22, 0x22, 0x04 ;|} |
| 309 | + defb 0x56, 0xa9, 0x06, 0x04, 0x06, 0x09, 0x06 ;~� |
| 310 | + |
| 311 | +; ----------------------------------------------------------------------------- |
| 312 | +; NOTE: Other choices for 4x8 fonts designed by Einar Saukas available on tape! |
| 313 | +; ----------------------------------------------------------------------------- |
0 commit comments