title 'Revised screen handler for Kaypro 4-83 85/6/13' ; Requires z80asm to handle the external byte variables? .z80 ; ; Access to this module entry vidinit; initialize entry vidout; output (c) to screen ; ; System constants extrn bitport; bank switching, Kaypro extrn bankbit; how to bank switch ; ; Connection to other areas extrn .kbdout; (c) to keyboard, for bells ; ; Connection to the actual video memory extrn scrnbas; 3000h, address of 1st screen line extrn oneline; 128, storage used per line extrn maxln; 24, lines on screen available ; ; Computed from above, must be external for assembler extrn endline; address of last screen line ; scrnbas + (maxln-1) * oneline extrn line25; base address past screen shr 8 ; (endline + oneline) shr 8 extrn colmask; oneline - 1 ; ; This emulates the original Kaypro/ADM3A protocol, with some ; additions and refinements. The video RAM is organized as ; 24 lines of 128 chars, but chars past column 80 must not be ; written. The control and escape sequences are an amalgamation ; of those in the original Kaypro, those used with SWP's CoPower ; 88 on the Kaypro, and extensions. The characteristics of some ; control sequences are altered, e.g. control of column position, ; line wrap only occurs when a character is output past column ; 80, etc. Characters with high order bits set are used directly ; for display, not as controls. ; Copyright (c) 85/6/13 by C.B. Falconer ; ; 85/6/18. vidout now returns current cursor position in hl ; ; Control Char usage ( * all not mentioned are normally ignored) ; bel ^G Ring bell ; bs ^H cursor left (not past start of line) ; * tab ^I cursor right to tab pt (not past end of line) ; lf ^J cursor down. Scroll at bottom, keep column ; vt ^K cursor up. Scroll at top, keep column ; ff ^L cursor right one (not past end of line) ; cr ^M cursor to left of current line ; etb ^W Clear from cursor to end of screen inclusive ; can ^X clear cursor to end of line inclusive ; sub ^Z clear screen and home cursor ; esc ^[ SEE BELOW ; rs ^6 Home cursor to top left. Do not clear ; * us ^- Next single char not a control, display only ; ; Escape sequences, all begin with esc (01bh) ; * C following char used as cursor marker, flashing. Useful ; ones on Kaypro are DEL, US (=^-), and _. Any allowed ; * D following char used as cursor, not flashing. See above ; E Insert cursor line, scrolling all below down. Keep column ; R Delete cursor line, scrolling all below up. Keep column ; * e Insert blank at cursor posn. Chars off end of line lost ; * r Delete char. at cursor posn. Line closes up ; * ( Set attribute to flash. Further chars. to screen flash ; * ) Set attribute no flash. Further chars do not flash ; = Next two chars position cursor, as before and ADM3 ; * other The esc is discarded, and the char. used as normal. ; ; ( * marks usage not available in original Kaypro ) ; ( The esc ( and esc ) sequences are reversed from the ) ; ( usage on the CoPower88. The original esc A and esc G ) ; ( have been deleted. Use the ^- operation instead. ) ; ( esc C, esc D, esc e, and esc r are totally new. The ) ; ( tab is also new, and has stops every 8 columns. The ) ; ( scroll action on ^J, ^K at screen ends is different, ) ; ( as is the wrap around action at column 80. ) ; true equ -1 false equ not true ; bell equ 07h bs equ 08h tab equ 09h lf equ 0ah vt equ 0bh ff equ 0ch cr equ 0dh etb equ 17h can equ 18h subs equ 1ah esc equ 1bh rs equ 1eh us equ 1fh del equ 7fh ; maxcol equ 80 ; flash equ 080h; video bit causes flashing ; initcur equ del + flash; for light block. ; us + flash; for solid block ; '_' + flash; for original ; remove "+ flash" for steady ; ; Case table entry table macro ch, where db ch dw where endm ; ; ----------------------------------------- ; ; dseg ; extrn vstate, cursor extrn row, cchar, cmark, attrib ; ; data area. The Kaypro Screen memory has 128 bytes of unused ; space, in 8 blocks of 16 (peculiar addressing). This storage ; should eventually be placed there for privacy. ;vstate: ds 2; init to vnorm ;cursor: ds 2; location of cursor on screen ;row: ds 1; 1st arg in "esc=yx" sequence ;cchar: ds 1; char for cursor locn ;cmark: ds 1; cursor char for screen for blank ;attrib: ds 1; Video attrib for new chars, 80h for flash ; ; ------------------------------------ ; cseg ; ; Output (c) to screen; ; Return hl as suitable chars for cursor positioning sequence ; i.e. '=',h,l will restore the cursor to present posn. ; any non executed chars (e.g. esc) will return the present ; cursor without moving it. Thus the value returned at the ; start of a cursor sequence can be used for restoration. ; a := c, f, h,l vidout: push bc push de ld hl,(vstate) call xpchl; execute per current state ld hl,(cursor); return current cursor posn ld de,-scrnbas add hl,de ld a,l and colmask cp maxcol ccf; so carry if = maxcol sbc a,0; If past eol lie, put at eol add a,' '; so that a restore is reasonable ld e,a; x position character add hl,hl ld a,h add a,' ' ld d,a ex de,hl; one off if past end of line pop de pop bc ld a,c ret ; ; for executing "call (hl)" xpchl: jp (hl) ; ; -------- NORMAL processing. Everything else is frills -------- ; ; output the char in (c) to the screen. 8th bits mean display ; the character without interpreting as a control code, etc. ; The video RAM itself uses the 8th bit as a "flash" attribute. ; The system never wraps a line unless there is really more data ; to be placed on the new line. Cursor is user-settable. A ; line feed (or down arrow) on the bottom line preserves the ; column location, while scrolling. Planned window scrolling. ; a,f,b,c,d,e,h,l vnorm: ld a,c or a jp p,vnorm1; no, hi bit, normal use and 07fh; remove hi bit ld c,a jp vdirkt; and use as is vnorm1: inc a and 07fh; map rub into controls cp ' '+1 jp c,vctrl; implement control chars ; " " ; Output char. directly. No control implemention ; a,f,b,d,e,h,l vdirkt: ld hl,vnorm ld (vstate),hl; always reverts to normal call clrcur; no carry if line must wrap, set hl call nc,lnwrap; clears carry if no scroll needed call nc,scroll; only called when off page bottom ; " " ; set character (c) into location (hl), setting cursor at next ; location. If next is past eol then set cursor at eol. At ; entry hl must NOT be outside the range 0..79, i.e. in line ; a,f,h,l putc: ld a,(attrib) or c; set current attribute ld (hl),a; put char on screen ; " " ; Advance cursor hl to next position. Mark screen etc. ; a,f,h,l advcur: inc hl ; " " ; put cursor to location hl. Range may be 0..80 (i.e. off eol) ; a,f,h,l putcur: ld (cursor),hl; save for next use ld a,l; (col = 80 signals off screen) and colmask cp maxcol jp c,setcur; next is not off screen dec hl ; " " ; set cursor at location (hl), known to be on screen ; a,f setcur: ld a,(hl) ld (cchar),a or flash; mark as a cursor cp ' ' + flash jp nz,setc1 ld a,(cmark) setc1: ld (hl),a; put it in screen display ret ; ; --------------- CONTROL Char. Processing ---------------- ; ; Control char input vctrl: ld hl,ctbl; dont optimise, because we call case; need this level on stack ret; if not found, ignore the char ; ; Control char case tabel ctbl: table bell, dobell table bs, goleft table tab, dotab table lf, down table vt, up table ff, right table cr, docr table etb, clreos table can, clreol table subs, clrscrn table rs, home table us, exact table esc, escape db 0ffh; end marker ; ; found in input escape: ld hl,esc1 ; " " ; Set state for next input character next: ld (vstate),hl ret ; ; Accept next character as is, never a control ; h,l exact: ld hl,vdirkt jp next ; ; initialize system ; a,f,h,l vidinit: ld hl,vnorm ld (vstate),hl ld hl,scrnbas ld (cursor),hl ld a,' ' ld (cchar),a ld a,initcur ld (cmark),a xor a ld (attrib),a ; " " ; clear screen. Put cursor at line 0 column 0 ; a,f,h,l clrscrn: call clrcur ld hl,scrnbas push hl clrsc1: call clrln call nxtln jp c,clrsc1; not done yet clrsc2: pop hl; << *** Entry from clreos ** jp putcur ; ; home cursor home: call clrcur ld hl,scrnbas jp putcur ; ; clear from cursor to end of screen, inclusive clreos: call clrcur push hl call c,clrel; Not at eol, clear rest if line clres1: call nxtln jp nc,clrsc2; done remainder call clrln jp clres1; and check next ; ; clear to end of line, from and including current cursor posn ; a,f,h,l clreol: call clrcur jp nc,advcur; at eol already, no work push hl; save cursor for later setting call clrel pop hl jp putcur; and install the marker ; ; Move cursor right ; a,f,h,l right: call clrcur jp advcur ; ; move cursor left, unless at line start ; a,f,h,l goleft: ld hl,(cursor) ld a,l and colmask ret z; at line left, hang call clrcur jp nc,putcur; was past eol, just put it back dec hl jp putcur ; ; move cursor up, maintaining column. May scroll screen down ; a,f,b,c,d,e,h,l up: call clrcur; and ignore line wraps call prvln call nc,scrldn; at screen top. hl points to ln -1 jp putcur; put it back ; ; move cursor down, maintaining column. May scroll screen up ; a,f,b,c,d,e,h,l down: call clrcur; but ignore line wraps call nxtln call nc,scroll; now must scroll, maintaining column jp putcur ; ; put cursor at line left ; a,f,h,l docr: call clrcur ld a,l and not colmask; set to column 0 ld l,a jp putcur ; ; Tab to column modulo 8 ; a,f,h,l dotab: call clrcur jp nc,advcur; was at eol, dont change inc hl; at least one space ld a,l add a,7; max result 87, no carry and not 7; must be <= 80 ld l,a jp putcur ; ; -------------- ESCAPE Sequence processing ----------------- ; ; 1st char after is in c esc1: ld hl,vnorm ld (vstate),hl; default, unless long sequence ld hl,etbl call case; If not found then jp vnorm; discard esc, output char ; ; Escape sequence initial char table etbl: table 'R', deline table 'r', delch table 'E', insline table 'e', insch table '(', setflash table ')', clrflash table 'C', escC table 'D', escD table '=', escEQ db 0ffh; end marker ; ; Cursor with flash. Next char is cursor char escC: ld hl,esc2c jp next ; ; Cursor with no flash. Next char is cursor char escD: ld hl,esc2d jp next ; ; Cursor setting coming. Next char is row escEQ: ld hl,esc2 jp next ; ; Insert blank at cursor position insch: call clrcur jp nc,advcur; already past eol, put it back push hl ld b,(hl) insch1: inc hl ld a,l and colmask cp maxcol jp nc,inschx; done line move ld a,(hl) ld (hl),b ld b,a jp insch1; continue inschx: pop hl ld (hl),' ' jp putcur ; ; Delete char at cursor position delch: call clrcur jp nc,putcur; was past eol, back to end push hl ld d,h ld e,l delch1: inc hl ld a,l and colmask cp maxcol jp nc,delchx; done ld a,(hl) ld (de),a inc de jp delch1 delchx: ex de,hl ld (hl),' ' pop hl jp putcur ; ; insert line at current cursor position. Scroll below down ; a,f,b,c,d,e,h,l insline: call clrcur push hl; save for final cursor setting call dnscrl pop hl jp putcur ; ; delete line at current cursor position. Scroll below up ; a,f,b,c,d,e,h,l deline: call clrcur push hl; save for final cursor setting ld b,endline shr 7; end pointer call mvlns ex de,hl call clrln; clear the bottom line pop hl jp putcur ; ; Setting attribute for flashing ; a setflash: ld a,flash ld (attrib),a ret ; ; Setting attribute for no flashing ; a clrflash: xor a ld (attrib),a ret ; ; setting cursor on esc C (flash) ; a,f,h,l esc2c: ld a,c or flash esc2x: ld (cmark),a call clrcur jp c,esc2xa; not past eol inc hl esc2xa: call putcur; update the cursor style ld hl,vnorm jp next ; ; setting cursor on esc D (no flash) ; a,f,h,l esc2d: ld a,c and not flash jp esc2x ; ; First char after "esc =" recognized. ; a,f,h,l esc2: ld a,c ld (row),a; Save for next char. ld hl,esc3 jp next ; ; Position cursor to column (c), row (row). ; a,f,c,h,l esc3: ld hl,vnorm ld (vstate),hl call clrcur ld a,c sub ' ' esc3a: sub maxcol jp nc,esc3a; col modulo maxcol add a,maxcol rla; compensate for later rra ld c,a ld a,(row) sub ' ' esc3b: sub maxln jp nc,esc3b; row modulo maxln add a,maxln + scrnbas shr 7 or a rra; position, save rh bit ld h,a ld a,c; which goes into col byte rra ld l,a jp putcur ; ; ------------------ SUBROUTINES ------------------ ; ; clear current cursor marker. Return hl = cursor posn ; Return no carry if past eol, when hl is decremented ; a,f,h,l clrcur: ld hl,(cursor) ld a,l and colmask cp maxcol jp c,clrc1; Normal, no line end dec hl clrc1: ld a,(cchar) ld (hl),a; Restore actual char at cursor ret ; ; Wrap cursor hl to start of next line. ; a,f,h,l lnwrap: ld a,l and not colmask ld l,a; point to line start ; " " ; advance to next line, same col. No carry for off screen ; a,f,h,l nxtln: ld a,oneline add a,l ld l,a adc a,h sub l ld h,a cp line25 ret; with carry unless off screen ; ; advance BACKwards to previous line. No carry for off screen ; a,f,h,l prvln: ld a,l sub oneline ld l,a ccf; so similar to nxtln ret c; not off screen, no cy into h dec h; depends on page aligned screen ld a,h cp scrnbas shr 8; carry if too far back ccf; now no carry means off screen ret ; ; PROCEDURE movelines(endline, objective); ; ; WHILE endline <> objective DO BEGIN (* h <> b *) ; destination := endline; (* de := hl *) ; IF destination < objective THEN (* normal scroll *) ; endline := destination + oneline (* screen up *) ; ELSE endline := destination - oneline; (* screen down *) ; moveline(endline, destination); END; ; ; The entry hl and b values are the high bytes of the cursor for ; the respective lines, i.e. actual RAM addresses. At entry: ; hl^ is the farthest line to alter (destination). ; b is the line to empty, left shifted to hold lsb. (objective) ; returns de = start of destination line, h = d. ; a,f,d,e,h,l (*** ENTRY IN MIDDLE ***) mvlns2: ld a,e; h > b sub oneline ld l,a; hl := hl - oneline jp nc,mvlns3; no borrow dec h mvlns3: push bc ld bc,maxcol ldir; do the actual move pop bc ; " " mvlns: ld a,l; **** ENTRY POINT **** << and not colmask ld e,a ld d,h; de := hl masked to line start rla ld a,h rla cp b ret z; h = b; exit move jp nc,mvlns2; h > b ld a,e; h < b add a,oneline ld l,a; hl := hl + oneline adc a,h sub l ld h,a jp mvlns3 ; ; scroll complete screen down, clear first line, ; At entry from normal scroll operation, (c) is char yet ; to be placed, hl points to the start of the non-existent ; line -1. Set cursor for unchanged column on first line. ; a,f,b,c,d,e,h,l scrldn: call nxtln ; " " ; As scrldn, except input cursor is on the final line ; a,f,b,c,d,e,h,l dnscrl: ld a,l push af; save final column ld a,l rla ld a,h rla ld b,a; set destination line ld hl,endline; 1st to modify call mvlns ex de,hl; point to last (garbage) line call clrln pop af ld l,a; set final column ret ; ; scroll complete screen up, clear last line, ; At entry from normal scroll operation, (c) is char yet ; to be placed, hl points to the start of the non-existent ; line 25. Set cursor for unchanged column on last line. ; a,f,b,c,d,e,h,l scroll: call prvln ; " " ; As scroll, except input cursor is on the final line ; a,f,b,c,d,e,h,l upscrl: ld a,l push af; save final column ld a,l rla ld a,h rla ld b,a; set destination line ld hl,scrnbas; 1st line to modify is 0 call mvlns ex de,hl; point to last (garbage) line call clrln pop af ld l,a; set final column ret ; ; clear one line ; a,f,h,l clrln: ld a,l and not colmask ld l,a ; " " ; clear from cursor (in hl) inclusive to end of line ; a,f,h,l clrel: ld a,l and not colmask add a,maxcol ; " " ; clear from cursor (in hl) to point marked by (a) ; f,h,l clr: ld (hl),' ' inc hl cp l jp nz,clr ret ; ; Ring the bell ; a,f,c dobell: ld c,04h jp .kbdout ; ; case jump via table hl^ and char in c. ; Returns only if no entry found, else return address purged. ; Therefore do NOT jump to this (will omit purgable address) ; Table structure is byte, address. Byte = 0ffh for end marker. ; a,f,h,l *** ENTRY IN MIDDLE *** case1: inc hl inc hl case: ld a,(hl); <<< ** ENTRY HERE ** <<< inc a ret z; table end, no entry dec a cp c inc hl; to low byte of address jp nz,case1; not this entry ld a,(hl) inc hl ld h,(hl) ld l,a ex (sp),hl; purge return, stack exu point ret ; ; -------------- END of Screen Control System --------------- ; end