Apple2-IO-RPi/RaspberryPi/apple2driver/a2io/vt100.go
2021-11-10 21:44:28 -05:00

183 lines
4.0 KiB
Go

// Copyright Terence J. Boldt (c)2021
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file is contains VT100 terminal emulation
package a2io
import (
"fmt"
"time"
)
var escapeSequence string
var htab, vtab, savedHtab, savedVtab int
var applicationMode bool
var windowTop int
var windowBottom = 22
func sendCharacter(comm A2Io, b byte) {
if b == 0x1b {
escapeSequence = "^["
return
}
if len(escapeSequence) > 0 {
escapeSequence += string(b)
// save cursor
if escapeSequence == "^[7" {
savedHtab = htab
savedVtab = vtab
escapeSequence = ""
}
// restore cursor
if escapeSequence == "^[8" {
htab = savedHtab
vtab = savedVtab
comm.WriteByte('H')
comm.WriteByte(byte(htab))
comm.WriteByte('V')
comm.WriteByte(byte(vtab))
escapeSequence = ""
}
if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') {
switch b {
// Set cursor location
case 'H', 'f':
var ignore string
fmt.Sscanf(escapeSequence, "^[[%d;%d%s", &vtab, &htab, &ignore)
htab--
vtab--
comm.WriteByte('H')
comm.WriteByte(byte(htab))
comm.WriteByte('V')
comm.WriteByte(byte(vtab))
escapeSequence = ""
case 'r':
fmt.Sscanf(escapeSequence, "^[[%d;%dr", &windowTop, &windowBottom)
windowTop--
//windowBottom--
comm.WriteByte('T')
comm.WriteByte(byte(windowTop))
comm.WriteByte('B')
comm.WriteByte(byte(windowBottom))
escapeSequence = ""
case 'C':
var right int
fmt.Sscanf(escapeSequence, "^[[%dC", &right)
htab -= right
for i := 0; i < right; i++ {
comm.WriteByte(0x08)
}
escapeSequence = ""
}
switch escapeSequence {
// Set/clear application mode for cursor
case "^[[?1h":
applicationMode = true
comm.WriteByte(0x0c) // ^L clears the screen
escapeSequence = ""
case "^[[?1l":
applicationMode = false
comm.WriteByte('T')
comm.WriteByte(0x00)
comm.WriteByte('B')
comm.WriteByte(0x18)
comm.WriteByte(0x0c) // ^L clears the screen
escapeSequence = ""
// Tab to home position
case "^[[H", "^[[;H", "^[[f", "^[[;f":
htab = 0
vtab = 0
comm.WriteByte(0x19) // ^Y moves to home position
escapeSequence = ""
// Clear screen
case "^[[2J", "^[[c":
htab = 0
vtab = 0
comm.WriteByte(0x0c) // ^L clears the screen
escapeSequence = ""
// Move down one line
case "^[E":
comm.WriteByte(0x0A) // ^J moves cursor down
escapeSequence = ""
case "^[D":
comm.WriteByte(0x17) // ^W scrolls up
escapeSequence = ""
// Clear line to the right
case "^[[K", "^[[0K":
comm.WriteByte(0x1d) // ^] clears to end of line
escapeSequence = ""
case "^[M":
comm.WriteByte(0x16) // ^V scrolls down
escapeSequence = ""
// Clear screen below cursor
case "^[[J":
comm.WriteByte(0x0b) // ^K clears to end of screen
escapeSequence = ""
case "^[[7m":
comm.WriteByte(0x0f) // ^O inverse video
escapeSequence = ""
case "^[[m", "^[[0m":
comm.WriteByte(0x0e) // ^N normal video
escapeSequence = ""
}
if len(escapeSequence) > 0 {
fmt.Printf("\nUnhandled escape sequence: %s\n", escapeSequence)
}
escapeSequence = ""
return
}
return
}
fmt.Print(string(b))
htabIncrement := 0
switch b {
// convert LF to CR for Apple II compatiblity
case 10:
b = 13
vtab++
htab = 0
htabIncrement = 0
case 13:
htab = 0
htabIncrement = 0
return
// convert TAB to spaces
case 9:
b = ' '
b |= 0x80
err := comm.WriteByte(b)
if err != nil {
// try again because could have been cancelled by input
time.Sleep(time.Millisecond * 10)
comm.WriteByte(b)
}
htabIncrement = 2
default:
htabIncrement = 1
}
if !applicationMode || htab < 78 {
updateTabs(comm)
b |= 0x80
htab += htabIncrement
err := comm.WriteByte(b)
if err != nil {
// try again because could have been cancelled by input
time.Sleep(time.Millisecond * 10)
comm.WriteByte(b)
}
}
}
func updateTabs(comm A2Io) {
if htab >= 80 {
htab = 0
vtab++
}
if vtab > windowBottom {
vtab = windowBottom
}
}