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