1 /*
2 * TilEm II
3 *
4 * Copyright (c) 2011-2012 Benjamin Moody
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program 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 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <gtk/gtk.h>
28 #include <glib/gstdio.h>
29 #include <ticalcs.h>
30 #include <tilem.h>
31
32 #include "gui.h"
33 #include "emucore.h"
34 #include "msgbox.h"
35 #include "filedlg.h"
36
37 #define MILLISEC_PER_FRAME 30
38 #define MICROSEC_PER_FRAME (MILLISEC_PER_FRAME * 1000)
39
40 #define GRAY_WINDOW_SIZE 4
41 #define GRAY_SAMPLE_INT 200
42
43
44 /* Lock emulator. Notify the core loop that we wish to do so - note
45 that if the core is running at full speed, it keeps the mutex
46 locked almost all the time, so we need to explicitly ask it to
47 yield. (This may not be necessary with all mutex implementations,
48 but definitely is necessary with some.) */
tilem_calc_emulator_lock(TilemCalcEmulator * emu)49 void tilem_calc_emulator_lock(TilemCalcEmulator *emu)
50 {
51 g_atomic_int_inc(&emu->calc_lock_waiting);
52 g_mutex_lock(emu->calc_mutex);
53 }
54
55 /* Unlock emulator and (if no other threads are waiting to lock it)
56 wake up the core thread. This also serves to resume emulation if
57 the calculator has been powered off. */
tilem_calc_emulator_unlock(TilemCalcEmulator * emu)58 void tilem_calc_emulator_unlock(TilemCalcEmulator *emu)
59 {
60 if (g_atomic_int_dec_and_test(&emu->calc_lock_waiting))
61 g_cond_signal(emu->calc_wakeup_cond);
62 g_mutex_unlock(emu->calc_mutex);
63 }
64
refresh_lcd(gpointer data)65 static gboolean refresh_lcd(gpointer data)
66 {
67 TilemCalcEmulator* emu = data;
68
69 if (emu->ewin)
70 tilem_emulator_window_refresh_lcd(emu->ewin);
71
72 return FALSE;
73 }
74
tmr_screen_update(TilemCalc * calc,void * data)75 static void tmr_screen_update(TilemCalc *calc, void *data)
76 {
77 TilemCalcEmulator *emu = data;
78
79 g_mutex_lock(emu->lcd_mutex);
80
81 if (emu->glcd)
82 tilem_gray_lcd_get_frame(emu->glcd, emu->lcd_buffer);
83 else
84 tilem_lcd_get_frame(calc, emu->lcd_buffer);
85
86 if (emu->anim) {
87 if (emu->anim_grayscale) {
88 tilem_animation_append_frame(emu->anim,
89 emu->lcd_buffer,
90 MILLISEC_PER_FRAME);
91 }
92 else {
93 tilem_lcd_get_frame(calc, emu->tmp_lcd_buffer);
94 tilem_animation_append_frame(emu->anim,
95 emu->tmp_lcd_buffer,
96 MILLISEC_PER_FRAME);
97 }
98 }
99
100 if (!emu->lcd_update_pending) {
101 emu->lcd_update_pending = TRUE;
102 g_idle_add_full(G_PRIORITY_DEFAULT, &refresh_lcd, emu, NULL);
103 }
104
105 g_mutex_unlock(emu->lcd_mutex);
106 }
107
cancel_animation(TilemCalcEmulator * emu)108 static void cancel_animation(TilemCalcEmulator *emu)
109 {
110 if (emu->anim)
111 g_object_unref(emu->anim);
112 emu->anim = NULL;
113 }
114
get_toplevel(TilemCalcEmulator * emu)115 static GtkWidget *get_toplevel(TilemCalcEmulator *emu)
116 {
117 if (emu->ewin)
118 return emu->ewin->window;
119 else
120 return NULL;
121 }
122
link_update_nop()123 static void link_update_nop()
124 {
125 }
126
tilem_calc_emulator_new()127 TilemCalcEmulator *tilem_calc_emulator_new()
128 {
129 TilemCalcEmulator *emu = g_new0(TilemCalcEmulator, 1);
130 CalcUpdate *update;
131
132 emu->calc_mutex = g_mutex_new();
133 emu->calc_wakeup_cond = g_cond_new();
134 emu->lcd_mutex = g_mutex_new();
135
136 tilem_config_get("emulation",
137 "grayscale/b=1", &emu->grayscale,
138 "limit_speed/b=1", &emu->limit_speed,
139 NULL);
140
141 emu->task_queue = g_queue_new();
142 emu->task_finished_cond = g_cond_new();
143
144 emu->timer = g_timer_new();
145
146 emu->pbar_mutex = g_mutex_new();
147
148 update = g_new0(CalcUpdate, 1);
149 update->start = &link_update_nop;
150 update->stop = &link_update_nop;
151 update->refresh = &link_update_nop;
152 update->pbar = &link_update_nop;
153 update->label = &link_update_nop;
154 emu->link_update = update;
155
156 return emu;
157 }
158
tilem_calc_emulator_free(TilemCalcEmulator * emu)159 void tilem_calc_emulator_free(TilemCalcEmulator *emu)
160 {
161 g_return_if_fail(emu != NULL);
162
163 tilem_calc_emulator_cancel_tasks(emu);
164
165 tilem_calc_emulator_lock(emu);
166 cancel_animation(emu);
167 emu->exiting = TRUE;
168 tilem_calc_emulator_unlock(emu);
169
170 if (emu->z80_thread)
171 g_thread_join(emu->z80_thread);
172
173 g_free(emu->key_queue);
174
175 g_free(emu->rom_file_name);
176 g_free(emu->state_file_name);
177
178 g_mutex_free(emu->calc_mutex);
179 g_mutex_free(emu->lcd_mutex);
180 g_cond_free(emu->calc_wakeup_cond);
181
182 g_cond_free(emu->task_finished_cond);
183 g_queue_free(emu->task_queue);
184
185 g_timer_destroy(emu->timer);
186
187 g_mutex_free(emu->pbar_mutex);
188 g_free(emu->link_update);
189
190 if (emu->lcd_buffer)
191 tilem_lcd_buffer_free(emu->lcd_buffer);
192 if (emu->tmp_lcd_buffer)
193 tilem_lcd_buffer_free(emu->tmp_lcd_buffer);
194 if (emu->glcd)
195 tilem_gray_lcd_free(emu->glcd);
196 if (emu->calc)
197 tilem_calc_free(emu->calc);
198
199 g_free(emu);
200 }
201
get_sav_name(const char * romname)202 static char *get_sav_name(const char *romname)
203 {
204 char *dname, *bname, *sname, *suff;
205
206 dname = g_path_get_dirname(romname);
207 bname = g_path_get_basename(romname);
208
209 if ((suff = strrchr(bname, '.')))
210 *suff = 0;
211 sname = g_strconcat(dname, G_DIR_SEPARATOR_S, bname, ".sav", NULL);
212
213 g_free(dname);
214 g_free(bname);
215 return sname;
216 }
217
tilem_calc_emulator_load_state(TilemCalcEmulator * emu,const char * romfname,const char * statefname,int model,GError ** err)218 gboolean tilem_calc_emulator_load_state(TilemCalcEmulator *emu,
219 const char *romfname,
220 const char *statefname,
221 int model, GError **err)
222 {
223 const char *modelname;
224 FILE *romfile, *savfile;
225 char *rname = NULL, *sname = NULL;
226 TilemCalc *calc;
227 char *dname;
228 int errnum;
229
230 g_return_val_if_fail(emu != NULL, FALSE);
231 g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
232
233 tilem_calc_emulator_cancel_tasks(emu);
234
235 if (romfname)
236 rname = g_strdup(romfname);
237 if (!sname && statefname)
238 sname = g_strdup(statefname);
239
240 /* Choose ROM file */
241
242 if (!rname && model) {
243 modelname = model_to_name(model);
244 g_return_val_if_fail(modelname != NULL, FALSE);
245 if (sname) g_free(sname);
246 tilem_config_get(modelname,
247 "rom_file/f", &rname,
248 "state_file/f", &sname,
249 NULL);
250 }
251
252 if (!rname) {
253 g_set_error(err, TILEM_EMULATOR_ERROR,
254 TILEM_EMULATOR_ERROR_NO_ROM,
255 "No ROM file specified");
256 g_free(rname);
257 g_free(sname);
258 return FALSE;
259 }
260
261 /* Open ROM file */
262
263 romfile = g_fopen(rname, "rb");
264 if (!romfile) {
265 errnum = errno;
266 dname = g_filename_display_basename(rname);
267 g_set_error(err, G_FILE_ERROR,
268 g_file_error_from_errno(errnum),
269 "Unable to open %s for reading: %s",
270 dname, g_strerror(errnum));
271 g_free(dname);
272 g_free(rname);
273 g_free(sname);
274 return FALSE;
275 }
276
277 /* Open state file */
278
279 if (!sname)
280 sname = get_sav_name(rname);
281
282 savfile = g_fopen(sname, "rb");
283
284 if (!savfile && errno != ENOENT) {
285 errnum = errno;
286 dname = g_filename_display_basename(sname);
287 g_set_error(err, G_FILE_ERROR,
288 g_file_error_from_errno(errnum),
289 "Unable to open %s for reading: %s",
290 dname, g_strerror(errnum));
291 g_free(dname);
292 g_free(rname);
293 g_free(sname);
294 fclose(romfile);
295 return FALSE;
296 }
297
298 /* Determine model from state file, if possible */
299
300 if (!model && savfile)
301 model = tilem_get_sav_type(savfile);
302
303 /* Otherwise, guess from ROM file; ask user if ambiguous */
304
305 if (!model) {
306 model = tilem_guess_rom_type(romfile);
307 if (model) {
308 model = choose_rom_popup(get_toplevel(emu),
309 rname, model);
310 }
311 else {
312 dname = g_filename_display_basename(rname);
313 g_set_error(err, TILEM_EMULATOR_ERROR,
314 TILEM_EMULATOR_ERROR_INVALID_ROM,
315 "The file %s is not a recognized"
316 " calculator ROM file.",
317 dname);
318 g_free(dname);
319 }
320 }
321
322 if (!model) {
323 fclose(romfile);
324 if (savfile) fclose(savfile);
325 g_free(rname);
326 g_free(sname);
327 return FALSE;
328 }
329
330 /* Create new calc, and load state */
331
332 calc = tilem_calc_new(model);
333 if (tilem_calc_load_state(calc, romfile, savfile)) {
334 g_set_error(err, TILEM_EMULATOR_ERROR,
335 TILEM_EMULATOR_ERROR_INVALID_STATE,
336 "The specified ROM or state file is invalid.");
337 fclose(romfile);
338 if (savfile) fclose(savfile);
339 g_free(rname);
340 g_free(sname);
341 return FALSE;
342 }
343
344 if (!savfile) {
345 /* save model as default for the future */
346 savfile = g_fopen(sname, "wb");
347 if (savfile)
348 fprintf(savfile, "MODEL = %s\n", calc->hw.name);
349 }
350
351 fclose(romfile);
352 if (savfile) fclose(savfile);
353
354 /* Switch to new calc */
355
356 tilem_calc_emulator_lock(emu);
357
358 cancel_animation(emu);
359
360 if (emu->glcd)
361 tilem_gray_lcd_free(emu->glcd);
362 if (emu->calc)
363 tilem_calc_free(emu->calc);
364
365 emu->calc = calc;
366 emu->lcd_buffer = tilem_lcd_buffer_new();
367 emu->tmp_lcd_buffer = tilem_lcd_buffer_new();
368
369 if (emu->grayscale)
370 emu->glcd = tilem_gray_lcd_new(calc, GRAY_WINDOW_SIZE,
371 GRAY_SAMPLE_INT);
372 else
373 emu->glcd = NULL;
374
375 tilem_z80_add_timer(calc, MICROSEC_PER_FRAME,
376 MICROSEC_PER_FRAME, 1,
377 &tmr_screen_update, emu);
378
379 tilem_calc_emulator_unlock(emu);
380
381 if (emu->rom_file_name)
382 g_free(emu->rom_file_name);
383 emu->rom_file_name = rname;
384
385 if (emu->state_file_name)
386 g_free(emu->state_file_name);
387 emu->state_file_name = sname;
388
389 tilem_keybindings_init(emu, calc->hw.name);
390
391 if (emu->ewin)
392 tilem_emulator_window_calc_changed(emu->ewin);
393 if (emu->dbg)
394 tilem_debugger_calc_changed(emu->dbg);
395
396 if (emu->rcvdlg)
397 tilem_receive_dialog_free(emu->rcvdlg);
398 emu->rcvdlg = NULL;
399
400 return TRUE;
401 }
402
tilem_calc_emulator_revert_state(TilemCalcEmulator * emu,GError ** err)403 gboolean tilem_calc_emulator_revert_state(TilemCalcEmulator *emu, GError **err)
404 {
405 FILE *romfile, *savfile;
406 char *dname;
407 int errnum = 0;
408 gboolean status = TRUE;
409
410 g_return_val_if_fail(emu != NULL, FALSE);
411 g_return_val_if_fail(emu->calc != NULL, FALSE);
412 g_return_val_if_fail(emu->rom_file_name != NULL, FALSE);
413 g_return_val_if_fail(emu->state_file_name != NULL, FALSE);
414 g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
415
416 /* Open ROM file */
417
418 if (emu->calc->hw.flags & TILEM_CALC_HAS_FLASH) {
419 romfile = g_fopen(emu->rom_file_name, "rb");
420 if (!romfile) {
421 errnum = errno;
422 dname = g_filename_display_basename(emu->rom_file_name);
423 g_set_error(err, G_FILE_ERROR,
424 g_file_error_from_errno(errnum),
425 "Unable to open %s for reading: %s",
426 dname, g_strerror(errnum));
427 g_free(dname);
428 return FALSE;
429 }
430 }
431 else {
432 romfile = NULL;
433 }
434
435 /* Open state file */
436
437 savfile = g_fopen(emu->state_file_name, "rb");
438 if (!savfile) {
439 errnum = errno;
440 dname = g_filename_display_basename(emu->state_file_name);
441 g_set_error(err, G_FILE_ERROR,
442 g_file_error_from_errno(errnum),
443 "Unable to open %s for reading: %s",
444 dname, g_strerror(errnum));
445 g_free(dname);
446 if (romfile) fclose(romfile);
447 return FALSE;
448 }
449
450 /* Read state */
451
452 tilem_calc_emulator_lock(emu);
453
454 if (tilem_calc_load_state(emu->calc, romfile, savfile)) {
455 g_set_error(err, TILEM_EMULATOR_ERROR,
456 TILEM_EMULATOR_ERROR_INVALID_STATE,
457 "The specified ROM or state file is invalid.");
458 status = FALSE;
459 }
460
461 tilem_calc_emulator_unlock(emu);
462
463 if (emu->dbg)
464 tilem_debugger_refresh(emu->dbg, TRUE);
465
466 if (romfile) fclose(romfile);
467 fclose(savfile);
468 return status;
469 }
470
tilem_calc_emulator_save_state(TilemCalcEmulator * emu,GError ** err)471 gboolean tilem_calc_emulator_save_state(TilemCalcEmulator *emu, GError **err)
472 {
473 FILE *romfile, *savfile;
474 char *dname;
475 int errnum = 0;
476
477 g_return_val_if_fail(emu != NULL, FALSE);
478 g_return_val_if_fail(emu->calc != NULL, FALSE);
479 g_return_val_if_fail(emu->rom_file_name != NULL, FALSE);
480 g_return_val_if_fail(emu->state_file_name != NULL, FALSE);
481 g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
482
483 /* Open ROM file */
484
485 if (emu->calc->hw.flags & TILEM_CALC_HAS_FLASH) {
486 romfile = g_fopen(emu->rom_file_name, "r+b");
487 if (!romfile) {
488 errnum = errno;
489 dname = g_filename_display_basename(emu->rom_file_name);
490 g_set_error(err, G_FILE_ERROR,
491 g_file_error_from_errno(errnum),
492 "Unable to open %s for writing: %s",
493 dname, g_strerror(errnum));
494 g_free(dname);
495 return FALSE;
496 }
497 }
498 else {
499 romfile = NULL;
500 }
501
502 /* Open state file */
503
504 savfile = g_fopen(emu->state_file_name, "wb");
505 if (!savfile) {
506 errnum = errno;
507 dname = g_filename_display_basename(emu->state_file_name);
508 g_set_error(err, G_FILE_ERROR,
509 g_file_error_from_errno(errnum),
510 "Unable to open %s for writing: %s",
511 dname, g_strerror(errnum));
512 g_free(dname);
513 if (romfile) fclose(romfile);
514 return FALSE;
515 }
516
517 /* Write state */
518
519 tilem_calc_emulator_lock(emu);
520
521 if (romfile && tilem_calc_save_state(emu->calc, romfile, NULL))
522 errnum = errno;
523 if (romfile && fclose(romfile))
524 errnum = errno;
525
526 if (errnum) {
527 dname = g_filename_display_basename(emu->rom_file_name);
528 g_set_error(err, G_FILE_ERROR,
529 g_file_error_from_errno(errnum),
530 "Error writing %s: %s",
531 dname, g_strerror(errnum));
532 g_free(dname);
533 fclose(savfile);
534 tilem_calc_emulator_unlock(emu);
535 return FALSE;
536 }
537
538 if (tilem_calc_save_state(emu->calc, NULL, savfile))
539 errnum = errno;
540 if (fclose(savfile))
541 errnum = errno;
542
543 tilem_calc_emulator_unlock(emu);
544
545 if (errnum) {
546 dname = g_filename_display_basename(emu->state_file_name);
547 g_set_error(err, G_FILE_ERROR,
548 g_file_error_from_errno(errnum),
549 "Error writing %s: %s",
550 dname, g_strerror(errnum));
551 g_free(dname);
552 return FALSE;
553 }
554
555 return TRUE;
556 }
557
tilem_calc_emulator_reset(TilemCalcEmulator * emu)558 void tilem_calc_emulator_reset(TilemCalcEmulator *emu)
559 {
560 g_return_if_fail(emu != NULL);
561 g_return_if_fail(emu->calc != NULL);
562
563 tilem_calc_emulator_lock(emu);
564 tilem_calc_reset(emu->calc);
565 tilem_calc_emulator_unlock(emu);
566
567 if (emu->dbg)
568 tilem_debugger_refresh(emu->dbg, TRUE);
569 }
570
tilem_calc_emulator_pause(TilemCalcEmulator * emu)571 void tilem_calc_emulator_pause(TilemCalcEmulator *emu)
572 {
573 g_return_if_fail(emu != NULL);
574
575 tilem_calc_emulator_lock(emu);
576 emu->paused = TRUE;
577 tilem_calc_emulator_unlock(emu);
578 }
579
tilem_calc_emulator_run(TilemCalcEmulator * emu)580 void tilem_calc_emulator_run(TilemCalcEmulator *emu)
581 {
582 g_return_if_fail(emu != NULL);
583 g_return_if_fail(emu->calc != NULL);
584
585 tilem_calc_emulator_lock(emu);
586 emu->paused = FALSE;
587 tilem_calc_emulator_unlock(emu);
588
589 if (!emu->z80_thread)
590 emu->z80_thread = g_thread_create(&tilem_em_main, emu, TRUE, NULL);
591 }
592
tilem_calc_emulator_set_limit_speed(TilemCalcEmulator * emu,gboolean limit)593 void tilem_calc_emulator_set_limit_speed(TilemCalcEmulator *emu,
594 gboolean limit)
595 {
596 emu->limit_speed = limit;
597 }
598
tilem_calc_emulator_set_grayscale(TilemCalcEmulator * emu,gboolean grayscale)599 void tilem_calc_emulator_set_grayscale(TilemCalcEmulator *emu,
600 gboolean grayscale)
601 {
602 emu->grayscale = grayscale;
603
604 if (grayscale && emu->calc && !emu->glcd) {
605 tilem_calc_emulator_lock(emu);
606 emu->glcd = tilem_gray_lcd_new(emu->calc, GRAY_WINDOW_SIZE,
607 GRAY_SAMPLE_INT);
608 tilem_calc_emulator_unlock(emu);
609 }
610 else if (!grayscale && emu->glcd) {
611 tilem_calc_emulator_lock(emu);
612 tilem_gray_lcd_free(emu->glcd);
613 emu->glcd = NULL;
614 tilem_calc_emulator_unlock(emu);
615 }
616 }
617
618 /* If currently recording a macro, record a keypress */
record_key(TilemCalcEmulator * emu,int code)619 static void record_key(TilemCalcEmulator* emu, int code)
620 {
621 char* codechar;
622 int type = 0;
623
624 if (emu->isMacroRecording) {
625 codechar = g_strdup_printf("%04d", code);
626 tilem_macro_add_action(emu->macro, type, codechar);
627 g_free(codechar);
628 }
629 }
630
tilem_calc_emulator_press_key(TilemCalcEmulator * emu,int key)631 void tilem_calc_emulator_press_key(TilemCalcEmulator *emu, int key)
632 {
633 g_return_if_fail(emu != NULL);
634
635 if (key == 0)
636 return;
637
638 tilem_calc_emulator_lock(emu);
639 tilem_keypad_press_key(emu->calc, key);
640 tilem_calc_emulator_unlock(emu);
641
642 record_key(emu, key);
643
644 if (emu->dbg && emu->dbg->keypad_dialog)
645 tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog);
646 }
647
tilem_calc_emulator_release_key(TilemCalcEmulator * emu,int key)648 void tilem_calc_emulator_release_key(TilemCalcEmulator *emu, int key)
649 {
650 g_return_if_fail(emu != NULL);
651
652 if (key == 0)
653 return;
654
655 tilem_calc_emulator_lock(emu);
656 tilem_keypad_release_key(emu->calc, key);
657 tilem_calc_emulator_unlock(emu);
658
659 if (emu->dbg && emu->dbg->keypad_dialog)
660 tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog);
661 }
662
refresh_kpd(gpointer data)663 static gboolean refresh_kpd(gpointer data)
664 {
665 TilemCalcEmulator *emu = data;
666
667 if (emu->dbg && emu->dbg->keypad_dialog)
668 tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog);
669
670 return FALSE;
671 }
672
673 /* Timer callback for key sequences */
tmr_key_queue(TilemCalc * calc,void * data)674 static void tmr_key_queue(TilemCalc* calc, void* data)
675 {
676 TilemCalcEmulator *emu = data;
677 int nextkey;
678
679 if (emu->key_queue_pressed) {
680 if (emu->key_queue_len > 0 || !emu->key_queue_hold) {
681 tilem_keypad_release_key(calc, emu->key_queue_cur);
682 emu->key_queue_pressed = 0;
683 emu->key_queue_cur = 0;
684 tilem_z80_set_timer(calc, emu->key_queue_timer,
685 50000, 0, 1);
686 }
687 else {
688 tilem_z80_remove_timer(calc, emu->key_queue_timer);
689 emu->key_queue_timer = 0;
690 }
691 }
692 else {
693 if (emu->key_queue_len > 0) {
694 nextkey = emu->key_queue[--emu->key_queue_len];
695 tilem_keypad_press_key(calc, nextkey);
696 emu->key_queue_pressed = 1;
697 emu->key_queue_cur = nextkey;
698 tilem_z80_set_timer(calc, emu->key_queue_timer,
699 20000, 0, 1);
700 }
701 else {
702 tilem_z80_remove_timer(calc, emu->key_queue_timer);
703 emu->key_queue_timer = 0;
704 }
705 }
706
707 g_idle_add(&refresh_kpd, emu);
708 }
709
queue_keys(TilemCalcEmulator * emu,const byte * keys,int nkeys)710 static void queue_keys(TilemCalcEmulator *emu, const byte *keys, int nkeys)
711 {
712 byte *q;
713 int i;
714
715 q = g_new(byte, emu->key_queue_len + nkeys);
716
717 for (i = 0; i < nkeys; i++) {
718 q[nkeys - i - 1] = keys[i];
719 record_key(emu, keys[i]);
720 }
721
722 if (emu->key_queue_len)
723 memcpy(q + nkeys, emu->key_queue, emu->key_queue_len);
724
725 g_free(emu->key_queue);
726 emu->key_queue = q;
727 emu->key_queue_len += nkeys;
728 emu->key_queue_hold = 1;
729
730 if (!emu->key_queue_timer) {
731 emu->key_queue_timer
732 = tilem_z80_add_timer(emu->calc, 1, 0, 1,
733 &tmr_key_queue, emu);
734 }
735 }
736
tilem_calc_emulator_queue_keys(TilemCalcEmulator * emu,const byte * keys,int nkeys)737 void tilem_calc_emulator_queue_keys(TilemCalcEmulator *emu,
738 const byte *keys, int nkeys)
739 {
740 g_return_if_fail(emu != NULL);
741 g_return_if_fail(keys != NULL);
742 g_return_if_fail(nkeys > 0);
743
744 tilem_calc_emulator_lock(emu);
745 queue_keys(emu, keys, nkeys);
746 tilem_calc_emulator_unlock(emu);
747 }
748
tilem_calc_emulator_release_queued_key(TilemCalcEmulator * emu)749 void tilem_calc_emulator_release_queued_key(TilemCalcEmulator *emu)
750 {
751 g_return_if_fail(emu != NULL);
752
753 tilem_calc_emulator_lock(emu);
754 if (emu->key_queue_timer) {
755 emu->key_queue_hold = 0;
756 }
757 else if (emu->key_queue_pressed) {
758 tilem_keypad_release_key(emu->calc, emu->key_queue_cur);
759 emu->key_queue_pressed = 0;
760 emu->key_queue_cur = 0;
761 }
762 tilem_calc_emulator_unlock(emu);
763
764 if (emu->dbg && emu->dbg->keypad_dialog)
765 tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog);
766 }
767
tilem_calc_emulator_press_or_queue(TilemCalcEmulator * emu,int key)768 gboolean tilem_calc_emulator_press_or_queue(TilemCalcEmulator *emu,
769 int key)
770 {
771 byte b;
772 gboolean status;
773
774 g_return_val_if_fail(emu != NULL, FALSE);
775
776 tilem_calc_emulator_lock(emu);
777
778 if (emu->key_queue_timer) {
779 b = key;
780 queue_keys(emu, &b, 1);
781 status = TRUE;
782 }
783 else {
784 tilem_keypad_press_key(emu->calc, key);
785 status = FALSE;
786 }
787 tilem_calc_emulator_unlock(emu);
788
789 if (emu->dbg && emu->dbg->keypad_dialog)
790 tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog);
791
792 return status;
793 }
794
tilem_calc_emulator_get_screenshot(TilemCalcEmulator * emu,gboolean grayscale)795 TilemAnimation * tilem_calc_emulator_get_screenshot(TilemCalcEmulator *emu,
796 gboolean grayscale)
797 {
798 TilemAnimation *anim;
799
800 g_return_val_if_fail(emu != NULL, NULL);
801 g_return_val_if_fail(emu->calc != NULL, NULL);
802
803 anim = tilem_animation_new(emu->calc->hw.lcdwidth,
804 emu->calc->hw.lcdheight);
805
806 tilem_calc_emulator_lock(emu);
807
808 if (grayscale && emu->glcd)
809 tilem_gray_lcd_get_frame(emu->glcd, emu->tmp_lcd_buffer);
810 else
811 tilem_lcd_get_frame(emu->calc, emu->tmp_lcd_buffer);
812
813 tilem_animation_append_frame(anim, emu->tmp_lcd_buffer, 1);
814
815 tilem_calc_emulator_unlock(emu);
816
817 return anim;
818 }
819
tilem_calc_emulator_begin_animation(TilemCalcEmulator * emu,gboolean grayscale)820 void tilem_calc_emulator_begin_animation(TilemCalcEmulator *emu,
821 gboolean grayscale)
822 {
823 g_return_if_fail(emu != NULL);
824 g_return_if_fail(emu->calc != NULL);
825
826 tilem_calc_emulator_lock(emu);
827 cancel_animation(emu);
828 emu->anim = tilem_animation_new(emu->calc->hw.lcdwidth,
829 emu->calc->hw.lcdheight);
830 emu->anim_grayscale = grayscale;
831 tilem_calc_emulator_unlock(emu);
832 }
833
tilem_calc_emulator_end_animation(TilemCalcEmulator * emu)834 TilemAnimation * tilem_calc_emulator_end_animation(TilemCalcEmulator *emu)
835 {
836 TilemAnimation *anim;
837
838 g_return_val_if_fail(emu != NULL, NULL);
839 g_return_val_if_fail(emu->anim != NULL, NULL);
840
841 tilem_calc_emulator_lock(emu);
842 anim = emu->anim;
843 emu->anim = NULL;
844 tilem_calc_emulator_unlock(emu);
845
846 return anim;
847 }
848
849 /* Prompt for a ROM file to open */
tilem_calc_emulator_prompt_open_rom(TilemCalcEmulator * emu)850 int tilem_calc_emulator_prompt_open_rom(TilemCalcEmulator *emu)
851 {
852 char *dir, *filename;
853 GError *err = NULL;
854 const char *modelname;
855
856 if (emu->rom_file_name)
857 dir = g_path_get_dirname(emu->rom_file_name);
858 else
859 dir = g_get_current_dir();
860
861 filename = prompt_open_file("Open Calculator", GTK_WINDOW(get_toplevel(emu)),
862 dir, "ROM files", "*.rom;*.clc;*.bin",
863 "All files", "*", NULL);
864 g_free(dir);
865 if (!filename)
866 return 0;
867
868 if (tilem_calc_emulator_load_state(emu, filename, NULL,
869 0, &err)) {
870 modelname = emu->calc->hw.name;
871 tilem_config_set(modelname,
872 "rom_file/f", emu->rom_file_name,
873 "state_file/f", emu->state_file_name,
874 NULL);
875 tilem_config_set("recent", "last_model/s", modelname, NULL);
876 }
877 g_free(filename);
878
879 if (err) {
880 messagebox01(GTK_WINDOW(get_toplevel(emu)), GTK_MESSAGE_ERROR,
881 "Unable to load calculator state",
882 "%s", err->message);
883 g_error_free(err);
884 return -1;
885 }
886 else {
887 return 1;
888 }
889 }
890
891 /* Run slowly to play macro (used instead run_with_key() function) */
run_with_key_slowly(TilemCalc * calc,int key)892 void run_with_key_slowly(TilemCalc* calc, int key)
893 {
894 tilem_z80_run_time(calc, 5000000, NULL); /* Wait */
895 tilem_keypad_press_key(calc, key); /* Press */
896 tilem_z80_run_time(calc, 10000, NULL); /* Wait (don't forget to wait) */
897 tilem_keypad_release_key(calc, key); /* Release */
898 tilem_z80_run_time(calc, 50, NULL); /* Wait */
899 }
900