mirror of
https://github.com/hsoft/collapseos.git
synced 2024-09-07 12:38:44 +10:00
I implement the screen using XCB which is much more friendly than z80e's SDL+CMake for development machines that want to install minimal dependencies (for example, a port-less OpenBSD rig).
142 lines
4.0 KiB
C
142 lines
4.0 KiB
C
#include <string.h>
|
|
#include "t6a04.h"
|
|
|
|
void t6a04_init(T6A04 *lcd)
|
|
{
|
|
memset(lcd->ram, 0, T6A04_RAMSIZE);
|
|
lcd->enabled = false;
|
|
lcd->incmode = T6A04_XINC;
|
|
lcd->offset = 0;
|
|
lcd->currow = 0;
|
|
lcd->curcol = 0;
|
|
lcd->just_moved = true;
|
|
}
|
|
|
|
uint8_t t6a04_cmd_rd(T6A04 *lcd)
|
|
{
|
|
return 0; // we are always ready for a new cmd
|
|
}
|
|
|
|
/*
|
|
* 0x00/0x01: 6/8 bit mode
|
|
* 0x02/0x03: enable/disable
|
|
* 0x04-0x07: incmodes
|
|
* 0x20-0x34: set col
|
|
* 0x40-0x7f: set Z offset
|
|
* 0x80-0xbf: set row
|
|
* 0xc0-0xff: set contrast
|
|
*/
|
|
void t6a04_cmd_wr(T6A04 *lcd, uint8_t val)
|
|
{
|
|
if ((val & 0xc0) == 0xc0) {
|
|
// contrast, ignoring
|
|
} else if (val & 0x80) {
|
|
lcd->currow = val & 0x3f;
|
|
lcd->just_moved = true;
|
|
} else if (val & 0x40) {
|
|
lcd->offset = val & 0x3f;
|
|
} else if (val & 0x20) {
|
|
lcd->curcol = val & 0x1f;
|
|
lcd->just_moved = true;
|
|
} else if (val & 0x18) {
|
|
// stuff we don't emulate
|
|
} else if (val & 0x04) {
|
|
lcd->incmode = val & 0x03;
|
|
} else if (val & 0x02) {
|
|
lcd->enabled = val & 0x01;
|
|
} else {
|
|
lcd->has8bitmode = val;
|
|
}
|
|
}
|
|
|
|
// Advance current position according to current incmode
|
|
static void _advance(T6A04 *lcd)
|
|
{
|
|
uint8_t maxY = lcd->has8bitmode ? 14 : 19;
|
|
switch (lcd->incmode) {
|
|
case T6A04_XDEC:
|
|
lcd->currow = (lcd->currow-1) & 0x3f;
|
|
break;
|
|
case T6A04_XINC:
|
|
lcd->currow = (lcd->currow+1) & 0x3f;
|
|
break;
|
|
case T6A04_YDEC:
|
|
if (lcd->curcol == 0) {
|
|
lcd->curcol = maxY;
|
|
} else {
|
|
lcd->curcol--;
|
|
}
|
|
break;
|
|
case T6A04_YINC:
|
|
if (lcd->curcol < maxY) {
|
|
lcd->curcol++;
|
|
} else {
|
|
lcd->curcol = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t t6a04_data_rd(T6A04 *lcd)
|
|
{
|
|
uint8_t res;
|
|
if (lcd->just_moved) {
|
|
// After a move command, the first read op is a noop.
|
|
lcd->just_moved = false;
|
|
return 0;
|
|
}
|
|
if (lcd->has8bitmode) {
|
|
int pos = lcd->currow * T6A04_ROWSIZE + lcd->curcol;
|
|
res = lcd->ram[pos];
|
|
} else {
|
|
// 6bit mode is a bit more complicated because the 6-bit number often
|
|
// spans two bytes. We manage this by loading two bytes into a uint16_t
|
|
// and then shift it right properly.
|
|
// bitpos represents the leftmost bit of our 6bit number.
|
|
int bitpos = lcd->curcol * 6;
|
|
// offset represents the shift right we need to perform from the two
|
|
// bytes following bitpos/8 so that we can have our number with a 6-bit
|
|
// mask.
|
|
// Example, col 3 has a bitpos of 18, which means that it loads bytes 2
|
|
// and 3. Its bits would be in bit pos 14:8, which means it has an
|
|
// offset of 8. There is always an offset and its always in the 3-10
|
|
// range
|
|
int offset = 10 - (bitpos % 8); // 10 is for 16bit - 6bit
|
|
int pos = (lcd->currow * T6A04_ROWSIZE) + (bitpos / 8);
|
|
uint16_t word = lcd->ram[pos] << 8;
|
|
word |= lcd->ram[pos+1];
|
|
res = (word >> offset) & 0x3f;
|
|
}
|
|
_advance(lcd);
|
|
return res;
|
|
}
|
|
|
|
void t6a04_data_wr(T6A04 *lcd, uint8_t val)
|
|
{
|
|
lcd->just_moved = false;
|
|
if (lcd->has8bitmode) {
|
|
int pos = lcd->currow * T6A04_ROWSIZE + lcd->curcol;
|
|
lcd->ram[pos] = val;
|
|
} else {
|
|
// See comments in t6a04_data_rd().
|
|
int bitpos = lcd->curcol * 6;
|
|
int offset = 10 - (bitpos % 8);
|
|
int pos = (lcd->currow * T6A04_ROWSIZE) + (bitpos / 8);
|
|
uint16_t word = lcd->ram[pos] << 8;
|
|
word |= lcd->ram[pos+1];
|
|
// word contains our current ram value. Let's fit val in this.
|
|
word &= ~(0x003f << offset);
|
|
word |= val << offset;
|
|
lcd->ram[pos] = word >> 8;
|
|
lcd->ram[pos+1] = word & 0xff;
|
|
}
|
|
_advance(lcd);
|
|
}
|
|
|
|
bool t6a04_pixel(T6A04 *lcd, uint8_t y, uint8_t x)
|
|
{
|
|
x = (x + lcd->offset) & 0x3f;
|
|
uint8_t val = lcd->ram[x * T6A04_ROWSIZE + (y / 8)];
|
|
return (val >> (7 - (y % 8))) & 1;
|
|
}
|