; vml.s - machine code helpers for the c64 vim clone (called from power c). ; ; Assembled for $C000. vml.c holds the bytes; mlinit() calls highmem($C000) ; so power c keeps its heap and stack below, then copies the code up here. ; Parameters are written by C into a block at $C400 (PB) before each call; ; entry addresses come from the ld65 label file (see build-ml.ps1). ; ; Zero page: 34-42 ($22-$2A), 75-96 ($4B-$60) and 251-252 ($FB-$FC) are the ; temporaries power c leaves to machine language - but sys() itself is using ; some of them while our code runs (it keeps where to store A/X/Y back), so ; every entry that touches zero page saves all three ranges first (zsave) and ; puts them back before it returns (zrest). Without that, sys() wrote X/Y ; through our screen pointer and then jumped into zero page. ; ; Entries ; scrn paint screen rows r0..r1 straight from the buffer and line index ; row paint one row from a byte string (the status row) ; bmove overlapping block move (memmove) ; ladj add a 16 bit delta to n consecutive words (line index fix-up) ; kpop take one key from the kernal keyboard buffer, irqs masked: A = key or 0 SAVE = $C410 ; zsave's copy of the zero page we use PB = $C400 ; above the code; NOT $CF00 - power c's stack lives under $D000 ; paint's inputs src = $22 ; 2 text to show (petscii) len = $24 ; 1 bytes of it, 0..40 dst = $25 ; 2 screen address of the row cur = $27 ; 1 column to invert, $FF = none rev = $28 ; 1 $00 normal, $80 whole row reverse endp = $29 ; 2 scrn: end offset of the line ; scrn's copy of its parameter block: PB+0..PB+15 -> $4B..$5A bfr = $4B ; 2 buffer address lptr = $4D ; 2 address of lptr[0] nl = $4F ; 2 number of lines top = $51 ; 2 first line on screen blen = $53 ; 2 bytes in buffer lcol = $55 ; 2 horizontal scroll crow = $57 ; 1 screen row of the cursor ($FF none) ccol = $58 ; 1 screen column of the cursor r0 = $59 ; 1 first row to paint r1 = $5A ; 1 last row to paint (inclusive) ln = $5B ; 2 line being painted lp = $5D ; 2 -> lptr[ln] rowc = $5F ; 1 screen row being painted st = $FB ; 2 start offset of the visible text .segment "CODE" ; ---------------------------------------------------------------- scrn scrn: jsr zsave ldx #15 @cp: lda PB,x sta bfr,x dex bpl @cp clc ; ln = top + r0 lda top adc r0 sta ln lda top+1 adc #0 sta ln+1 lda ln ; lp = lptr + 2*ln asl a sta lp lda ln+1 rol a sta lp+1 clc lda lp adc lptr sta lp lda lp+1 adc lptr+1 sta lp+1 lda #0 ; dst = $0400 + 40*r0 sta dst lda #4 sta dst+1 ldx r0 beq @d1 @dl: clc lda dst adc #40 sta dst bcc @dn inc dst+1 @dn: dex bne @dl @d1: lda r0 sta rowc @row: lda #0 sta rev sta len lda #$FF sta cur lda ln ; past the last line: a blank row cmp nl lda ln+1 sbc nl+1 bcs @paint ldy #0 ; st = lptr[ln] lda (lp),y sta st iny lda (lp),y sta st+1 clc ; is ln the last line? lda ln adc #1 tax lda ln+1 adc #0 cpx nl bne @mid cmp nl+1 bne @mid lda blen ; last line ends at blen sta endp lda blen+1 sta endp+1 jmp @gotend @mid: ldy #2 ; else at lptr[ln+1] - 1 (before the $0d) lda (lp),y sec sbc #1 sta endp iny lda (lp),y sbc #0 sta endp+1 @gotend: clc ; st += lcol lda st adc lcol sta st lda st+1 adc lcol+1 sta st+1 sec ; len = min(40, end - st), 0 if negative lda endp sbc st tax lda endp+1 sbc st+1 bcc @src ; scrolled past the end of this line bne @full cpx #41 bcc @setn @full: ldx #40 @setn: stx len @src: clc ; src = bfr + st lda bfr adc st sta src lda bfr+1 adc st+1 sta src+1 lda rowc ; the cursor row? cmp crow bne @paint lda ccol sta cur @paint: jsr paint lda rowc ; next row, until r1 is done cmp r1 bcs @done inc rowc inc ln bne @l1 inc ln+1 @l1: clc lda lp adc #2 sta lp bcc @l2 inc lp+1 @l2: clc lda dst adc #40 sta dst bcc @l3 inc dst+1 @l3: jmp @row @done: jmp zrest ; ---------------------------------------------------------------- row ; PB+0 src, PB+2 len, PB+3 dst, PB+5 cur, PB+6 rev row: jsr zsave ldx #6 @cp: lda PB,x sta src,x dex bpl @cp jsr paint jmp zrest ; ---------------------------------------------------------------- paint ; clear the row to blanks, copy len bytes converted petscii -> screen code, ; then invert the cursor cell paint: lda rev ora #$20 ldy #39 @clr: sta (dst),y dey bpl @clr ldy #0 @cp: cpy len beq @cur lda (src),y cmp #$20 bcs @ok lda #$20 ; control codes show as a blank @ok: cmp #$C0 bcc @l1 sbc #$80 bcs @st @l1: cmp #$A0 bcc @l2 sbc #$40 bcs @st @l2: cmp #$60 bcc @l3 sbc #$20 bcs @st @l3: cmp #$40 bcc @st sbc #$40 @st: ora rev sta (dst),y iny bne @cp @cur: lda cur cmp #40 bcs @fin ; $FF (none) or off screen tay lda (dst),y eor #$80 sta (dst),y @fin: rts ; ---------------------------------------------------------------- bmove ; PB+0 from, PB+2 to, PB+4 count. Overlap safe. mfrom = $22 mto = $25 mcnt = $4B bmove: jsr zsave lda PB sta mfrom lda PB+1 sta mfrom+1 lda PB+2 sta mto lda PB+3 sta mto+1 lda PB+4 sta mcnt lda PB+5 sta mcnt+1 lda mto ; to < from: copy upwards from the start cmp mfrom lda mto+1 sbc mfrom+1 bcc @fwd ; to >= from: copy downwards from the end clc ; point both at their last whole page lda mfrom+1 adc mcnt+1 sta mfrom+1 clc lda mto+1 adc mcnt+1 sta mto+1 ldy mcnt ; the odd bytes at the top first beq @bpg @bodd: dey lda (mfrom),y sta (mto),y cpy #0 bne @bodd @bpg: ldx mcnt+1 ; then whole pages, highest first beq @out @bnext: dec mfrom+1 dec mto+1 ldy #0 @bpl: dey lda (mfrom),y sta (mto),y cpy #0 bne @bpl dex bne @bnext @out: jmp zrest @fwd: ldy #0 ldx mcnt+1 ; whole pages beq @fodd @fpg: lda (mfrom),y sta (mto),y iny bne @fpg inc mfrom+1 inc mto+1 dex bne @fpg @fodd: ldx mcnt ; then the rest beq @out @fo: lda (mfrom),y sta (mto),y iny dex bne @fo jmp zrest ; ---------------------------------------------------------------- ladj ; PB+0 address of the first word, PB+2 how many, PB+4 delta aptr = $22 acnt = $4B adel = $4D ladj: jsr zsave ldx #5 @cp: lda PB,x sta $4B,x dex bpl @cp lda $4B ; the address goes to $22 for (zp),y sta aptr lda $4C sta aptr+1 lda $4D ; count to acnt, delta to adel sta acnt lda $4E sta acnt+1 lda $4F sta adel lda $50 sta adel+1 @lp: lda acnt ora acnt+1 beq @done ldy #0 clc lda (aptr),y adc adel sta (aptr),y iny lda (aptr),y adc adel+1 sta (aptr),y clc lda aptr adc #2 sta aptr bcc @nc inc aptr+1 @nc: lda acnt bne @dc dec acnt+1 @dc: dec acnt jmp @lp @done: jmp zrest ; ---------------------------------------------------------------- kpop kpop: sei lda $C6 beq @none ldx #0 lda $0277 pha @sh: lda $0278,x sta $0277,x inx cpx #9 bne @sh dec $C6 pla cli rts @none: cli lda #0 rts ; ---------------------------------------------------------------- zsave / zrest ; $22-$2A -> SAVE+0..8, $4B-$60 -> SAVE+9..30, $FB-$FC -> SAVE+31..32 zsave: ldx #8 @a: lda $22,x sta SAVE,x dex bpl @a ldx #21 @b: lda $4B,x sta SAVE+9,x dex bpl @b lda $FB sta SAVE+31 lda $FC sta SAVE+32 rts zrest: ldx #8 @a: lda SAVE,x sta $22,x dex bpl @a ldx #21 @b: lda SAVE+9,x sta $4B,x dex bpl @b lda SAVE+31 sta $FB lda SAVE+32 sta $FC rts .export scrn, row, bmove, ladj, kpop