1 /*
2  * libtilemcore - Graphing calculator emulation library
3  *
4  * Copyright (C) 2009-2012 Benjamin Moody
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifndef _TILEM_H
22 #define _TILEM_H
23 
24 #include "tilemint.h"
25 
26 #ifdef __cplusplus
27 extern "C" {
28 #endif
29 
30 /* Basic integer types */
31 typedef uint8_t byte;
32 typedef uint16_t word;
33 typedef uint32_t dword;
34 typedef uint64_t qword;
35 
36 /* Structure types */
37 typedef struct _TilemHardware TilemHardware;
38 typedef struct _TilemCalc TilemCalc;
39 
40 /* Useful macros */
41 #if __GNUC__ >= 3
42 # define TILEM_ATTR_PURE __attribute__((__pure__))
43 # define TILEM_ATTR_UNUSED __attribute__((__unused__))
44 # define TILEM_ATTR_MALLOC __attribute__((__malloc__))
45 # define TILEM_ATTR_PRINTF(x,y) __attribute__((__format__(__printf__,x,y)))
46 # define TILEM_LIKELY(xxx) (__builtin_expect((xxx), 1))
47 # define TILEM_UNLIKELY(xxx) (__builtin_expect((xxx), 0))
48 #else
49 # define TILEM_ATTR_PURE
50 # define TILEM_ATTR_UNUSED
51 # define TILEM_ATTR_MALLOC
52 # define TILEM_ATTR_PRINTF(x,y)
53 # define TILEM_LIKELY(xxx) (xxx)
54 # define TILEM_UNLIKELY(xxx) (xxx)
55 #endif
56 
57 #define TILEM_DWORD_TO_PTR(xxx) ((void*)(uintptr_t)(xxx))
58 #define TILEM_PTR_TO_DWORD(xxx) ((dword)(uintptr_t)(xxx))
59 
60 /* Memory allocation */
61 void* tilem_malloc(size_t size) TILEM_ATTR_MALLOC;
62 void* tilem_malloc0(size_t size) TILEM_ATTR_MALLOC;
63 void* tilem_malloc_atomic(size_t size) TILEM_ATTR_MALLOC;
64 void* tilem_try_malloc(size_t size) TILEM_ATTR_MALLOC;
65 void* tilem_try_malloc0(size_t size) TILEM_ATTR_MALLOC;
66 void* tilem_try_malloc_atomic(size_t size) TILEM_ATTR_MALLOC;
67 void* tilem_realloc(void* ptr, size_t size) TILEM_ATTR_MALLOC;
68 void tilem_free(void* ptr);
69 #define tilem_new(ttt, nnn) ((ttt*) tilem_malloc((nnn) * sizeof(ttt)));
70 #define tilem_new0(ttt, nnn) ((ttt*) tilem_malloc0((nnn) * sizeof(ttt)));
71 #define tilem_new_atomic(ttt, nnn) ((ttt*) tilem_malloc_atomic((nnn) * sizeof(ttt)));
72 
73 #define tilem_try_new(ttt, nnn) ((ttt*) tilem_try_malloc((nnn) * sizeof(ttt)));
74 #define tilem_try_new0(ttt, nnn) ((ttt*) tilem_try_malloc0((nnn) * sizeof(ttt)));
75 #define tilem_try_new_atomic(ttt, nnn) ((ttt*) tilem_try_malloc_atomic((nnn) * sizeof(ttt)));
76 #define tilem_renew(ttt, ppp, nnn) ((ttt*) tilem_realloc((ppp), (nnn) * sizeof(ttt)))
77 
78 /* Message/error logging */
79 
80 /* Write an informative message.  This can be used to notify the user
81    of major occurences, such as changes in the Flash protection.
82    These messages can occur regularly in normal operation and are
83    provided chiefly to aid in debugging. */
84 void tilem_message(TilemCalc* calc, const char* msg, ...)
85 	TILEM_ATTR_PRINTF(2, 3);
86 
87 /* Write a warning message.  These messages occur when the calculator
88    software (either the OS or a user program) performs an invalid
89    operation; these messages often indicate a bug in the calculator
90    software, but are otherwise harmless. */
91 void tilem_warning(TilemCalc* calc, const char* msg, ...)
92 	TILEM_ATTR_PRINTF(2, 3);
93 
94 /* Write a warning about an internal error.  These messages should
95    never occur and indicate a bug in TilEm. */
96 void tilem_internal(TilemCalc* calc, const char* msg, ...)
97 	TILEM_ATTR_PRINTF(2, 3);
98 
99 
100 /* Z80 CPU */
101 
102 /* This union allows us to manipulate register pairs as either byte or
103    word values.  It may need to be modified for really unusual host
104    CPUs. */
105 typedef union _TilemZ80Reg {
106 #ifdef WORDS_BIGENDIAN
107 	struct {
108 		byte h3, h2, h, l;
109 	} b;
110 
111 	struct {
112 		word h, l;
113 	} w;
114 
115 	dword d;
116 #else
117 	struct {
118 		byte l, h, h2, h3;
119 	} b;
120 
121 	struct {
122 		word l, h;
123 	} w;
124 
125 	dword d;
126 #endif
127 } TilemZ80Reg;
128 
129 typedef struct _TilemZ80Regs {
130 	TilemZ80Reg af, bc, de, hl;
131 	TilemZ80Reg ix, iy, pc, sp;
132 	TilemZ80Reg ir, wz, wz2;
133 	TilemZ80Reg af2, bc2, de2, hl2;
134 	int iff1, iff2, im;
135 	byte r7;
136 } TilemZ80Regs;
137 
138 /* Breakpoint types */
139 enum {
140 	TILEM_BREAK_MEM_READ = 1, /* Break after reading from memory */
141 	TILEM_BREAK_MEM_EXEC,	  /* Break prior to executing from memory */
142 	TILEM_BREAK_MEM_WRITE,	  /* Break after writing to memory */
143 	TILEM_BREAK_PORT_READ,	  /* Break after reading from port */
144 	TILEM_BREAK_PORT_WRITE,	  /* Break after writing to port */
145 	TILEM_BREAK_EXECUTE,	  /* Break after executing opcode */
146 
147 	TILEM_BREAK_TYPE_MASK = 0xffff,
148 
149 	TILEM_BREAK_PHYSICAL = 0x10000, /* Use physical addresses */
150 	TILEM_BREAK_DISABLED = 0x20000  /* Disabled breakpoint */
151 };
152 
153 /* Emulation flags */
154 enum {
155 	TILEM_Z80_BREAK_INVALID = 1,      /* Break on invalid
156 					     instructions */
157 	TILEM_Z80_BREAK_UNDOCUMENTED = 2, /* Break on undocumented
158 					     instructions */
159 	TILEM_Z80_SKIP_UNDOCUMENTED  = 4, /* Ignore undocumented
160 					     instructions entirely
161 					     (act as two NOPs) */
162 	TILEM_Z80_RESET_UNDOCUMENTED = 8, /* Reset CPU following
163 	                                     undocumented instructions */
164 	TILEM_Z80_BREAK_EXCEPTIONS = 16,  /* Break on hardware exceptions */
165 	TILEM_Z80_IGNORE_EXCEPTIONS = 32  /* Ignore hardware exceptions */
166 };
167 
168 /* Reasons for stopping emulation */
169 enum {
170 	TILEM_STOP_TIMEOUT = 0,            /* stopped due to timeout */
171 	TILEM_STOP_BREAKPOINT = 1,         /* stopped due to breakpoint */
172 	TILEM_STOP_INVALID_INST = 2,       /* invalid instruction */
173 	TILEM_STOP_UNDOCUMENTED_INST = 4,  /* undocumented instruction */
174 	TILEM_STOP_EXCEPTION = 8,	   /* hardware exception */
175 	TILEM_STOP_LINK_STATE = 16,        /* blacklink state change */
176 	TILEM_STOP_LINK_READ_BYTE = 32,    /* graylink finished reading byte */
177 	TILEM_STOP_LINK_WRITE_BYTE = 64,   /* graylink finished writing byte */
178 	TILEM_STOP_LINK_ERROR = 128        /* graylink encountered error */
179 };
180 
181 /* Types of interrupt */
182 enum {
183 	TILEM_INTERRUPT_ON_KEY = 1, /* ON key pressed */
184 	TILEM_INTERRUPT_TIMER1 = 2, /* Main interrupt timer */
185 	TILEM_INTERRUPT_TIMER2 = 4, /* Alt. interrupt timer (83/83+) */
186 	TILEM_INTERRUPT_USER_TIMER1 = 8, /* Programmable timers (83+SE) */
187 	TILEM_INTERRUPT_USER_TIMER2 = 16,
188 	TILEM_INTERRUPT_USER_TIMER3 = 32,
189 	TILEM_INTERRUPT_LINK_ACTIVE = 512, /* Link port state changed */
190 	TILEM_INTERRUPT_LINK_READ = 1024,  /* Link assist read a byte */
191 	TILEM_INTERRUPT_LINK_IDLE = 2048,  /* Link assist is idle */
192 	TILEM_INTERRUPT_LINK_ERROR = 4096  /* Link assist failed */
193 };
194 
195 /* Types of hardware exception */
196 enum {
197 	TILEM_EXC_RAM_EXEC = 1,    /* Executing at invalid RAM address */
198 	TILEM_EXC_FLASH_EXEC = 2,  /* Executing at invalid Flash address */
199 	TILEM_EXC_FLASH_WRITE = 4, /* Writing to invalid Flash address */
200 	TILEM_EXC_INSTRUCTION = 8  /* Invalid instruction */
201 };
202 
203 /* Constant hardware timer IDs */
204 enum {
205 	TILEM_TIMER_NONE = 0,
206 	TILEM_TIMER_LCD_DELAY,
207 	TILEM_TIMER_FLASH_DELAY,
208 	TILEM_TIMER_LINK_ASSIST,
209 	TILEM_TIMER_USER1,
210 	TILEM_TIMER_USER2,
211 	TILEM_TIMER_USER3,
212 	TILEM_TIMER_HW
213 };
214 
215 #define TILEM_NUM_SYS_TIMERS (TILEM_TIMER_HW - 1)
216 
217 /* Type of a timer callback function.  Second arg is the callback data
218    passed to tilem_z80_add_timer(). */
219 typedef void (*TilemZ80TimerFunc)(TilemCalc*, void*);
220 
221 /* Type of a breakpoint test function.  Second arg is the memory
222    address (or opcode in the case of TILEM_BREAK_EXECUTE breakpoints.)
223    Third arg is the callback data passed to
224    tilem_z80_add_breakpoint(). */
225 typedef int (*TilemZ80BreakpointFunc)(TilemCalc*, dword, void*);
226 
227 typedef struct _TilemZ80Timer TilemZ80Timer;
228 typedef struct _TilemZ80Breakpoint TilemZ80Breakpoint;
229 
230 typedef struct _TilemZ80 {
231 	TilemZ80Regs r;
232 	unsigned int interrupts; /* Currently active interrupts */
233 	int clockspeed;		/* Current CPU speed (kHz) */
234 	int halted;
235 	unsigned int exception;
236 	dword clock;
237 	dword lastwrite;
238 	dword lastlcdwrite;
239 
240 	unsigned int emuflags;
241 
242 	int ntimers;
243 	TilemZ80Timer* timers;
244 	int timer_cpu;	       /* Sorted list of timers (CPU-based) */
245 	int timer_rt;	       /* Sorted list of timers (realtime) */
246 	int timer_free;	       /* List of free timer structs */
247 
248 	int nbreakpoints;
249 	TilemZ80Breakpoint* breakpoints;
250 	int breakpoint_mr;	/* Memory read breakpoints */
251 	int breakpoint_mx;	/* Memory exec breakpoints */
252 	int breakpoint_mw;	/* Memory write breakpoints */
253 	int breakpoint_pr;	/* Port read breakpoints */
254 	int breakpoint_pw;	/* Port write breakpoints */
255 	int breakpoint_op;	/* Opcode breakpoints */
256 	int breakpoint_mpr;	/* Physical mem read breakpoints */
257 	int breakpoint_mpx;	/* Physical mem exec breakpoints */
258 	int breakpoint_mpw;	/* Physical mem write breakpoints */
259 	int breakpoint_disabled; /* Disabled breakpoints */
260 	int breakpoint_free;	/* List of free bp structs */
261 
262 	int stopping;
263 	dword stop_reason;
264 	dword stop_mask;
265 	int stop_breakpoint;
266 } TilemZ80;
267 
268 /* Reset CPU */
269 void tilem_z80_reset(TilemCalc* calc);
270 
271 /* Halt simulation */
272 void tilem_z80_stop(TilemCalc* calc, dword reason);
273 
274 /* Set CPU speed (kHz) */
275 void tilem_z80_set_speed(TilemCalc* calc, int speed);
276 
277 /* Raise a hardware exception */
278 void tilem_z80_exception(TilemCalc* calc, unsigned type);
279 
280 /* Add a timer with the given callback function and data.  The
281    callback function will be called after 'count' time units, and
282    every 'period' time units thereafter.  If rt = 0, the time units
283    are CPU clock cycles; if rt = 1, time units are microseconds.
284 
285    Note that if a timer is set in response to a memory read or write,
286    the length of the delay may be off by as much as 19 clock cycles;
287    the precise timings for these are not (yet) properly emulated.
288    Timers set in response to port I/O events will be accurate to the
289    nearest CPU clock cycle.
290 
291    The timer is considered to have "fired" as soon as the specified
292    amount of time has elapsed.  The callback function, however, may
293    not be called until after the instruction finishes.  If multiple
294    timers fire during the same instruction, the order in which the
295    callback functions will be called is undefined.
296 
297    If you create a timer using this function (either repeating or
298    non-repeating), you must call tilem_z80_remove_timer() when the
299    timer is no longer needed.
300 */
301 int tilem_z80_add_timer(TilemCalc* calc, dword count, dword period,
302 			int rt, TilemZ80TimerFunc func, void* data);
303 
304 /* Change settings for an existing timer.  Arguments are the same as
305    above.  If count = 0, the timer is disabled. */
306 void tilem_z80_set_timer(TilemCalc* calc, int id, dword count,
307 			 dword period, int rt);
308 
309 /* Change period for an existing timer without affecting the current
310    interval. */
311 void tilem_z80_set_timer_period(TilemCalc* calc, int id, dword period);
312 
313 /* Delete a timer. */
314 void tilem_z80_remove_timer(TilemCalc* calc, int id);
315 
316 /* Check whether a timer is currently running. */
317 int tilem_z80_timer_running(TilemCalc* calc, int id)
318 	TILEM_ATTR_PURE;
319 
320 /* Get the number of clock ticks from now until the next time the
321    given timer fires.  (This may be negative, if the timer has already
322    fired during this instruction.)  NOTE: If the timer is disabled,
323    the return value is undefined. */
324 int tilem_z80_get_timer_clocks(TilemCalc* calc, int id)
325 	TILEM_ATTR_PURE;
326 
327 /* Get the number of microseconds from now until the next time the
328    given timer fires. */
329 int tilem_z80_get_timer_microseconds(TilemCalc* calc, int id)
330 	TILEM_ATTR_PURE;
331 
332 /* Add a breakpoint.  The breakpoint will be triggered if the address,
333    ANDed with the given mask, falls between the given start and end
334    inclusive.  If a callback function is specified it acts as an
335    additional filter, to determine whether the simulation should be
336    halted. */
337 int tilem_z80_add_breakpoint(TilemCalc* calc, int type,
338 			     dword start, dword end, dword mask,
339 			     TilemZ80BreakpointFunc func, void* data);
340 
341 /* Remove the given breakpoint. */
342 void tilem_z80_remove_breakpoint(TilemCalc* calc, int id);
343 
344 /* Enable the given breakpoint. */
345 void tilem_z80_enable_breakpoint(TilemCalc* calc, int id);
346 
347 /* Disable the given breakpoint. */
348 void tilem_z80_disable_breakpoint(TilemCalc* calc, int id);
349 
350 /* Check whether the given breakpoint is currently enabled. */
351 int tilem_z80_breakpoint_enabled(TilemCalc* calc, int id);
352 
353 /* Get the type of the given breakpoint. */
354 int tilem_z80_get_breakpoint_type(TilemCalc* calc, int id);
355 
356 /* Get the start address of the given breakpoint. */
357 dword tilem_z80_get_breakpoint_address_start(TilemCalc* calc, int id);
358 
359 /* Get the start address of the given breakpoint. */
360 dword tilem_z80_get_breakpoint_address_end(TilemCalc* calc, int id);
361 
362 /* Get the start address of the given breakpoint. */
363 dword tilem_z80_get_breakpoint_address_mask(TilemCalc* calc, int id);
364 
365 /* Get the callback/filter function associated to the given breakpoint. */
366 TilemZ80BreakpointFunc tilem_z80_get_breakpoint_callback(TilemCalc* calc,
367 							 int id);
368 
369 /* Get the data associated to the given breakpoint. */
370 void* tilem_z80_get_breakpoint_data(TilemCalc* calc, int id);
371 
372 /* Set the type of the given breakpoint. */
373 void tilem_z80_set_breakpoint_type(TilemCalc* calc, int id, int type);
374 
375 /* Set the start address of the given breakpoint. */
376 void tilem_z80_set_breakpoint_address_start(TilemCalc* calc, int id,
377 					    dword start);
378 
379 /* Set the start address of the given breakpoint. */
380 void tilem_z80_set_breakpoint_address_end(TilemCalc* calc, int id, dword end);
381 
382 /* Set the start address of the given breakpoint. */
383 void tilem_z80_set_breakpoint_address_mask(TilemCalc* calc, int id, dword mask);
384 
385 /* Set the callback/filter function associated to the given breakpoint. */
386 void tilem_z80_set_breakpoint_callback(TilemCalc* calc, int id,
387 				       TilemZ80BreakpointFunc func);
388 
389 /* Set the data associated to the given breakpoint. */
390 void tilem_z80_set_breakpoint_data(TilemCalc* calc, int id, void* data);
391 
392 
393 /* Run the simulated CPU for the given number of clock
394    ticks/microseconds, or until a breakpoint is hit or
395    tilem_z80_stop() is called. */
396 dword tilem_z80_run(TilemCalc* calc, int clocks, int* remaining);
397 dword tilem_z80_run_time(TilemCalc* calc, int microseconds, int* remaining);
398 
399 
400 /* LCD driver */
401 
402 /* Emulation flags */
403 enum {
404 	TILEM_LCD_REQUIRE_DELAY = 1,	  /* Emulate required delay
405 					     between commands */
406 	TILEM_LCD_REQUIRE_LONG_DELAY = 2  /* Require extra-long delay */
407 };
408 
409 typedef struct _TilemLCD {
410 	/* Common settings */
411 	byte active;		/* LCD driver active */
412 	byte contrast;		/* Contrast value (0-63) */
413 	int rowstride;		/* Number of bytes per row */
414 	unsigned int emuflags;
415 
416 	/* T6A43 internal driver */
417 	word addr;		/* Memory address */
418 
419 	/* T6A04 external driver */
420 	byte mode;		/* I/O mode (0 = 6bit, 1 = 8bit) */
421 	byte inc;		/* Increment mode (4-7) */
422 	byte nextbyte;		/* Output register */
423 	int x, y;		/* Current position */
424 	int rowshift;		/* Starting row for display */
425 	byte busy;
426 } TilemLCD;
427 
428 /* Reset LCD driver */
429 void tilem_lcd_reset(TilemCalc* calc);
430 
431 /* Get LCD driver status (port 10 input) */
432 byte tilem_lcd_t6a04_status(TilemCalc* calc);
433 
434 /* Send command to LCD driver (port 10 output) */
435 void tilem_lcd_t6a04_control(TilemCalc* calc, byte val);
436 
437 /* Read data from LCD driver (port 11 input) */
438 byte tilem_lcd_t6a04_read(TilemCalc* calc);
439 
440 /* Write data to LCD driver (port 11 output) */
441 void tilem_lcd_t6a04_write(TilemCalc* calc, byte val);
442 
443 /* Get screen image (T6A04 style) */
444 void tilem_lcd_t6a04_get_data(TilemCalc* calc, byte* data);
445 
446 /* Get screen image (T6A43 style) */
447 void tilem_lcd_t6a43_get_data(TilemCalc* calc, byte* data);
448 
449 /* Callback for TILEM_TIMER_LCD_DELAY */
450 void tilem_lcd_delay_timer(TilemCalc* calc, void* data);
451 
452 
453 
454 /* DBUS link port driver */
455 
456 /* Link port / assist mode flags */
457 enum {
458 	TILEM_LINK_MODE_ASSIST        = 1,  /* Enable link assist */
459 	TILEM_LINK_MODE_NO_TIMEOUT    = 2,  /* Assist doesn't time out (xp) */
460 	TILEM_LINK_MODE_INT_ON_ACTIVE = 4,  /* Interrupt on state change */
461 	TILEM_LINK_MODE_INT_ON_READ   = 8,  /* Interrupt on asst. read */
462 	TILEM_LINK_MODE_INT_ON_IDLE   = 16, /* Interrupt when asst. idle */
463 	TILEM_LINK_MODE_INT_ON_ERROR  = 32  /* Interrupt on asst. error */
464 };
465 
466 /* Link port state flags */
467 enum {
468 	TILEM_LINK_ASSIST_READ_BYTE   = 1, /* Assisted read finished */
469 	TILEM_LINK_ASSIST_READ_BUSY   = 2, /* Assisted read in progress */
470 	TILEM_LINK_ASSIST_READ_ERROR  = 4, /* Assisted read failed */
471 	TILEM_LINK_ASSIST_WRITE_BUSY  = 8, /* Assisted write in progress */
472 	TILEM_LINK_ASSIST_WRITE_ERROR = 16 /* Assisted write failed */
473 };
474 
475 /* Link emulation mode */
476 enum {
477 	TILEM_LINK_EMULATOR_NONE = 0,  /* Link port disconnected */
478 	TILEM_LINK_EMULATOR_BLACK = 1, /* Connected to virtual BlackLink
479 					  (exit emulation on state change) */
480 	TILEM_LINK_EMULATOR_GRAY = 2   /* Connected to virtual GrayLink
481 					  (auto send/receive bytes) */
482 };
483 
484 typedef struct _TilemLinkport {
485 	byte lines, extlines;	/* Link line state for TI/PC
486 				   0 = both lines high
487 				   1 = red wire low
488 				   2 = white wire low
489 				   3 = both wires low */
490 
491 	unsigned int mode;	/* Mode flags */
492 
493 	/* Internal link assist */
494 	unsigned int assistflags; /* Assist state */
495 	byte assistin;		  /* Input buffer (recv from PC) */
496 	byte assistinbits;	  /* Input bit count */
497 	byte assistout;		  /* Output buffer (send to PC) */
498 	byte assistoutbits;	  /* Output bit count */
499 	byte assistlastbyte;	  /* Last byte received */
500 
501 	/* External link emulator */
502 	byte linkemu;
503 	byte graylinkin;	/* Input buffer (recv from TI) */
504 	byte graylinkinbits;	/* Input bit count */
505 	byte graylinkout;	/* Output buffer (send to TI) */
506 	byte graylinkoutbits;	/* Output bit count */
507 } TilemLinkport;
508 
509 /* Reset link port */
510 void tilem_linkport_reset(TilemCalc* calc);
511 
512 /* Read link port lines */
513 byte tilem_linkport_get_lines(TilemCalc* calc);
514 
515 /* Set link port lines */
516 void tilem_linkport_set_lines(TilemCalc* calc, byte lines);
517 
518 /* Read from, and clear, link assist input buffer */
519 byte tilem_linkport_read_byte(TilemCalc* calc);
520 
521 /* Write to link assist output buffer */
522 void tilem_linkport_write_byte(TilemCalc* calc, byte data);
523 
524 /* Get assist state */
525 unsigned int tilem_linkport_get_assist_flags(TilemCalc* calc);
526 
527 /* Set link port mode */
528 void tilem_linkport_set_mode(TilemCalc* calc, unsigned int mode);
529 
530 /* Set line states for virtual BlackLink */
531 void tilem_linkport_blacklink_set_lines(TilemCalc* calc, byte lines);
532 
533 /* Get line states from virtual BlackLink */
534 byte tilem_linkport_blacklink_get_lines(TilemCalc* calc);
535 
536 /* Reset GrayLink */
537 void tilem_linkport_graylink_reset(TilemCalc* calc);
538 
539 /* Check if GrayLink is ready to send data */
540 int tilem_linkport_graylink_ready(TilemCalc* calc);
541 
542 /* Send a byte via virtual GrayLink */
543 int tilem_linkport_graylink_send_byte(TilemCalc* calc, byte value);
544 
545 /* Get byte received by virtual GrayLink (-1 = none available) */
546 int tilem_linkport_graylink_get_byte(TilemCalc* calc);
547 
548 /* Callback for TILEM_TIMER_LINK_ASSIST */
549 void tilem_linkport_assist_timer(TilemCalc* calc, void* data);
550 
551 
552 /* Keypad */
553 
554 typedef struct _TilemKeypad {
555 	byte group;
556 	byte onkeydown;
557 	byte onkeyint;
558 	byte keysdown[8];
559 } TilemKeypad;
560 
561 /* Reset keypad */
562 void tilem_keypad_reset(TilemCalc* calc);
563 
564 /* Set current group (port 1 output) */
565 void tilem_keypad_set_group(TilemCalc* calc, byte group);
566 
567 /* Read keys from current group (port 1 input) */
568 byte tilem_keypad_read_keys(TilemCalc* calc);
569 
570 /* Press a key */
571 void tilem_keypad_press_key(TilemCalc* calc, int scancode);
572 
573 /* Release a key */
574 void tilem_keypad_release_key(TilemCalc* calc, int scancode);
575 
576 
577 /* Flash */
578 
579 /* Emulation flags */
580 enum {
581 	TILEM_FLASH_REQUIRE_DELAY = 1 /* Require delay after
582 					 program/erase */
583 };
584 
585 typedef struct _TilemFlashSector {
586 	dword start;
587 	dword size;
588 	byte protectgroup;
589 } TilemFlashSector;
590 
591 typedef struct _TilemFlash {
592 	byte unlock;
593 	byte state;
594 	unsigned int emuflags;
595 	byte busy;
596 	dword progaddr;
597 	byte progbyte;
598 	byte toggles;
599 	byte overridegroup;
600 } TilemFlash;
601 
602 /* Reset Flash */
603 void tilem_flash_reset(TilemCalc* calc);
604 
605 /* Read a byte from the Flash chip */
606 byte tilem_flash_read_byte(TilemCalc* calc, dword pa);
607 
608 /* Erase a Flash sector */
609 void tilem_flash_erase_address(TilemCalc* calc, dword pa);
610 
611 /* Write a byte to the Flash chip */
612 void tilem_flash_write_byte(TilemCalc* calc, dword pa, byte v);
613 
614 /* Callback for TILEM_TIMER_FLASH_DELAY */
615 void tilem_flash_delay_timer(TilemCalc* calc, void* data);
616 
617 
618 /* MD5 assist */
619 
620 enum {
621 	TILEM_MD5_REG_A = 0,	/* initial 'a' value */
622 	TILEM_MD5_REG_B = 1,	/* 'b' value */
623 	TILEM_MD5_REG_C = 2,	/* 'c' value */
624 	TILEM_MD5_REG_D = 3,	/* 'd' value */
625 	TILEM_MD5_REG_X = 4,	/* 'X' (or 'T') value */
626 	TILEM_MD5_REG_T = 5	/* 'T' (or 'X') value */
627 };
628 
629 enum {
630 	TILEM_MD5_FUNC_FF = 0,
631 	TILEM_MD5_FUNC_GG = 1,
632 	TILEM_MD5_FUNC_HH = 2,
633 	TILEM_MD5_FUNC_II = 3
634 };
635 
636 typedef struct _TilemMD5Assist {
637 	dword regs[6];
638 	byte shift;
639 	byte mode;
640 } TilemMD5Assist;
641 
642 /* Reset MD5 assist */
643 void tilem_md5_assist_reset(TilemCalc* calc);
644 
645 /* Get output value */
646 dword tilem_md5_assist_get_value(TilemCalc* calc);
647 
648 
649 /* Programmable timers */
650 
651 #define TILEM_MAX_USER_TIMERS 3
652 
653 enum {
654 	TILEM_USER_TIMER_LOOP        = 1,   /* loop when counter
655 					       reaches 0 */
656 	TILEM_USER_TIMER_INTERRUPT   = 2,   /* generate interrupt when
657 					       finished */
658 	TILEM_USER_TIMER_OVERFLOW    = 4,   /* timer has expired at
659 					       least twice since last
660 					       mode setting */
661 	TILEM_USER_TIMER_FINISHED    = 256, /* timer has expired at
662 					       least once since last
663 					       mode setting (port 4
664 					       status bit) */
665 	TILEM_USER_TIMER_NO_HALT_INT = 512  /* suppress interrupt if
666 					       CPU is halted */
667 };
668 
669 typedef struct _TilemUserTimer {
670 	byte frequency;
671 	byte loopvalue;
672 	unsigned int status;
673 } TilemUserTimer;
674 
675 /* Reset timers */
676 void tilem_user_timers_reset(TilemCalc* calc);
677 
678 /* Set frequency control register */
679 void tilem_user_timer_set_frequency(TilemCalc* calc, int n, byte value);
680 
681 /* Set status flags */
682 void tilem_user_timer_set_mode(TilemCalc* calc, int n, byte mode);
683 
684 /* Start timer */
685 void tilem_user_timer_start(TilemCalc* calc, int n, byte value);
686 
687 /* Get timer value */
688 byte tilem_user_timer_get_value(TilemCalc* calc, int n);
689 
690 /* Callback function */
691 void tilem_user_timer_expired(TilemCalc* calc, void* data);
692 
693 
694 /* Calculators */
695 
696 /* Model IDs */
697 enum {
698 	TILEM_CALC_TI73 = '7',	       /* TI-73 / TI-73 Explorer */
699 	TILEM_CALC_TI76 = 'f',	       /* TI-76.fr */
700 	TILEM_CALC_TI81 = '1',	       /* TI-81 */
701 	TILEM_CALC_TI82 = '2',	       /* TI-82 */
702 	TILEM_CALC_TI83 = '3',	       /* TI-83 / TI-82 STATS [.fr] */
703 	TILEM_CALC_TI83P = 'p',	       /* TI-83 Plus */
704 	TILEM_CALC_TI83P_SE = 's',     /* TI-83 Plus Silver Edition */
705 	TILEM_CALC_TI84P = '4',	       /* TI-84 Plus */
706 	TILEM_CALC_TI84P_SE = 'z',     /* TI-84 Plus Silver Edition */
707 	TILEM_CALC_TI84P_NSPIRE = 'n', /* TI-Nspire 84 Plus emulator */
708 	TILEM_CALC_TI85 = '5',	       /* TI-85 */
709 	TILEM_CALC_TI86 = '6'	       /* TI-86 */
710 };
711 
712 /* Calculator flags */
713 enum {
714 	TILEM_CALC_HAS_LINK        = 1,  /* Has link port */
715 	TILEM_CALC_HAS_LINK_ASSIST = 2,  /* Has hardware link assist */
716 	TILEM_CALC_HAS_USB         = 4,  /* Has USB controller */
717 	TILEM_CALC_HAS_FLASH       = 8,  /* Has (writable) Flash */
718 	TILEM_CALC_HAS_T6A04       = 16, /* Has separate LCD driver */
719 	TILEM_CALC_HAS_MD5_ASSIST  = 32  /* Has hardware MD5 assist */
720 };
721 
722 /* Calculator hardware description */
723 struct _TilemHardware {
724 	char model_id;	      /* Single character identifying model */
725 	const char* name;     /* Short name (e.g. ti83p) */
726 	const char* desc;     /* Full name (e.g. TI-83 Plus) */
727 
728 	unsigned int flags;
729 
730 	int lcdwidth, lcdheight; /* Size of LCD */
731 	dword romsize, ramsize;	 /* Size of ROM and RAM */
732 	dword lcdmemsize;	 /* Size of external LCD memory */
733 	byte rampagemask;	 /* Bit mask used for RAM page */
734 
735 	int nflashsectors;
736 	const TilemFlashSector* flashsectors;
737 
738 	int nusertimers;
739 
740 	int nhwregs;		 /* Number of hardware registers */
741 	const char** hwregnames; /* Harware register names */
742 
743 	int nhwtimers;		 /* Number of hardware timers */
744 	const char** hwtimernames; /* Hardware timer names*/
745 
746 	const char** keynames;
747 
748 	/* Reset calculator */
749 	void	(*reset)	(TilemCalc*);
750 
751 	/* Reinitialize after loading state */
752 	void    (*stateloaded)  (TilemCalc*, int);
753 
754 	/* Z80 ports and memory */
755 	byte	(*z80_in)	(TilemCalc*, dword);
756 	void	(*z80_out) 	(TilemCalc*, dword, byte);
757 	void	(*z80_wrmem)	(TilemCalc*, dword, byte);
758 	byte	(*z80_rdmem)	(TilemCalc*, dword);
759 	byte	(*z80_rdmem_m1)	(TilemCalc*, dword);
760 
761 	/* Evaluate a non-standard instruction */
762 	void    (*z80_instr)    (TilemCalc*, dword);
763 
764 	/* Persistent timer callback */
765 	void    (*z80_ptimer)   (TilemCalc*, int);
766 
767 	/* Retrieve LCD contents */
768 	void    (*get_lcd)      (TilemCalc*, byte*);
769 
770 	/* Convert physical <-> logical addresses */
771 	dword	(*mem_ltop)	(TilemCalc*, dword);
772 	dword	(*mem_ptol)	(TilemCalc*, dword);
773 };
774 
775 /* Current state of the calculator */
776 struct _TilemCalc {
777 	TilemHardware hw;
778 
779 	TilemZ80 z80;
780 	byte* mem;
781 	byte* ram;
782 	byte* lcdmem;
783 	byte mempagemap[4];
784 
785 	TilemLCD lcd;
786 	TilemLinkport linkport;
787 	TilemKeypad keypad;
788 	TilemFlash flash;
789 	TilemMD5Assist md5assist;
790 	TilemUserTimer usertimers[TILEM_MAX_USER_TIMERS];
791 
792 	byte poweronhalt;	/* System power control.  If this is
793 				   zero, turn off LCD, timers,
794 				   etc. when CPU halts */
795 
796 	byte battery;		/* Battery level (units of 0.1 V) */
797 
798 	dword* hwregs;
799 };
800 
801 /* Get a list of supported hardware models */
802 void tilem_get_supported_hardware(const TilemHardware*** models,
803 				  int* nmodels);
804 
805 /* Create a new calculator.  This function returns NULL if
806    insufficient memory is available. */
807 TilemCalc* tilem_calc_new(char id);
808 
809 /* Make an exact copy of an existing calculator (including both
810    internal and external state.)  Be careful when using this in
811    conjunction with custom timer/breakpoint callback functions.  This
812    function returns NULL if insufficient memory is available. */
813 TilemCalc* tilem_calc_copy(TilemCalc* calc);
814 
815 /* Free a calculator that was previously created by tilem_calc_new()
816    or tilem_calc_copy(). */
817 void tilem_calc_free(TilemCalc* calc);
818 
819 /* Reset calculator (essentially, remove and replace batteries.) */
820 void tilem_calc_reset(TilemCalc* calc);
821 
822 /* Load calculator state from ROM and/or save files. */
823 int tilem_calc_load_state(TilemCalc* calc, FILE* romfile, FILE* savfile);
824 
825 /* Save calculator state to ROM and/or save files. */
826 int tilem_calc_save_state(TilemCalc* calc, FILE* romfile, FILE* savfile);
827 
828 
829 /* LCD image conversion/scaling */
830 
831 /* Scaling algorithms */
832 enum {
833 	TILEM_SCALE_FAST = 0,	/* Fast scaling (nearest neighbor) -
834 				   looks lousy unless the scaling
835 				   factor is fairly large */
836 	TILEM_SCALE_SMOOTH	/* Smooth scaling - slower and looks
837 				   better at small sizes; note that
838 				   this falls back to using the "fast"
839 				   algorithm if we are scaling up by
840 				   an integer factor */
841 };
842 
843 /* Buffer representing a snapshot of the LCD state */
844 typedef struct _TilemLCDBuffer {
845 	byte width;             /* Width of LCD */
846 	byte height;            /* Height of LCD */
847 	byte rowstride;         /* Offset between rows in buffer */
848 	byte contrast;          /* Contrast value (0-63) */
849 	dword stamp;            /* Timestamp */
850 	dword tmpbufsize;       /* Size of temporary buffer */
851 	byte *data;             /* Image data (rowstride*height bytes) */
852 	void *tmpbuf;           /* Temporary buffer used for scaling */
853 } TilemLCDBuffer;
854 
855 /* Create new TilemLCDBuffer. */
856 TilemLCDBuffer* tilem_lcd_buffer_new(void)
857 	TILEM_ATTR_MALLOC;
858 
859 /* Free  a TilemLCDBuffer. */
860 void tilem_lcd_buffer_free(TilemLCDBuffer *buf);
861 
862 /* Convert current LCD memory contents to a TilemLCDBuffer (i.e., a
863    monochrome snapshot.) */
864 void tilem_lcd_get_frame(TilemCalc * restrict calc,
865                          TilemLCDBuffer * restrict buf);
866 
867 /* Convert current LCD memory contents to a TilemLCDBuffer (i.e., a
868    monochrome snapshot.)
869    Output is only 0 and 1 */
870 void tilem_lcd_get_frame1(TilemCalc * restrict calc,
871                          TilemLCDBuffer * restrict buf);
872 
873 /* Convert and scale image to an 8-bit indexed image buffer.  IMGWIDTH
874    and IMGHEIGHT are the width and height of the output image,
875    ROWSTRIDE the number of bytes from the start of one row to the next
876    (often equal to IMGWIDTH), and SCALETYPE the scaling algorithm to
877    use. */
878 void tilem_draw_lcd_image_indexed(TilemLCDBuffer * restrict frm,
879                                   byte * restrict buffer,
880                                   int imgwidth, int imgheight,
881                                   int rowstride, int scaletype);
882 
883 /* Convert and scale image to a 24-bit RGB or 32-bit RGBA image
884    buffer.  IMGWIDTH and IMGHEIGHT are the width and height of the
885    output image, ROWSTRIDE the number of bytes from the start of one
886    row to the next (often equal to 3 * IMGWIDTH), PIXBYTES the number
887    of bytes per pixel (3 or 4), and SCALETYPE the scaling algorithm to
888    use.  PALETTE is an array of 256 color values. */
889 void tilem_draw_lcd_image_rgb(TilemLCDBuffer * restrict frm,
890                               byte * restrict buffer,
891                               int imgwidth, int imgheight, int rowstride,
892                               int pixbytes, const dword * restrict palette,
893                               int scaletype);
894 
895 /* Calculate a color palette for use with the above functions.
896    RLIGHT, GLIGHT, BLIGHT are the RGB components (0 to 255) of the
897    lightest possible color; RDARK, GDARK, BDARK are the RGB components
898    of the darkest possible color.  GAMMA is the gamma value for the
899    output device (2.2 for most current computer displays and image
900    file formats.) */
901 dword* tilem_color_palette_new(int rlight, int glight, int blight,
902                                int rdark, int gdark, int bdark,
903                                double gamma);
904 
905 /* Calculate a color palette, as above, and convert it to a packed
906    array of bytes (R, G, B) */
907 byte* tilem_color_palette_new_packed(int rlight, int glight, int blight,
908                                      int rdark, int gdark, int bdark,
909                                      double gamma);
910 
911 
912 /* Grayscale LCD simulation */
913 
914 typedef struct _TilemGrayLCD TilemGrayLCD;
915 
916 /* Create a new LCD and attach to a calculator.  Sampling for
917    grayscale is done across WINDOWSIZE frames, with samples taken
918    every SAMPLEINT microseconds. */
919 TilemGrayLCD* tilem_gray_lcd_new(TilemCalc *calc, int windowsize,
920                                  int sampleint);
921 
922 /* Detach and free an LCD. */
923 void tilem_gray_lcd_free(TilemGrayLCD *glcd);
924 
925 /* Generate a grayscale image for the next frame, based on current
926    calculator state.  This function also updates the frame counter and
927    internal state; for proper grayscale behavior, this function needs
928    to be called at regular intervals. */
929 void tilem_gray_lcd_get_frame(TilemGrayLCD * restrict glcd,
930                               TilemLCDBuffer * restrict frm);
931 
932 
933 /* Miscellaneous functions */
934 
935 /* Guess calculator type for a ROM file */
936 char tilem_guess_rom_type(FILE* romfile);
937 
938 /* Get calculator type for a SAV file */
939 char tilem_get_sav_type(FILE* savfile);
940 
941 /* Check validity of calculator certificate; repair if necessary */
942 void tilem_calc_fix_certificate(TilemCalc* calc, byte* cert,
943                                 int app_start, int app_end,
944                                 unsigned exptab_offset);
945 
946 #ifdef __cplusplus
947 }
948 #endif
949 
950 #endif
951