You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

535 lines
11 KiB

'' AnsiTerm.spin
''
'' MODIFIED BY WAYNE WARTHEN FOR ANSI COLOR CHARACTER SUPPORT
''
'' based on...
''
'' VGA_1024.spin
''
'' MODIFIED BY VINCE BRIEL FOR POCKETERM FEATURES
'' MODIIFED BY JEFF LEDGER / AKA OLDBITCOLLECTOR
''
CON
cols = 80 ' screen columns
rows = 40 ' screen rows
chars = rows * cols ' screen characters
termRows = 25 ' 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 = rows - TermRows ' status area rows
blank = $20
OBJ
vga : "vgacolour"
'vga : "vga8x8d"
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
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
setMode(0) ' reset attributes
PUB vidOn
if (!vga.start(vgaBasePin, @screen, @cursor, @sync))
return false
PUB vidOff
vga.stop
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)
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 => 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
clEOL(pos)
elseif arg0 == 1
clBOL(pos)
else
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
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)
state := 0
return
return