gigatron/rom/Contrib/xopr/NetTerm/helpers.h
2025-01-28 19:17:01 +03:00

819 lines
17 KiB
C

#ifdef TINY_FONT
#define ROWS 20
#define COLS 40
#define CHAR_LEADING 0
#define CHAR_WIDTH 4
#define CHAR_HEIGHT 6
#else
// NOTE: Normal chars have a horizontal offset of 2, 26 chars
#define ROWS 15
#define COLS 26
#define CHAR_LEADING 0
#define CHAR_WIDTH 6
#define CHAR_HEIGHT 8
#endif
#define MAX_X (COLS*CHAR_WIDTH)
#define MAX_Y (ROWS*(CHAR_HEIGHT+CHAR_LEADING))
// Standard includes
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
// System include
#include <Gigatron.h>
// SPI ports (active low)
#define SS0 0x04
#define SS1 0x08
#define SS2 0x10
#define SS3 0x20
#define BANK0 0x40
#define BANK1 0x80
#define SPI_MASK 0x3c
#define BANK_MASK 0xc0
#define DarkYellow 0x05
#define Yellow 0x0a
#define LightYellow 0x0f
#define DarkMagenta 0x11
#define Magenta 0x22
#define LightMagenta 0x33
#define DarkCyan 0x14
#define Cyan 0x28
#define LightCyan 0x3c
#define DarkGray 0x15
#define LightGray 0x2a
#define romType (*(byte*)0x0021)
#define romTypeValue_ROMv4 0x38
#define romTypeValue_DEVROM 0xf8
#define xoutMask (*(byte*)0x0014)
#define ledState (*(byte*)0x002E)
#define ledTempo (*(byte*)0x002F)
#define SYS_VDrawWord_134 0x00cb
#define SYS_Draw4_30 0x04d4
#define SYS_SetMode_v2_80 0x0b00
#define SYS_SetMemory_v2_54 0x0b03
#define SYS_ExpanderControl_v4_40 0x0b09
#define SYS_SpiExchangeBytes_v4_134 0x0b15
// Forward declaration
void cursorDown( byte _amount, byte _allowScroll );
#define NEWLINE_EX() {cursorDown(1,1);ScreenPos &= 0xff00;}
#define NEWLINE_EX2() {cursorDown(1,1);ScreenPos &= 0xff00;ScreenPos |= 0x0002;}
#define ROW_TO_SCREEN(_r,_s){\
((byte*)&_s)[1] = _r * ( CHAR_HEIGHT + CHAR_LEADING ) + videoTable[0];\
if ( ((byte*)&_s)[1] > 0x7f )\
((byte*)&_s)[1] -= 120;\
}
#ifdef TINY_FONT
// Hijack original functions
#define PutChar(c) PutChar_ex(c)
//#define puts(s) puts_ex(s)
#define Newline() NEWLINE_EX()
#else
#define Newline() NEWLINE_EX2()
#endif
int cursor[ 12 ];
const int tinyfont[96] = {
#include "../Utils/BabelFish/tinyfont.h"
};
void EraseInLine( byte _mode, int _screenCoord )
{
byte size;
char line;
switch ( _mode )
{
case 0:
// Erase from the active position to the end of the line, inclusive (default)
//size = 160 - (_screenCoord & 0x00ff);
size = 160 - ((byte*)&_screenCoord)[0];
break;
case 1:
// Erase from the start of the line to the active position, inclusive
size = 160 - ((byte*)&_screenCoord)[0] + CHAR_WIDTH;
_screenCoord = _screenCoord & 0xff00;
break;
case 2:
// Erase all of the line, inclusive
_screenCoord = _screenCoord & 0xff00;
size = 160;
break;
}
// Clear this line
sysFn = SYS_SetMemory_v2_54;
sysArgs[1] = BgColor; // Set background color
for ( line = 0; line < CHAR_HEIGHT; ++line )
{
*(int*)(sysArgs+2) = _screenCoord; // Destination
sysArgs[0] = size; // Count
__syscall(243); // 270-54/2
_screenCoord += 256;
}
}
void PutChar_ex( byte c )
{
// DECAWM->autowrap to newline (allow scroll) or overwrite last char
byte i;
#ifdef TERMINAL_MODES
byte wrap = (terminalModes & DECAWM) ? 1 : 0;
#else
byte wrap = 1;
#endif
// Accept newlines
if (c == '\n')
{
Newline();
return;
}
if ( c == 9 )
{
// TODO: make tab work for big font as well
ScreenPos += 8 * CHAR_WIDTH;
ScreenPos &= ~(8*CHAR_WIDTH - 1);
}
else if ( c < 32 || c > 127 )
return;
// Wrap to new line if we're outside the view
if (((byte*)&ScreenPos)[0] >= MAX_X )
{
if ( wrap )
{
Newline();
}
else
{
ScreenPos -= CHAR_WIDTH;
}
}
if ( c == 9 )
return;
i = c - 32;
sysFn = SYS_VDrawWord_134;
sysArgs[0] = BgColor;
sysArgs[1] = Color;
*(int*)(sysArgs+2) = tinyfont[ i ];
*(int*)(sysArgs+4) = ScreenPos;
__syscall(202); // 270-135/2
// Note that the column can be out of the display area
// (to prevent automatic newlines at the end of the screen)
//column++;
ScreenPos += CHAR_WIDTH;
}
signed char puts_ex(const char *s)
{
// Note that this fuction is dumbed down: no stream support
for (; *s; s++)
{
PutChar(*s);
}
return EOF;
}
void ScrollUp()
{
int screenCoord;
byte i;
// Clear first line since video rolls over
ROW_TO_SCREEN( 0, screenCoord );
EraseInLine( 2, screenCoord );
// Scroll up by manipulating video indirection table
i = 240;
do
{
byte page;
i -= 2;
page = videoTable[i] + CHAR_HEIGHT + CHAR_LEADING;
videoTable[i] = page & 128 ? page - 120 : page;
} while (i);
}
void PutGlyph( unsigned long _g )
{
// Wrap to new line if we're outside the view
if (((byte*)&ScreenPos)[0] >= MAX_X )
Newline();
sysFn = SYS_VDrawWord_134;
sysArgs[0] = BgColor;
sysArgs[1] = Color;
*(int*)(sysArgs+2) = _g;
*(int*)(sysArgs+4) = ScreenPos;
__syscall(202); // 270-135/2
// Note that the column can be out of the display area
// (to prevent automatic newlines at the end of the screen)
//column++;
ScreenPos += CHAR_WIDTH;
}
void drawChar( int* _char )
{
if (((byte*)&ScreenPos)[0] >= MAX_X )
return;
*(int*)ScreenPos = _char[0];
*(int*)(ScreenPos + 2) = _char[1];
*(int*)(ScreenPos + 256) = _char[2];
*(int*)(ScreenPos + 258) = _char[3];
*(int*)(ScreenPos + 512) = _char[4];
*(int*)(ScreenPos + 514) = _char[5];
*(int*)(ScreenPos + 768) = _char[6];
*(int*)(ScreenPos + 770) = _char[7];
*(int*)(ScreenPos + 1024) = _char[8];
*(int*)(ScreenPos + 1026) = _char[9];
*(int*)(ScreenPos + 1280) = _char[10];
*(int*)(ScreenPos + 1282) = _char[11];
}
void drawCursor( byte _copy )
{
byte i;
// TODO: maybe try and draw on the next line
if (((byte*)&ScreenPos)[0] >= MAX_X )
return;
// TODO: MAX_Y
if ( _copy )
{
cursor[0] = *(int*)ScreenPos;
cursor[1] = *(int*)(ScreenPos + 2);
cursor[2] = *(int*)(ScreenPos + 256);
cursor[3] = *(int*)(ScreenPos + 258);
cursor[4] = *(int*)(ScreenPos + 512);
cursor[5] = *(int*)(ScreenPos + 514);
cursor[6] = *(int*)(ScreenPos + 768);
cursor[7] = *(int*)(ScreenPos + 770);
cursor[8] = *(int*)(ScreenPos + 1024);
cursor[9] = *(int*)(ScreenPos + 1026);
cursor[10] = *(int*)(ScreenPos + 1280);
cursor[11] = *(int*)(ScreenPos + 1282);
}
else
{
for ( i = 0; i < 12; i++ )
cursor[i] = BgColor << 8 | BgColor;
}
// Prepare SYS call
sysFn = SYS_Draw4_30;
sysArgs[0] = Color;
sysArgs[1] = Color;
sysArgs[2] = Color;
sysArgs[3] = BgColor;
*(int*)(sysArgs+4) = ScreenPos;
// Draw bitmap to screen as 'n' vertical slices
// TODO: there are not enough cycles to include the 6th line when drawing a character; dont draw any pixels in it for now to avoid clutter
//for (i=CHAR_HEIGHT; i>0; --i)
for (i=5; i>0; --i)
{
__syscall(255); // 270-30/2
sysArgs[5]++;
}
}
void setCursor( byte _r, byte _c )
{
// Restore character behind cursor
//drawChar( cursor );
if ( _r >= ROWS )
_r = ROWS - 1;
if ( _c >= COLS )
_c = COLS - 1;
// Calculate row with scroll offset in mind
ROW_TO_SCREEN( _r, ScreenPos );
ScreenPos &= 0xff00;
#ifdef TINY_FONT
ScreenPos |= (( _c * CHAR_WIDTH + videoTable[1] ) & 0x00ff );
#else
ScreenPos |= (( _c * CHAR_WIDTH + videoTable[1] + 2) & 0x00ff );
#endif
// Draw cursor
//drawCursor( 1 );
}
void EraseInDisplay( byte _mode, int _screenCoord )
{
byte line;
byte row = ((byte*)&_screenCoord)[1];
if ( videoTable[0] > row )
row += 120;
row -= videoTable[0];
row /= ( CHAR_HEIGHT + CHAR_LEADING );
// Erase current line
EraseInLine( _mode, _screenCoord );
// 0: end
// 1: begin
// 2: all
if ( _mode && row )
{
// Erase begin or all of screen
for ( line = 0; line < row; ++line )
{
#if defined( SLOW ) && defined( TESTS )
BusyWait( 10 );
#endif
ROW_TO_SCREEN( line, _screenCoord );
EraseInLine( 2, _screenCoord );
}
}
if ( _mode != 1 )
{
// Erase end or all of screen
//for ( ++row; row < ROWS - 1; ++row )
for ( ++row; row < ROWS; ++row )
{
#if defined( SLOW ) && defined( TESTS )
BusyWait( 10 );
#endif
ROW_TO_SCREEN( row, _screenCoord );
EraseInLine( 2, _screenCoord );
}
}
}
void cursorUp( byte _amount, byte _allowScroll )
{
// TODO: avoid usage of int -> (byte*)&_screenCoord)[1];
/*
byte row = ((byte*)&_screenCoord)[1];
if ( videoTable[0] > row )
row += 120;
row -= videoTable[0];
row /= ( CHAR_HEIGHT + CHAR_LEADING );
*/
int y = (ScreenPos >> 8) - videoTable[0];
if ( y < 0 )
y += 120;
while ( _amount-- )
{
if ( y >= ( CHAR_HEIGHT + CHAR_LEADING ) )
{
ScreenPos -= (( CHAR_HEIGHT + CHAR_LEADING ) << 8 );
y -= CHAR_HEIGHT + CHAR_LEADING;
}
else if ( _allowScroll )
{
// TODO: ScrollDown();
}
else
{
break;
}
}
}
void cursorDown( byte _amount, byte _allowScroll )
{
// Determine y offset (avoid usage of int)
byte row = ((byte*)&ScreenPos)[1];
if ( videoTable[0] > row )
row += 120;
row -= videoTable[0];
row /= ( CHAR_HEIGHT + CHAR_LEADING );
while ( _amount-- )
{
if ( row < (ROWS - 1) )
{
row++;
ROW_TO_SCREEN( row, ScreenPos );
}
else if ( _allowScroll )
{
ScrollUp();
ROW_TO_SCREEN( row, ScreenPos );
}
else
{
break;
}
}
}
void cursorLeft( byte _amount )
{
// TODO: avoid usage of int
int x = (ScreenPos & 0xff );
while ( _amount-- )
{
if ( x >= CHAR_WIDTH )
{
ScreenPos -= CHAR_WIDTH;
x -= CHAR_WIDTH;
}
else
{
break;
}
}
}
void cursorRight( byte _amount )
{
// TODO: avoid usage of int
int x = (ScreenPos & 0xff );
while ( _amount-- )
{
if ( x < (MAX_X - CHAR_WIDTH) )
{
ScreenPos += CHAR_WIDTH;
x += CHAR_WIDTH;
}
else
{
break;
}
}
}
#ifdef TESTS
void test()
{
reset();
setCursor( 0, 0 );
puts_ex( "Tests:" );
reset();
test_cursor();
reset();
test_clear();
reset();
test_colors();
reset();
test_linewrap();
reset();
test_scroll();
reset();
test_cursorscroll();
reset();
test_newline();
reset();
test_e3();
// Done
reset();
ledState = 0;
setCursor( 2, 2 );
puts_ex( "Done." );
}
void test_cursor()
{
// test setCursor, drawCursor and cursor up/down/left/right
setCursor( 2, 2 );
puts_ex( "Cursor" );
// bottom right corner: '#'
drawChar( cursor );
setCursor( 100, 100 );
PutChar( '#' );
drawCursor( 1 );
xoutMask++;
BusyWait( 60 );
// top right corner: 'B'
drawChar( cursor );
cursorLeft( 1 ); // TODO: fix cursor
cursorUp( 100, 0 );
PutChar( 'B' );
drawCursor( 1 );
xoutMask++;
BusyWait( 60 );
// top left corner: 'A'
drawChar( cursor );
cursorLeft( 100 );
PutChar( 'A' );
drawCursor( 1 );
xoutMask++;
BusyWait( 60 );
// bottom left corner: 'D'
drawChar( cursor );
cursorLeft( 1 ); // TODO: fix cursor
cursorDown( 100, 0 );
PutChar( 'D' );
drawCursor( 1 );
xoutMask++;
BusyWait( 60 );
// bottom right corner: 'C'
drawChar( cursor );
cursorRight( 100 );
PutChar( 'C' );
drawCursor( 1 );
xoutMask++;
BusyWait( 60 );
}
void test_clear()
{
// Test EraseInDisplay and inherently, EraseInLine in all 3 variations
setCursor( 2, 2 );
puts_ex( "Clear" );
// Erase lower half with red
BgColor = DarkRed;
setCursor( ROWS / 2, COLS / 2 );
EraseInDisplay( 0, ScreenPos );
xoutMask++;
BusyWait( 60 );
// Erase upper half with green
BgColor = DarkGreen;
EraseInDisplay( 1, ScreenPos );
xoutMask++;
BusyWait( 60 );
// Erase all with black
BgColor = Black;
EraseInDisplay( 2, ScreenPos );
xoutMask++;
BusyWait( 60 );
}
void test_colors()
{
// Test all 64 foreground and background colors
byte r;
byte c;
setCursor( 2, 2 );
puts_ex( "Colors" );
BgColor = Black;
for ( r = 0; r < 8; ++r )
{
for ( c = 0; c < 8; ++c )
{
setCursor( r + 5, c + 8 );
Color = 8 * r + c;
PutChar( '#' );
}
}
xoutMask++;
Color = White;
for ( r = 0; r < 8; ++r )
{
for ( c = 0; c < 8; ++c )
{
setCursor( r + 5, c + 24 );
BgColor = 8 * r + c;
PutChar( '.' );
}
}
xoutMask++;
BusyWait( 60 );
}
void test_linewrap()
{
// Test line wrapping
byte count;
char linewrap[] = "Line wrap";
setCursor( 2, 2 );
puts_ex( linewrap );
BusyWait( 60 );
setCursor( 0, 0 );
count = 20;
Color = LightGreen;
while ( count-- )
{
puts_ex( linewrap );
xoutMask++;
BusyWait( 5 );
}
// Force DECAWM (automatic wrap mode) off
#ifdef TERMINAL_MODES
terminalModes &= ~DECAWM;
count = 5;
Color = LightRed;
while ( count-- )
{
puts_ex( linewrap );
xoutMask++;
BusyWait( 5 );
}
#endif
}
void test_scroll()
{
// Test screen scrolling
byte count = 21;
setCursor( 2, 2 );
puts_ex( "Scroll" );
while ( count-- )
{
ScrollUp();
setCursor( 0, 10 );
puts_ex( "line 1 will be erased" );
setCursor( 19, 16 );
puts_ex( "line 20" );
xoutMask++;
BusyWait( 10 );
}
// TODO: scroll down/left/up
}
void test_cursorscroll()
{
// Test cursor scrolling
byte count;
setCursor( 2, 2 );
puts_ex( "CursorScroll" );
// Center
setCursor( ROWS / 2, COLS / 2 );
count = 21;
while ( count-- )
{
cursorDown( 1, 1 );
PutChar( '^' );
xoutMask++;
BusyWait( 30 );
}
/*
// Center
setCursor( ROWS / 2, COLS / 2 );
count = 21;
while ( count-- )
{
cursorUp( 1, 1 );
PutChar( 'v' );
xoutMask++;
BusyWait( 30 );
}
*/
}
void test_newline()
{
// Test newline scrolling
byte count = 21;
char newline[] = "Newline";
setCursor( 2, 2 );
puts_ex( newline );
setCursor( 8, 0 );
drawCursor( 0 );
while ( count-- )
{
drawChar( cursor );
Newline();
puts_ex( newline );
drawCursor( 1 );
xoutMask++;
BusyWait( 30 );
}
}
void test_e3()
{
// Fill screen with 'E', wait a second and fill the next 3 lines with '3'
byte c;
byte r = ROWS;
setCursor( 2, 2 );
puts_ex( "E3" );
drawCursor( 1 );
BusyWait( 60 );
drawChar( cursor );
setCursor( 0, 0 );
drawCursor( 1 );
// Fill screen with 'E'
while ( r-- )
{
c = COLS;
while ( c-- )
{
PutChar( 'E' );
drawCursor( 0 );
#ifdef SLOW
BusyWait( 1 );
#endif
xoutMask++;
}
}
BusyWait( 60 );
r = 3;
while ( r-- )
{
c = COLS;
while ( c-- )
{
PutChar( '3' );
drawCursor( 0 );
#ifdef SLOW
BusyWait( 3 );
#endif
xoutMask++;
}
}
}
void reset()
{
byte i = 240;
byte t = 127;
ledState = 0;
BusyWait( 60 );
// LEDs static on
ledState = 1;
xoutMask = 0xf;
//Reset cursor
ScreenPos = (int)screenMemory;
// Clear display
BgColor = Blue;
Color = White;
EraseInDisplay( 2, ScreenPos );
// Reset scroll
// Scroll up by manipulating video indirection table
do
{
i -= 2;
videoTable[i] = t--;
} while (i);
// Store and draw (empty) cursor
drawCursor( 0 );
drawChar( cursor );
}
#endif