1; m/c entry point for print string function 2; 02.2008 aralbrec, Sprite Pack v3.0 3; zx81 hi-res version 4 5PUBLIC SP1PrintString 6 7EXTERN sp1_GetUpdateStruct_callee, l_jpix 8EXTERN ASMDISP_SP1_GETUPDATESTRUCT_CALLEE 9EXTERN SP1V_UPDATELISTT, SP1V_DISPWIDTH 10EXTERN SP1V_TEMP_IX, SP1V_TEMP_AF 11 12; A sophisticated print string function 13; 14; The string to print is pointed at by HL and terminates with a 0 byte. 15; Characters are printed as background tiles 16; with the following special character codes understood: 17; 18; code meaning 19; ---- ------- 20; 0 terminate string 21; 1 NOP N* [other ports: logically AND into attribute mask N*] 22; 2 gotoy N* (goto y coordinate on same column) 23; 3 ywrap N* 24; 4 NOP N* [other ports: attribute mask N*] 25; 5 16-bit tile code follows W* 26; 6 gotox N* (goto x coordinate on same line) 27; 7 print string at address W* (like a subroutine call / macro for printstrings; see also 26,28) 28; 8 left 29; 9 right 30; 10 up 31; 11 down 32; 12 home (to top left of bounds rectangle) 33; 13 carriage return (move to start of next line in bounds rectangle) 34; 14 repeat N* 35; 15 endrepeat 36; 16 NOP N* [other ports: ink N*] 37; 17 NOP N* [other ports: paper N*] 38; 18 NOP N* [other ports: flash N*] 39; 19 NOP N* [other ports: bright N*] 40; 20 NOP N* [other ports: attribute N*] 41; 21 invalidate N* 42; 22 AT y(N*),x(N*) (relative to bounds rectangle) 43; 23 AT dy(N*), dx(N*) (relative to current position in bounds rectangle) 44; 24 xwrap N* 45; 25 yinc N* 46; 26 push state 47; 27 escape, next char is literal not special code 48; 28 pop state 49; 29 transparent char 50; 30 NOP N* [other ports: logically OR into attribute mask N*] 51; 31 visit : call function pointed at by ix with current struct_sp1_update as parameter 52; 53; * N is a single byte parameter following the code. 54; * W is a 16-bit parameter following the code. 55; 56; All printing is done within a bounds rectangle. No printing outside this 57; bounds rectangle will occur. 58; 59; enter: HL = address of string to print 60; E = flags (bit 0 = invalidate?, bit 1 = xwrap?, bit 2 = yinc?, bit3 = ywrap?) 61; B = current x coordinate (relative to bounds rect IX) 62; C = current y coordinate (relative to bounds rect IX) 63; ( HL' = & tail struct sp1_update.ulist in invalidated list ) loaded here, not entry condition 64; DE' = current struct sp1_update * 65; (SP1V_TEMP_IX) = visit function 66; IX+0 = row coordinate \ 67; IX+1 = col coordinate | Bounds Rectangle 68; IX+2 = width in chars | Must Fit On Screen 69; IX+3 = height in chars / 70; exit : same as enter 71; uses : all except af', iy 72; 73; The C API maintains a structure to retain the print state between calls. 74; Doing something similar from assembly language may be helpful. 75 76.SP1PrintString 77 78 exx 79 ld hl,(SP1V_UPDATELISTT) 80 ld bc,5 81 add hl,bc 82 exx 83 84 call psloop 85 86 exx 87 ld (hl),0 88 ld bc,-5 89 add hl,bc 90 ld (SP1V_UPDATELISTT),hl 91 exx 92 ret 93 94.psloop 95 96 ld a,(hl) 97 or a 98 ret z 99 100 inc hl 101 cp 32 102 jp nc, printable 103 104 ; here we have a special code [1,31] 105 106 push hl 107 ld d,a 108 add a,a ; get address of handler from jump table 109 ld h,jumptbl/256 110 add a,jumptbl%256 111 ld l,a 112 jp nc, nospill 113 inc h 114.nospill 115 ld a,(hl) 116 inc hl 117 ld h,(hl) 118 ld l,a 119 ld a,d ; restore A to code 120 ex (sp),hl 121 ret 122 123.jumptbl 124 defw NOP0, NOP1, codeGotoY, codeYWrap 125 defw NOP1, codeTC, codeGotoX, codePString 126 defw codeLeft, codeRight, codeUp, codeDown 127 defw codeHome, codeReturn, codeRepeat, codeEndRepeat 128 defw NOP1, NOP1, NOP1, NOP1 129 defw NOP1, codeInvalidate, codeAt, codeAtRel 130 defw codeXWrap, codeYInc, codePush, codeEscape 131 defw codePop, codeTransparent, NOP1, codeVisit 132 133.NOP1 134 135 inc hl ; consume a single byte parameter 136 137 ; fall through to NOP0 138 139.NOP0 140 141 jp psloop ; on to the next byte to interpret 142 143.codeVisit 144 ld a,b ; only visit if inbounds 145 cp (ix+2) 146 jp nc, psloop 147 ld a,c 148 cp (ix+3) 149 jp nc, psloop 150 151 push ix 152 push bc 153 push de 154 push hl 155 exx 156 push hl 157 push de 158 ex de,hl 159 ld ix,(SP1V_TEMP_IX) 160 call l_jpix 161 pop de 162 pop hl 163 exx 164 pop hl 165 pop de 166 pop bc 167 pop ix 168 jp psloop 169 170.codeYWrap 171 ld a,(hl) ; parameter following YWRAP (0/1) 172 inc hl 173 rra 174 jp nc, noywrap 175 set 3,e 176 jp psloop 177.noywrap 178 res 3,e 179 jp psloop 180 181.codeEscape 182 ld a,(hl) ; char following ESCAPE 183 inc hl 184 jp printable 185 186.codePop 187 exx 188 pop de 189 exx 190 pop de 191 pop bc 192 jp psloop 193 194.codePush 195 push bc 196 push de 197 exx 198 push de 199 exx 200 jp psloop 201 202.codeYInc 203 ld a,(hl) ; parameter following YINC (0/1) 204 inc hl 205 rra 206 jp nc, noywrap5 207 set 2,e 208 jp psloop 209.noywrap5 210 res 2,e 211 jp psloop 212 213.codeXWrap 214 ld a,(hl) ; parameter following XWRAP (0/1) 215 inc hl 216 rra 217 jp nc, noxwrap 218 set 1,e 219 jp psloop 220.noxwrap 221 res 1,e 222 jp psloop 223 224.codeAtRel 225 ld a,(hl) 226 add a,c 227 ld c,a 228 inc hl 229 ld a,(hl) 230 add a,b 231 ld b,a 232 inc hl 233 jp computenewupdate 234 235.codeAt 236 ld c,(hl) 237 inc hl 238 ld b,(hl) 239 inc hl 240 jp computenewupdate 241 242.codeGotoX 243 244 ld b,(hl) 245 inc hl 246 jp computenewupdate 247 248.codeGotoY 249 250 ld c,(hl) 251 inc hl 252 jp computenewupdate 253 254.codePString 255 ld a,(hl) 256 inc hl 257 ld d,(hl) 258 inc hl 259 push hl 260 ld h,d 261 ld l,a 262 call psloop 263 pop hl 264 jp psloop 265 266.codeInvalidate 267 ld a,(hl) ; parameter following INVALIDATE (0/1) 268 inc hl 269 srl e 270 rra 271 rl e 272 jp psloop 273 274.codeHome 275 276 ld bc,0 277 jp computenewupdate 278 279.codeReturn 280 281 ld b,0 282 inc c 283 jp computenewupdate 284 285.codeRepeat 286 287 ld a,(hl) ; # times to repeat 288 inc hl 289.reploop 290 push hl ; save string position at start of repeat block 291 push af ; save # remaining iterations 292 call psloop ; returns after endrepeat or 0 terminator 293 pop af 294 dec a ; any more iterations? 295 jr z, nomoreiter 296 pop hl ; restore string pointer for next repeat 297 jp reploop 298.nomoreiter 299 pop af ; trash saved string position 300 jp psloop 301 302.codeEndRepeat 303 304 ret 305 306.codeTC ; a 16 bit tile code follows 307 308 ; first check if in bounds 309 310 ld a,b 311 cp (ix+2) 312 jr nc, codeRight2 313 ld a,c 314 cp (ix+3) 315 jr nc, codeRight2 316 317 ; are we invalidating? 318 319 bit 0,e 320 push hl 321 exx 322 jr z, noinvalidation2 323 324 ; invalidate the char 325 326 ld a,(de) 327 xor $80 328 jp p, noinvalidation2 ; if already invalidated, skip 329 ld (de),a 330 ld (hl),d 331 inc hl 332 ld (hl),e 333 ld hl,5 334 add hl,de 335 336.noinvalidation2 337 338 pop bc ; bc = & 16-bit tile 339 ld a,(bc) 340 inc de 341 ld (de),a ; write tile into sp1_update 342 inc bc 343 ld a,(bc) 344 inc de 345 ld (de),a 346 dec de 347 dec de 348 349 exx 350 351.codeRight2 ; skip 16-bit tile code in string 352 353 inc hl 354 inc hl 355 356 ; advance to next char on right 357 358 jp codeRight 359 360.codeTransparent 361 362 ; are we invalidating? 363 364 bit 0,e 365 jp z, codeRight 366 367 ; invalidate the char 368 369 exx 370 ld a,(de) 371 xor $80 372 jp p, noinvalidation20 ; if already invalidated, skip 373 ld (de),a 374 ld (hl),d 375 inc hl 376 ld (hl),e 377 ld hl,5 378 add hl,de 379 380.noinvalidation20 381 382 exx 383 jp codeRight 384 385.printable ; a = tile# 386 387 ld (SP1V_TEMP_AF + 1),a 388 389 ; first check if in bounds 390 391 ld a,b 392 cp (ix+2) 393 jr nc, codeRight 394 ld a,c 395 cp (ix+3) 396 jr nc, codeRight 397 398 ; are we invalidating? 399 400 bit 0,e 401 exx 402 jr z, noinvalidation 403 404 ; invalidate the char 405 406 ld a,(de) 407 xor $80 408 jp p, noinvalidation ; if already invalidated, skip 409 ld (de),a 410 ld (hl),d 411 inc hl 412 ld (hl),e 413 ld hl,5 414 add hl,de 415 416.noinvalidation 417 418 ex de,hl 419 inc hl 420 ld a,(SP1V_TEMP_AF + 1) 421 ld (hl),a ; store tile 422 inc hl 423 ld (hl),0 424 425 ; advance to next char on right 426 427 dec hl 428 dec hl 429 ex de,hl 430 exx 431 432.codeRight 433 434 inc b ; increase x coord 435 bit 1,e ; are we doing x wrap? 436 jr nz, yesxwrap 437 438.inxbounds 439 440 exx ; move update struct to right 441 ld a,9 442 add a,e 443 ld e,a 444 jp nc, noinc1 445 inc d 446.noinc1 447 exx 448 449 jp psloop 450 451.yesxwrap 452 453 ld a,b 454 cp (ix+2) 455 jr c, inxbounds 456 ld b,0 457 458 bit 2,e ; are we doing yinc? 459 jr z, noyinc 460 inc c 461 462 bit 3,e ; are we doing ywrap? 463 jr z, noyinc 464 ld a,c 465 cp (ix+3) 466 jr c, noyinc 467 ld c,0 468 469.noyinc 470.computenewupdate 471 472 ; need to compute struct sp1_update for new coords 473 474 push bc 475 exx 476 ex (sp),hl 477 ld a,(ix+0) 478 add a,l 479 ld d,a 480 ld a,(ix+1) 481 add a,h 482 ld e,a 483 call sp1_GetUpdateStruct_callee + ASMDISP_SP1_GETUPDATESTRUCT_CALLEE 484 ex de,hl 485 pop hl 486 exx 487 488 jp psloop 489 490.codeLeft 491 492 dec b 493 bit 1,e 494 jr nz, yesxwrap2 495 496.inxbounds2 497 498 exx 499 ld a,-9 500 add a,e 501 ld e,a 502 ld a,$ff 503 adc a,d 504 ld d,a 505 exx 506 jp psloop 507 508.yesxwrap2 509 510 ld a,b 511 cp (ix+2) 512 jr c, inxbounds2 513 ld b,(ix+2) 514 dec b 515 516 bit 2,e 517 jr z, computenewupdate 518 dec c 519 520 bit 3,e 521 jr z, computenewupdate 522 523 ld a,c 524 cp (ix+3) 525 jr c, computenewupdate 526 ld c,(ix+3) 527 dec c 528 jp computenewupdate 529 530.codeUp 531 532 dec c 533 bit 3,e 534 jr nz, yesywrap 535 536.inybounds 537 538 exx 539 ld a,+(-SP1V_DISPWIDTH*9) & 0xff 540 add a,e 541 ld e,a 542 543 ld a,-SP1V_DISPWIDTH*9/256 544; ld a,+(((SP1V_DISPWIDTH*9):$ffff)+1)/256 545 546 adc a,d 547 ld d,a 548 exx 549 jp psloop 550 551.yesywrap 552 553 ld a,c 554 cp (ix+3) 555 jr c, inybounds 556 ld c,(ix+3) 557 dec c 558 jp computenewupdate 559 560.codeDown 561 562 inc c 563 bit 3,e 564 jr nz, yesywrap2 565 566.inybounds2 567 568 exx 569 ld a,0+(SP1V_DISPWIDTH*9)%256 570 add a,e 571 ld e,a 572 ld a,0+(SP1V_DISPWIDTH*9)/256 573 adc a,d 574 ld d,a 575 exx 576 jp psloop 577 578.yesywrap2 579 580 ld a,c 581 cp (ix+3) 582 jr c, inybounds2 583 ld c,0 584 jp computenewupdate 585