mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
The terminal uses console.Vga as its output device. A proper terminal implementation would be using a console.Console interface as its output. However, at this point we cannot use Go interfaces as the fn pointers in the itables have not been yet initialized. The Go runtime bits that set up the itables need access to a memory allocator, a facility which is not yet provided by the kernel.
117 lines
2.6 KiB
Go
117 lines
2.6 KiB
Go
package console
|
|
|
|
import (
|
|
"reflect"
|
|
"sync"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
clearColor = Black
|
|
clearChar = byte(' ')
|
|
)
|
|
|
|
// Vga implements an EGA-compatible text console. At the moment, it uses the
|
|
// ega console physical address as its outpucons. After implementing a memory
|
|
// allocator, each console will use its own framebuffer while the active console
|
|
// will periodically sync its internal buffer with the physical screen buffer.
|
|
type Vga struct {
|
|
sync.Mutex
|
|
|
|
width uint16
|
|
height uint16
|
|
|
|
fb []uint16
|
|
}
|
|
|
|
// Init sets up the console.
|
|
func (cons *Vga) Init() {
|
|
cons.width = 80
|
|
cons.height = 25
|
|
|
|
// Set up our frame buffer object by creating a fake slice object pointing
|
|
// to the physical address of the screen buffer.
|
|
if cons.fb != nil {
|
|
return
|
|
}
|
|
|
|
cons.fb = *(*[]uint16)(unsafe.Pointer(&reflect.SliceHeader{
|
|
Len: int(cons.width * cons.height),
|
|
Cap: int(cons.width * cons.height),
|
|
Data: uintptr(0xB8000),
|
|
}))
|
|
}
|
|
|
|
// OverrideFb overrides the console framebuffer slice with the supplied slice.
|
|
// This is a temporary function used by tests that will be removed once we can work
|
|
// with interfaces.
|
|
func (cons *Vga) OverrideFb(fb []uint16) {
|
|
cons.fb = fb
|
|
}
|
|
|
|
// Clear clears the specified rectangular region
|
|
func (cons *Vga) Clear(x, y, width, height uint16) {
|
|
var (
|
|
attr = uint16((clearColor << 4) | clearColor)
|
|
clr = attr | uint16(clearChar)
|
|
rowOffset, colOffset uint16
|
|
)
|
|
|
|
// clip rectangle
|
|
if x >= cons.width {
|
|
x = cons.width
|
|
}
|
|
if y >= cons.height {
|
|
y = cons.height
|
|
}
|
|
|
|
if x+width > cons.width {
|
|
width = cons.width - x
|
|
}
|
|
if y+height > cons.height {
|
|
height = cons.height - y
|
|
}
|
|
|
|
rowOffset = (y * cons.width) + x
|
|
for ; height > 0; height, rowOffset = height-1, rowOffset+cons.width {
|
|
for colOffset = rowOffset; colOffset < rowOffset+width; colOffset++ {
|
|
cons.fb[colOffset] = clr
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dimensions returns the console width and height in characters.
|
|
func (cons *Vga) Dimensions() (uint16, uint16) {
|
|
return cons.width, cons.height
|
|
}
|
|
|
|
// Scroll a particular number of lines to the specified direction.
|
|
func (cons *Vga) Scroll(dir ScrollDir, lines uint16) {
|
|
if lines == 0 || lines > cons.height {
|
|
return
|
|
}
|
|
|
|
var i uint16
|
|
offset := lines * cons.width
|
|
|
|
switch dir {
|
|
case Up:
|
|
for ; i < (cons.height-lines)*cons.width; i++ {
|
|
cons.fb[i] = cons.fb[i+offset]
|
|
}
|
|
case Down:
|
|
for i = cons.height*cons.width - 1; i >= lines*cons.width; i-- {
|
|
cons.fb[i] = cons.fb[i-offset]
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write a char to the specified location.
|
|
func (cons *Vga) Write(ch byte, attr Attr, x, y uint16) {
|
|
if x >= cons.width || y >= cons.height {
|
|
return
|
|
}
|
|
|
|
cons.fb[(y*cons.width)+x] = (uint16(attr) << 8) | uint16(ch)
|
|
}
|