'' AnsiTerm.spin '' '' MODIFIED BY WAYNE WARTHEN FOR ANSI COLOR CHARACTER SUPPORT '' SPEAKER ENHANCEMENT BY MICHAEL SCHWEIKERT '' '' based on... '' '' VGA_1024.spin '' '' MODIFIED BY VINCE BRIEL FOR POCKETERM FEATURES '' MODIIFED BY JEFF LEDGER / AKA OLDBITCOLLECTOR '' CON cols = 80 ' screen columns rows = 30 ' screen rows chars = rows * cols ' screen characters termRows = rows - 1 ' rows in terminal area termChars = termRows * cols ' characters in terminal area termLastRow = termChars - cols ' buffer pos of first char in last term row statArea = termChars ' starting position of status area statRows = 1 ' status area rows blank = $20 spkVol = 75 spkMaxFrq = 1_200 spkMinFrq = 200 spkBase = 13 ' Speaker pin OBJ vga : "vgacolour" 'vga : "vga8x8d" spk : "E555_SPKEngine" VAR word screen[chars] ' screen character buffer word tmpl[cols] ' temporary line buffer byte cursor[6] ' cursor info array (see CursorPtr description above) long sync, loc, xloc, yloc ' sync used by VGA routine, others are local screen pointers byte state ' Current state of state machine word pos ' Current Position on the screen word regionTop, regionBot ' Scroll region top/bottom long arg0 ' First argument of escape sequence long arg1 ' Second argument of escape sequence byte lastc ' Last displayed char long vgaBasePin word curAttr ' active attribute value word bold, underscore, blink, reverse, fg, bg word alt ' alt char set active DAT { color ansi rgb ----- ---- --- black 0 0 red 1 4 green 2 2 yellow 3 6 blue 4 1 magenta 5 5 cyan 6 3 white 7 7 } cmap WORD 0,4,2,6,1,5,3,7 ' Map ANSI color codes to VGA driver RGB PUB start(BasePin) | i, char vgaBasePin := BasePin cursor[2] := %110 ' init cursor to underscore with slow blink xloc := cursor[0] := 0 yloc := cursor[1] := 0 loc := xloc + yloc * cols pos := 0 regionTop := 0 regionBot := termChars state := 0 bold := 0 underscore := 0 blink := 0 reverse := 0 fg := 0 bg := 0 alt := 0 setMode(0) ' reset attributes PUB vidOn if (!vga.start(vgaBasePin, @screen, @cursor, @sync)) return false PUB vidOff vga.stop PUB speakerFrequency(newFrequency) result := spk.speakerFrequency(newFrequency, spkBase) PUB speakerVolume(newVolume) result := spk.speakerVolume(newVolume, spkBase) PUB beep spk.speakerFrequency(1000, spkBase) waitcnt((clkfreq >> 4) + cnt) spk.speakerFrequency(-1, spkBase) PUB cls wordfill(@screen, (curAttr | blank), chars) PUB dec(value) | i '' Print a decimal number if value < 0 -value outc("-") i := 1_000_000_000 repeat 10 if value => i outc(value/i + "0") value //= i result~~ elseif result or i == 1 outc("0") i /= 10 PUB bin(value, digits) '' Print a binary number, specify number of digits repeat while digits > 32 outc("0") digits-- value <<= 32 - digits repeat digits outc((value <-= 1) & 1 + "0") PUB hex(value, digits) '' Print a hexadecimal number, specify number of digits repeat while digits > 8 outc("0") digits-- value <<= (8 - digits) << 2 repeat digits outc(lookupz((value <-= 4) & $f : "0".."9", "A".."F")) PUB str(string_ptr) '' Print a zero terminated string repeat strsize(string_ptr) outc(byte[string_ptr++]) PUB statInfo result := (statRows << 8) | cols PUB statStr(row, col, attr, strVal) | nxtPos nxtPos := statArea + (row * cols) + col repeat strsize(strVal) screen[nxtPos++] := (attr | byte[strVal++]) PUB statFill(row, col, attr, charVal, count) wordfill(@screen + ((statArea + (row * cols) + col) * 2), (attr | charVal), count) PRI clsTerm wordfill (@screen, (curAttr | blank), termChars) PRI outc(c) if (alt == 1) if ((c => $5F) and (c < $7F)) c := c - $5F screen[pos++] := (curAttr | c) lastc := c if pos == regionBot scrollUp pos -= cols elseif pos == termChars pos := termLastRow PRI setMode(n) if (n == 0) bold := 0 underscore := 0 blink := 0 reverse := 0 fg := 7 bg := 0 elseif (n == 1) bold := 1 elseif (n == 4) underscore := 1 elseif (n == 5) blink := 1 elseif (n == 7) reverse := 1 elseif (n == 21) bold := 0 elseif (n == 22) bold := 0 elseif (n == 24) underscore := 0 elseif (n == 25) blink := 0 elseif (n == 27) reverse := 0 elseif ((n => 30) & (n =< 37)) fg := cmap[(n - 30)] elseif ((n => 40) & (n =< 47)) bg := cmap[(n - 40)] if (reverse == 0) curAttr := ((fg << 8) | (bg << 12)) else curAttr := ((fg << 12) | (bg << 8)) curAttr |= (bold << 11) | (underscore << 7) | (blink << 15) PRI clEOL(position) | count count := cols - (position // cols) wordfill(@screen + (position * 2), (curAttr | blank), count) PRI clBOL(position) | count count := position // cols wordfill(@screen + ((position - count) * 2), (curAttr | blank), count) PRI delLine(position) | src, count position -= position // cols src := position + cols count := termChars - src if count > 0 wordmove(@screen + (position * 2), @screen + (src * 2), count) wordfill(@screen + (termLastRow * 2), (curAttr | blank), cols) PRI clEOS(position) cleol(position) position += cols - (position // cols) repeat while position < termChars wordfill(@screen + (position * 2), (curAttr | blank), cols) pos += cols PRI setCursorPos(position) cursor[0] := position // cols cursor[1] := position / cols PRI insLine(position) | base, nxt base := position - (position // cols) position := termLastRow repeat while position > base nxt := position - cols wordmove(@screen + (position * 2), @screen + (nxt * 2), cols) position := nxt clEOL(base) PRI insChar(position) | count count := (cols - (position // cols)) - 1 wordmove(@tmpl, @screen + (position * 2), count) screen[position] := (curAttr | blank) wordmove(@screen + ((position + 1) * 2), @tmpl, count) PRI delChar(position) | count count := (cols - (position // cols)) - 1 wordmove(@screen + (position * 2), @screen + ((position + 1) * 2), count) screen[position + count] := (curAttr | blank) PRI inRegion : answer answer := (pos => regionTop) AND (pos < regionBot) PRI scrollUp delLine(regionTop) if regionBot < termChars insLine(regionBot) PRI scrollDown if regionBot < termChars delLine(regionBot) insLine(regionTop) PRI ansi(c) | x, defVal state := 0 if (c <> "r") AND (c <> "J") AND (c <> "m") AND (c <> "K") if arg0 == -1 arg0 := 1 if arg1 == -1 arg1 := 1 case c "@": repeat while arg0-- > 0 insChar(pos) "b": repeat while arg0-- > 0 outc(lastc) "d": if (arg0 < 1) OR (arg0 > rows) arg0 := rows pos := ((arg0 - 1) * cols) + (pos // cols) "m": if (arg0 == -1) setMode(0) else setMode(arg0) if arg1 <> -1 setMode(arg1) "r": if arg0 < 1 arg0 := 1 elseif arg0 > cols arg0 := cols if arg1 < 1 arg1 := 1 elseif arg1 > cols arg1 := cols if arg1 < arg0 arg1 := arg0 regionTop := (arg0 - 1) * cols regionBot := arg1 * cols pos := 0 "A": repeat while arg0-- > 0 pos -= cols if pos < 0 pos += cols return "B": repeat while arg0-- > 0 pos += cols if pos => termChars pos -= cols return "C": repeat while arg0-- > 0 pos += 1 if pos => termChars pos -= 1 return "D": repeat while arg0-- > 0 pos -= 1 if pos < 0 pos := 0 return "G": if (arg0 < 1) OR (arg0 > cols) arg0 := cols pos := (pos - (pos // cols)) + (arg0 - 1) "H", "f": if arg0 =< 0 arg0 := 1 if arg1 =< 0 arg1 := 1 pos := (cols * (arg0 - 1)) + (arg1 - 1) if pos < 0 pos := 0 if pos => termChars pos := termChars - 1 "J": if arg0 == 1 clBOL(pos) x := pos - cols x -= x // cols repeat while x => 0 clEOL(x) x -= cols return if arg0 == 2 pos := 0 clEOL(pos) x := pos + cols x -= (x // cols) repeat while x < termChars clEOL(x) x += cols "K": if ((arg0 == -1) or (arg0 == 0)) clEOL(pos) elseif (arg0 == 1) clBOL(pos) elseif (arg0 == 2) clEOL(pos - (pos // cols)) "L": if inRegion repeat while arg0-- > 0 if regionBot < termChars delLine(regionBot) insLine(pos) "M": if inRegion repeat while arg0-- > 0 delLine(pos) if regionBot < termChars insLine(regionBot) "P": repeat while arg0-- delChar(pos) PUB processChar(c) case state 0: ' Default state (no escape sequence in process) if c > 127 c := $20 if c => $20 outc(c) setCursorPos(pos) return if c == $1B state := 1 return if c == $0D pos := pos - (pos // cols) setCursorPos(pos) return if c == $0A if inRegion pos += cols if pos => regionBot scrollUp pos -= cols else pos += cols if pos => termChars pos -= cols setCursorPos(pos) return if c == 9 pos += (8 - (pos // 8)) if pos => termChars pos := termLastRow delLine(0) setCursorPos(pos) return if c == 8 if pos > 0 pos -= 1 setCursorPos(pos) return if c == 7 ' bel beep return 1: ' Process char following escape case c "[": arg0 := arg1 := -1 state := 2 return "P": pos += cols if pos => termChars pos -= cols "K": if pos > 0 pos -= 1 "H": pos -= cols if pos < 0 pos += cols "D": if inRegion scrollUp "M": if inRegion scrollDown "G": pos := 0 "(": state := 5 return state := 0 return 2: ' Parse first argument (arg0) of escape sequence if (c => "0") AND (c =< "9") if arg0 == -1 arg0 := c - "0" else arg0 := (arg0 * 10) + (c - "0") return if c == ";" state := 3 return ansi(c) setCursorPos(pos) return 3: ' Parse second argument (arg1) of escape sequence if (c => "0") AND (c =< "9") if arg1 == -1 arg1 := c - "0" else arg1 := (arg1 * 10) + (c - "0") return if c == ";" state := 4 return ansi(c) setCursorPos(pos) return 4: ' Parse remaining arguments (ignored) if (c => "0") AND (c =< "9") return if c == ";" return ansi(c) setCursorPos(pos) return 5: ' Set character set (not implemented) if (c == "0") alt := 1 if (c == "B") alt := 0 state := 0 return return