/*
* libtilemcore - Graphing calculator emulation library
*
* Copyright (C) 2001 Solignac Julien
* Copyright (C) 2004-2009 Benjamin Moody
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see
* .
*/
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include "tilem.h"
#ifdef DISABLE_LCD_DRIVER_DELAY
# define BUSY 0
# define SET_BUSY 0
#else
# define BUSY check_delay_timer(calc)
# define SET_BUSY set_delay_timer(calc)
static inline int check_delay_timer(TilemCalc* calc)
{
int t;
if (!calc->lcd.busy)
return 0;
t = tilem_z80_get_timer_clocks(calc, TILEM_TIMER_LCD_DELAY);
return (t > 0);
}
static inline void set_delay_timer(TilemCalc* calc)
{
int delay;
if (!(calc->lcd.emuflags & TILEM_LCD_REQUIRE_DELAY))
return;
if (calc->lcd.emuflags & TILEM_LCD_REQUIRE_LONG_DELAY)
delay = 70;
else
delay = 50;
calc->lcd.busy = 1;
tilem_z80_set_timer(calc, TILEM_TIMER_LCD_DELAY, delay, 0, 0);
}
#endif
void tilem_lcd_reset(TilemCalc* calc)
{
calc->lcd.active = 0;
calc->lcd.contrast = 32;
calc->lcd.addr = 0;
calc->lcd.mode = 1;
calc->lcd.nextbyte = 0;
calc->lcd.x = calc->lcd.y = 0;
calc->lcd.inc = 7;
calc->lcd.rowshift = 0;
calc->lcd.busy = 0;
if (calc->hw.lcdmemsize)
calc->lcd.rowstride = (calc->hw.lcdmemsize
/ calc->hw.lcdheight);
else
calc->lcd.rowstride = (calc->hw.lcdwidth / 8);
}
void tilem_lcd_delay_timer(TilemCalc* calc, void* data TILEM_ATTR_UNUSED)
{
calc->lcd.busy = 0;
}
byte tilem_lcd_t6a04_status(TilemCalc* calc)
{
return (calc->lcd.busy << 7
| calc->lcd.mode << 6
| calc->lcd.active << 5
| (calc->lcd.inc & 3));
}
void tilem_lcd_t6a04_control(TilemCalc* calc, byte val)
{
if (BUSY) return;
if (val <= 1) {
calc->lcd.mode = val;
} else if (val == 2) {
calc->lcd.active = 0;
} else if (val == 3) {
calc->lcd.active = 1;
} else if (val <= 7) {
calc->lcd.inc = val;
} else if ((val >= 0x20) && (val <= 0x3F)){
calc->lcd.x = val - 0x20;
} else if ((val >= 0x80) && (val <= 0xBF)) {
calc->lcd.y = val - 0x80;
} else if ((val >= 0x40) && (val <= 0x7F)) {
calc->lcd.rowshift = val - 0x40;
} else if (val >= 0xc0) {
calc->lcd.contrast = val - 0xc0;
}
calc->z80.lastlcdwrite = calc->z80.clock;
SET_BUSY;
}
byte tilem_lcd_t6a04_read(TilemCalc* calc)
{
byte retv = calc->lcd.nextbyte;
byte* lcdbuf = calc->lcdmem;
int stride = calc->lcd.rowstride;
int xlimit;
if (BUSY) return(0);
if (calc->lcd.mode)
xlimit = stride;
else
xlimit = (stride * 8 + 5) / 6;
if (calc->lcd.x >= xlimit)
calc->lcd.x = 0;
else if (calc->lcd.x < 0)
calc->lcd.x = xlimit - 1;
if (calc->lcd.y >= 0x40)
calc->lcd.y = 0;
else if (calc->lcd.y < 0)
calc->lcd.y = 0x3F;
if (calc->lcd.mode) {
calc->lcd.nextbyte = *(lcdbuf + calc->lcd.x + stride * calc->lcd.y);
} else {
int col = 0x06 * calc->lcd.x;
int ofs = calc->lcd.y * stride + (col >> 3);
int shift = 0x0A - (col & 0x07);
calc->lcd.nextbyte = ((*(lcdbuf + ofs) << 8) | *(lcdbuf + ofs + 1)) >> shift;
}
switch (calc->lcd.inc) {
case 4: calc->lcd.y--; break;
case 5: calc->lcd.y++; break;
case 6: calc->lcd.x--; break;
case 7: calc->lcd.x++; break;
}
SET_BUSY;
return(retv);
}
void tilem_lcd_t6a04_write(TilemCalc* calc, byte sprite)
{
byte* lcdbuf = calc->lcdmem;
int stride = calc->lcd.rowstride;
int xlimit;
if (BUSY) return;
if (calc->lcd.mode)
xlimit = stride;
else
xlimit = (stride * 8 + 5) / 6;
if (calc->lcd.x >= xlimit)
calc->lcd.x = 0;
else if (calc->lcd.x < 0)
calc->lcd.x = xlimit - 1;
if (calc->lcd.y >= 0x40)
calc->lcd.y = 0;
else if (calc->lcd.y < 0)
calc->lcd.y = 0x3F;
if (calc->lcd.mode) {
*(lcdbuf + calc->lcd.x + stride * calc->lcd.y) = sprite;
} else {
int col = 0x06 * calc->lcd.x;
int ofs = calc->lcd.y * stride + (col >> 3);
int shift = col & 0x07;
int mask;
sprite <<= 2;
mask = ~(0xFC >> shift);
*(lcdbuf + ofs) = (*(lcdbuf + ofs) & mask) | (sprite >> shift);
if (shift > 2 && (col >> 3) < (stride - 1)) {
ofs++;
shift = 8 - shift;
mask = ~(0xFC << shift);
*(lcdbuf + ofs) = (*(lcdbuf + ofs) & mask) | (sprite << shift);
}
}
switch (calc->lcd.inc) {
case 4: calc->lcd.y--; break;
case 5: calc->lcd.y++; break;
case 6: calc->lcd.x--; break;
case 7: calc->lcd.x++; break;
}
calc->z80.lastlcdwrite = calc->z80.clock;
SET_BUSY;
return;
}
void tilem_lcd_t6a04_get_data(TilemCalc* calc, byte* data)
{
int width = calc->hw.lcdwidth / 8;
byte* lcdbuf = calc->lcdmem;
int stride = calc->lcd.rowstride;
int i, j, k;
for (i = 0; i < calc->hw.lcdheight; i++) {
j = (i + calc->lcd.rowshift) % 64;
for (k = 0; k < width; k++)
data[k] = lcdbuf[j * stride + k];
data += width;
}
}
void tilem_lcd_t6a43_get_data(TilemCalc* calc, byte* data)
{
int width = calc->hw.lcdwidth / 8;
byte* lcdbuf = calc->ram + calc->lcd.addr;
int stride = calc->lcd.rowstride;
int i, j;
for (i = 0; i < calc->hw.lcdheight; i++) {
for (j = 0; j < 10; j++)
data[j] = lcdbuf[j];
for (; j < 10 + width - stride; j++)
data[j] = 0;
for (; j < width; j++)
data[j] = lcdbuf[j + stride - width];
data += width;
lcdbuf += stride;
}
}