mirror of https://github.com/wwarthen/RomWBW.git
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.
557 lines
12 KiB
557 lines
12 KiB
'' 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 = 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
|
|
|
|
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
|
|
|
|
|
|
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 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)
|
|
|
|
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
|
|
|
|
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)
|
|
state := 0
|
|
return
|
|
|
|
return
|