|
- #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;
- }
|