1 /*
2 * libtilemcore - Graphing calculator emulation library
3 *
4 * Copyright (C) 2001 Solignac Julien
5 * Copyright (C) 2004-2009 Benjamin Moody
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include "tilem.h"
28
29 #ifdef DISABLE_LCD_DRIVER_DELAY
30 # define BUSY 0
31 # define SET_BUSY 0
32 #else
33 # define BUSY check_delay_timer(calc)
34 # define SET_BUSY set_delay_timer(calc)
35
check_delay_timer(TilemCalc * calc)36 static inline int check_delay_timer(TilemCalc* calc)
37 {
38 int t;
39
40 if (!calc->lcd.busy)
41 return 0;
42
43 t = tilem_z80_get_timer_clocks(calc, TILEM_TIMER_LCD_DELAY);
44 return (t > 0);
45 }
46
set_delay_timer(TilemCalc * calc)47 static inline void set_delay_timer(TilemCalc* calc)
48 {
49 int delay;
50
51 if (!(calc->lcd.emuflags & TILEM_LCD_REQUIRE_DELAY))
52 return;
53
54 if (calc->lcd.emuflags & TILEM_LCD_REQUIRE_LONG_DELAY)
55 delay = 70;
56 else
57 delay = 50;
58
59 calc->lcd.busy = 1;
60
61 tilem_z80_set_timer(calc, TILEM_TIMER_LCD_DELAY, delay, 0, 0);
62 }
63 #endif
64
tilem_lcd_reset(TilemCalc * calc)65 void tilem_lcd_reset(TilemCalc* calc)
66 {
67 calc->lcd.active = 0;
68 calc->lcd.contrast = 32;
69 calc->lcd.addr = 0;
70 calc->lcd.mode = 1;
71 calc->lcd.nextbyte = 0;
72 calc->lcd.x = calc->lcd.y = 0;
73 calc->lcd.inc = 7;
74 calc->lcd.rowshift = 0;
75 calc->lcd.busy = 0;
76
77 if (calc->hw.lcdmemsize)
78 calc->lcd.rowstride = (calc->hw.lcdmemsize
79 / calc->hw.lcdheight);
80 else
81 calc->lcd.rowstride = (calc->hw.lcdwidth / 8);
82 }
83
tilem_lcd_delay_timer(TilemCalc * calc,void * data TILEM_ATTR_UNUSED)84 void tilem_lcd_delay_timer(TilemCalc* calc, void* data TILEM_ATTR_UNUSED)
85 {
86 calc->lcd.busy = 0;
87 }
88
tilem_lcd_t6a04_status(TilemCalc * calc)89 byte tilem_lcd_t6a04_status(TilemCalc* calc)
90 {
91 return (calc->lcd.busy << 7
92 | calc->lcd.mode << 6
93 | calc->lcd.active << 5
94 | (calc->lcd.inc & 3));
95 }
96
tilem_lcd_t6a04_control(TilemCalc * calc,byte val)97 void tilem_lcd_t6a04_control(TilemCalc* calc, byte val)
98 {
99 if (BUSY) return;
100
101 if (val <= 1) {
102 calc->lcd.mode = val;
103
104 } else if (val == 2) {
105 calc->lcd.active = 0;
106
107 } else if (val == 3) {
108 calc->lcd.active = 1;
109
110 } else if (val <= 7) {
111 calc->lcd.inc = val;
112
113 } else if ((val >= 0x20) && (val <= 0x3F)){
114 calc->lcd.x = val - 0x20;
115
116 } else if ((val >= 0x80) && (val <= 0xBF)) {
117 calc->lcd.y = val - 0x80;
118
119 } else if ((val >= 0x40) && (val <= 0x7F)) {
120 calc->lcd.rowshift = val - 0x40;
121
122 } else if (val >= 0xc0) {
123 calc->lcd.contrast = val - 0xc0;
124
125 }
126
127 calc->z80.lastlcdwrite = calc->z80.clock;
128 SET_BUSY;
129 }
130
131
tilem_lcd_t6a04_read(TilemCalc * calc)132 byte tilem_lcd_t6a04_read(TilemCalc* calc)
133 {
134 byte retv = calc->lcd.nextbyte;
135 byte* lcdbuf = calc->lcdmem;
136 int stride = calc->lcd.rowstride;
137 int xlimit;
138
139 if (BUSY) return(0);
140
141 if (calc->lcd.mode)
142 xlimit = stride;
143 else
144 xlimit = (stride * 8 + 5) / 6;
145
146 if (calc->lcd.x >= xlimit)
147 calc->lcd.x = 0;
148 else if (calc->lcd.x < 0)
149 calc->lcd.x = xlimit - 1;
150
151 if (calc->lcd.y >= 0x40)
152 calc->lcd.y = 0;
153 else if (calc->lcd.y < 0)
154 calc->lcd.y = 0x3F;
155
156 if (calc->lcd.mode) {
157 calc->lcd.nextbyte = *(lcdbuf + calc->lcd.x + stride * calc->lcd.y);
158
159 } else {
160 int col = 0x06 * calc->lcd.x;
161 int ofs = calc->lcd.y * stride + (col >> 3);
162 int shift = 0x0A - (col & 0x07);
163
164 calc->lcd.nextbyte = ((*(lcdbuf + ofs) << 8) | *(lcdbuf + ofs + 1)) >> shift;
165 }
166
167 switch (calc->lcd.inc) {
168 case 4: calc->lcd.y--; break;
169 case 5: calc->lcd.y++; break;
170 case 6: calc->lcd.x--; break;
171 case 7: calc->lcd.x++; break;
172 }
173
174 SET_BUSY;
175 return(retv);
176 }
177
178
tilem_lcd_t6a04_write(TilemCalc * calc,byte sprite)179 void tilem_lcd_t6a04_write(TilemCalc* calc, byte sprite)
180 {
181 byte* lcdbuf = calc->lcdmem;
182 int stride = calc->lcd.rowstride;
183 int xlimit;
184
185 if (BUSY) return;
186
187 if (calc->lcd.mode)
188 xlimit = stride;
189 else
190 xlimit = (stride * 8 + 5) / 6;
191
192 if (calc->lcd.x >= xlimit)
193 calc->lcd.x = 0;
194 else if (calc->lcd.x < 0)
195 calc->lcd.x = xlimit - 1;
196
197 if (calc->lcd.y >= 0x40)
198 calc->lcd.y = 0;
199 else if (calc->lcd.y < 0)
200 calc->lcd.y = 0x3F;
201
202 if (calc->lcd.mode) {
203 *(lcdbuf + calc->lcd.x + stride * calc->lcd.y) = sprite;
204
205 } else {
206 int col = 0x06 * calc->lcd.x;
207 int ofs = calc->lcd.y * stride + (col >> 3);
208 int shift = col & 0x07;
209 int mask;
210
211 sprite <<= 2;
212 mask = ~(0xFC >> shift);
213 *(lcdbuf + ofs) = (*(lcdbuf + ofs) & mask) | (sprite >> shift);
214 if (shift > 2 && (col >> 3) < (stride - 1)) {
215 ofs++;
216 shift = 8 - shift;
217 mask = ~(0xFC << shift);
218 *(lcdbuf + ofs) = (*(lcdbuf + ofs) & mask) | (sprite << shift);
219 }
220 }
221
222 switch (calc->lcd.inc) {
223 case 4: calc->lcd.y--; break;
224 case 5: calc->lcd.y++; break;
225 case 6: calc->lcd.x--; break;
226 case 7: calc->lcd.x++; break;
227 }
228
229 calc->z80.lastlcdwrite = calc->z80.clock;
230 SET_BUSY;
231 return;
232 }
233
234
tilem_lcd_t6a04_get_data(TilemCalc * calc,byte * data)235 void tilem_lcd_t6a04_get_data(TilemCalc* calc, byte* data)
236 {
237 int width = calc->hw.lcdwidth / 8;
238 byte* lcdbuf = calc->lcdmem;
239 int stride = calc->lcd.rowstride;
240 int i, j, k;
241
242 for (i = 0; i < calc->hw.lcdheight; i++) {
243 j = (i + calc->lcd.rowshift) % 64;
244 for (k = 0; k < width; k++)
245 data[k] = lcdbuf[j * stride + k];
246 data += width;
247 }
248 }
249
tilem_lcd_t6a43_get_data(TilemCalc * calc,byte * data)250 void tilem_lcd_t6a43_get_data(TilemCalc* calc, byte* data)
251 {
252 int width = calc->hw.lcdwidth / 8;
253 byte* lcdbuf = calc->ram + calc->lcd.addr;
254 int stride = calc->lcd.rowstride;
255 int i, j;
256
257 for (i = 0; i < calc->hw.lcdheight; i++) {
258 for (j = 0; j < 10; j++)
259 data[j] = lcdbuf[j];
260 for (; j < 10 + width - stride; j++)
261 data[j] = 0;
262 for (; j < width; j++)
263 data[j] = lcdbuf[j + stride - width];
264
265 data += width;
266 lcdbuf += stride;
267 }
268 }
269