kickc/src/main/kc/lib/cx16-conio.c

408 lines
12 KiB
C

/**
* @file cx16-conio.c
* @author Sven Van de Velde (sven.van.de.velde@telenet.be)
* @brief conio for the cx16. These methods allow to print and get information from the console.
*
* Important notes:
*
* - There is a pre-compile option __CONIO_BSOUT which if set,
* will output every conio operation to the standard output using the kernal API BSOUT,
* instead of directly posting to the vera. This is useful to log information in the emulator terminal while running.
*
* @version 0.1
* @date 2022-10-15
*
* @copyright Copyright (c) 2022
*
*/
#include <cx16.h>
#include <conio.h>
#include <cx16-conio.h>
#include <cx16-vera.h>
#include <kernal.h>
#include <string.h>
#define CONIO_TEXTCOLOR_DEFAULT WHITE // The default text color
#define CONIO_BACKCOLOR_DEFAULT BLUE // The default back color
struct cx16_conio_s {
unsigned char cursor_x; ///< current cursor x-position
unsigned char cursor_y; ///< current cursor y-position
unsigned char layer;
unsigned int mapbase_offset; // Base pointer to the tile map base of the conio screen.
char mapbase_bank; // Default screen of the CX16 emulator uses memory bank 0 for text.
unsigned char width; ///< the screen width;
unsigned char height; ///< the screen height;
unsigned char mapwidth; ///< the map width;
unsigned char mapheight; ///< the map height;
unsigned int rowskip; ///< the amount of vram bytes needed to skip a row.
unsigned char cursor; ///< is a cursor whown when waiting for input (0: no, other: yes)
unsigned char color; ///< color of the foreground and background
unsigned char bordercolor; ///< color of the border
/// Is scrolling enabled when outputting beyond the end of the screen (1: yes, 0: no).
/// If disabled the cursor just moves back to (0,0) instead
unsigned char scroll[2];
unsigned char hscroll[2];
unsigned int offset; ///< The current offset
unsigned int offsets[61]; ///< Calculated offsets per line according the mapbase and the row width (scale).
};
__mem __asm_export struct cx16_conio_s __conio;
/// Initializer for conio.h on X16 Commander.
#pragma constructor_for(conio_x16_init, cputc, clrscr, cscroll)
/// Set initial screen values.
void conio_x16_init() {
screenlayer1();
textcolor(CONIO_TEXTCOLOR_DEFAULT);
bgcolor(CONIO_BACKCOLOR_DEFAULT);
cursor(0);
__conio.cursor_x = BYTE1(cbm_k_plot_get());
__conio.cursor_y = BYTE0(cbm_k_plot_get());
gotoxy(__conio.cursor_x, __conio.cursor_y);
__conio.scroll[0] = 1;
__conio.scroll[1] = 1;
}
// Returns a value if a key is pressed.
unsigned char kbhit()
{
cbm_k_clrchn();
return cbm_k_getin();
}
// clears the screen and moves the cursor to the upper left-hand corner of the screen.
void clrscr(void)
{
unsigned int line_text = __conio.mapbase_offset;
*VERA_CTRL &= ~VERA_ADDRSEL;
*VERA_ADDRX_H = __conio.mapbase_bank | VERA_INC_1;
unsigned char l = __conio.mapheight;
do {
unsigned int ch = line_text;
// Set address
*VERA_ADDRX_L = BYTE0(ch);
*VERA_ADDRX_M = BYTE1(ch);
unsigned char c = __conio.mapwidth+1;
do{
*VERA_DATA0 = ' ';
*VERA_DATA0 = __conio.color;
c--;
} while(c);
line_text += __conio.rowskip;
l--;
} while(l);
__conio.cursor_x = 0;
__conio.cursor_y = 0;
__conio.offset = __conio.mapbase_offset;
}
// Set the cursor to the specified position
void gotoxy(unsigned char x, unsigned char y)
{
#ifndef __CONIO_BSOUT
__conio.cursor_x = (x>=__conio.width)?__conio.width:x;
__conio.cursor_y = (y>=__conio.height)?__conio.height:y;
__conio.offset = __conio.offsets[y] + __conio.cursor_x << 1;
#endif
}
// Return the current screen size.
void screensize(unsigned char* x, unsigned char* y) {
// VERA returns in VERA_DC_HSCALE the value of 128 when 80 columns is used in text mode,
// and the value of 64 when 40 columns is used in text mode.
// Basically, 40 columns mode in the VERA is a double scan mode.
// Same for the VERA_DC_VSCALE mode, but then the subdivision is 60 or 30 rows.
// I still need to test the other modes, but this will suffice for now for the pure text modes.
char hscale = (*VERA_DC_HSCALE) >> 7;
*x = (40 << hscale)-1;
char vscale = (*VERA_DC_VSCALE) >> 7;
*y = (30 << vscale)-1;
//printf("%u, %u\n", *x, *y);
}
// Return the current screen size x width.
unsigned char screensizex() {
return __conio.width;
}
// Return the current screen size y height.
unsigned char screensizey() {
return __conio.height;
}
// Return the x position of the cursor
unsigned char wherex(void) {
return __conio.cursor_x;
}
// Return the y position of the cursor
unsigned char wherey(void) {
return __conio.cursor_y;
}
// Output one character at the current cursor position
// Moves the cursor forward. Scrolls the entire screen if needed
void cputc(char c) {
if(c=='\n') {
cputln();
} else {
#ifdef __CONIO_BSOUT
cbm_k_plot_set(0,0);
cbm_k_chrout(c);
#endif
#ifndef __CONIO_BSOUT
*VERA_CTRL &= ~VERA_ADDRSEL;
*VERA_ADDRX_L = BYTE0(__conio.offset);
*VERA_ADDRX_M = BYTE1(__conio.offset);
*VERA_ADDRX_H = __conio.mapbase_bank | VERA_INC_1;
*VERA_DATA0 = c;
*VERA_DATA0 = __conio.color;
#endif
if(!__conio.hscroll[__conio.layer]) {
if(__conio.cursor_x >= __conio.width)
cputln();
else {
__conio.cursor_x++;
__conio.offset++;
__conio.offset++;
}
} else {
if(__conio.cursor_x >= __conio.mapwidth)
cputln();
else {
__conio.cursor_x++;
__conio.offset++;
__conio.offset++;
}
}
}
}
unsigned char cpeekc() {
#ifndef __CONIO_BSOUT
*VERA_CTRL &= ~VERA_ADDRSEL;
*VERA_ADDRX_L = BYTE0(__conio.offset);
*VERA_ADDRX_M = BYTE1(__conio.offset);
*VERA_ADDRX_H = __conio.mapbase_bank | VERA_INC_0;
return *VERA_DATA0;
#endif
}
unsigned char cpeekcxy(unsigned char x, unsigned char y) {
gotoxy(x,y);
return cpeekc();
}
// Print a newline
void cputln() {
__conio.cursor_x = 0;
__conio.cursor_y++;
__conio.offset = __conio.offsets[__conio.cursor_y];
if(__conio.scroll[__conio.layer]) {
cscroll();
}
#ifdef __CONIO_BSOUT
cbm_k_plot_set(0,0);
cbm_k_chrout(13);
#endif
}
void clearline() {
#ifndef __CONIO_BSOUT
unsigned int addr = __conio.offsets[__conio.cursor_y];
*VERA_CTRL &= ~VERA_ADDRSEL;
*VERA_ADDRX_L = BYTE0(addr);
*VERA_ADDRX_M = BYTE1(addr);
*VERA_ADDRX_H = __conio.mapbase_bank | VERA_INC_1;
register unsigned char c=__conio.width;
do {
*VERA_DATA0 = ' ';
*VERA_DATA0 = __conio.color;
c--;
} while(c);
#endif
}
// Insert a new line, and scroll the lower part of the screen down.
void insertdown(unsigned char rows) {
if(__conio.cursor_y>=0 && __conio.cursor_y <=__conio.height-1) {
#ifndef __CONIO_BSOUT
// {asm{.byte $db}}
unsigned char width = (__conio.width+1) * 2;
for(unsigned char y=__conio.height - __conio.cursor_y + 1; y>__conio.cursor_y; y--) {
memcpy8_vram_vram(__conio.mapbase_bank, __conio.offsets[y], __conio.mapbase_bank, __conio.offsets[y-1], width);
}
clearline();
#endif
}
}
// Insert a new line, and scroll the upper part of the screen up.
void insertup(unsigned char rows) {
#ifndef __CONIO_BSOUT
// {asm{.byte $db}}
unsigned char width = (__conio.width+1) * 2;
for(unsigned char y=0; y<__conio.cursor_y; y++) {
memcpy8_vram_vram(__conio.mapbase_bank, __conio.offsets[y], __conio.mapbase_bank, __conio.offsets[y+1], width);
}
clearline();
#endif
}
// Scroll the entire screen if the cursor is beyond the last line
void cscroll() {
#ifndef __CONIO_BSOUT
if(__conio.cursor_y>__conio.height) {
if(__conio.scroll[__conio.layer]) {
insertup(1);
gotoxy( 0, __conio.height);
clearline();
} else {
if(__conio.cursor_y>__conio.height) {
gotoxy(0,0);
}
}
}
#endif
}
// Output a NUL-terminated string at the current cursor position
void cputs(const char* s) {
#ifndef __CONIO_BSOUT
char c;
while(c=*s++)
cputc(c);
#endif
}
// Move cursor and output one character
// Same as "gotoxy (x, y); cputc (c);"
void cputcxy(unsigned char x, unsigned char y, char c) {
#ifndef __CONIO_BSOUT
gotoxy(x, y);
cputc(c);
#endif
}
// Move cursor and output a NUL-terminated string
// Same as "gotoxy (x, y); puts (s);"
void cputsxy(unsigned char x, unsigned char y, const char* s) {
#ifndef __CONIO_BSOUT
gotoxy(x, y);
cputs(s);
#endif
}
// If onoff is 1, a cursor is displayed when waiting for keyboard input.
// If onoff is 0, the cursor is hidden when waiting for keyboard input.
// The function returns the old cursor setting.
unsigned char cursor(unsigned char onoff) {
// not supported in CX16
char old = __conio.cursor;
__conio.cursor = onoff;
return old;
}
// If onoff is 1, scrolling is enabled when outputting past the end of the screen
// If onoff is 0, scrolling is disabled and the cursor instead moves to (0,0)
// The function returns the old scroll setting.
unsigned char scroll(unsigned char onoff) {
char old = __conio.scroll[__conio.layer];
__conio.scroll[__conio.layer] = onoff;
return old;
}
// If onoff is 1, scrolling is enabled when outputting past the end of the screen
// If onoff is 0, scrolling is disabled and the cursor instead moves to (0,0)
// The function returns the old scroll setting.
unsigned char hscroll(unsigned char onoff) {
char old = __conio.hscroll[__conio.layer];
__conio.hscroll[__conio.layer] = onoff;
return old;
}
// --- Defined in cx16.c and cx16-vera.h ---
// --- layer management in VERA ---
void screenlayer(char layer, char mapbase, char config)
{
unsigned char const VERA_LAYER_DIM[4] = {31, 63, 127, 255};
unsigned int const VERA_LAYER_SKIP[4] = {64, 128, 256, 512};
__mem char vera_dc_hscale_temp = *VERA_DC_HSCALE;
__mem char vera_dc_vscale_temp = *VERA_DC_VSCALE;
__conio.layer = 0;
__conio.mapbase_bank = mapbase >> 7;
__conio.mapbase_offset = MAKEWORD((mapbase)<<1,0);
__conio.mapwidth = VERA_LAYER_DIM[ (config & VERA_LAYER_WIDTH_MASK) >> 4];
__conio.mapheight = VERA_LAYER_DIM[ (config & VERA_LAYER_HEIGHT_MASK) >> 6];
// __conio.rowshift = ((config & VERA_LAYER_WIDTH_MASK)>>4)+6;
__conio.rowskip = VERA_LAYER_SKIP[(config & VERA_LAYER_WIDTH_MASK)>>4];
__conio.width = (40 << (char)(vera_dc_hscale_temp == 0x80))-1;
__conio.height = (30 << (char)(vera_dc_vscale_temp == 0x80))-1;
unsigned int mapbase_offset = __conio.mapbase_offset;
for(register char y=0; y<=__conio.height; y++) {
__conio.offsets[y] = mapbase_offset;
mapbase_offset += __conio.rowskip;
}
}
// Set the layer with which the conio will interact.
// - layer: value of 0 or 1.
void screenlayer0() {
screenlayer(0, *VERA_L0_MAPBASE, *VERA_L0_CONFIG);
}
// Set the layer with which the conio will interact.
void screenlayer1() {
screenlayer(1, *VERA_L1_MAPBASE, *VERA_L1_CONFIG);
}
// Set the front color for text output. The old front text color setting is returned.
// - color: a 4 bit value ( decimal between 0 and 15).
// This will only work when the VERA is in 16 color mode!
// Note that on the VERA, the transparent color has value 0.
char textcolor(char color) {
return __conio.color = __conio.color & 0xF0 | color;
}
// Set the back color for text output.
// - color: a 4 bit value ( decimal between 0 and 15).
// This will only work when the VERA is in 16 color mode!
// Note that on the VERA, the transparent color has value 0.
char bgcolor(char color) {
return __conio.color = __conio.color & 0x0F | color << 4;
}
// Set the color for the border.
char bordercolor(unsigned char color) {
return __conio.bordercolor = *VERA_DC_BORDER;
}