diff --git a/.gitignore b/.gitignore index 70d0b71..c8bf649 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ main.8xk main.bin ti83pv116.sav + +tool/rabbitsign diff --git a/Makefile b/Makefile index ae18967..4b54f14 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,19 @@ -CC=sdcc -CFLAGS=-Ilib83 -c -mz80 --std-sdcc2x --no-std-crt0 --reserve-regs-iy --opt-code-size +SDCC=sdcc +SDCCFLAGS=-Ilib83 -c -mz80 --std-sdcc2x --no-std-crt0 --reserve-regs-iy --opt-code-size OBJS=_crt0.rel clrscr.rel putchar.rel puts.rel exit.rel gotoxy.rel __assert_fail.rel \ getchar.rel put_int.rel ctype.rel memcpy.rel memset.rel memmove.rel memcmp.rel \ strcpy.rel strlen.rel strncpy.rel \ main.rel -.PHONY: all clean try +.PHONY: all clean try init all: main.8xk +init: + @echo "Compiling rabbitsign..." + @$(CC) tool/rabbitsign-src/*.c -o tool/rabbitsign -O2 -g -w -DPROTOTYPES + clean: @rm -f obj/* main.ihx main.bin main.8xk @@ -17,16 +21,16 @@ try: main.8xk @tilem2 --rom=ti83pv116.bin %.rel: lib83/%.c - @echo "(lib) CC $<" - @$(CC) $(CFLAGS) $< -o obj/$@ + @echo "(lib) SDCC $<" + @$(SDCC) $(SDCCFLAGS) $< -o obj/$@ %.rel: %.c - @echo "CC $<" - @$(CC) $(CFLAGS) $< -o obj/$@ + @echo "SDCC $<" + @$(SDCC) $(SDCCFLAGS) $< -o obj/$@ obj/main.ihx: $(OBJS) - @echo "LD $@" - @cd obj && $(CC) -mz80 --no-std-crt0 --code-loc 0x4000 --code-size 0x4000 --xram-loc 0x9D95 --xram-size 0x6060 $^ -o ../$@ && cd .. + @echo "SDCCLD $@" + @cd obj && $(SDCC) -mz80 --no-std-crt0 --code-loc 0x4000 --code-size 0x4000 --xram-loc 0x9D95 --xram-size 0x6060 $^ -o ../$@ && cd .. main.bin: obj/main.ihx @echo "IHX->BIN $@" @@ -35,5 +39,5 @@ main.bin: obj/main.ihx main.8xk: main.bin @echo "SIGN $@" - @rabbitsign -P -p -t 8xk -g $< + @tool/rabbitsign -P -p -t 8xk -g $< diff --git a/README.md b/README.md index 6a07a85..9a53d1e 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,25 @@ A C programming SDK for the TI-83 calculator. Includes pre-configured build syst ## Requirements -- SDCC -- GNU Make -- TilEm -- RabbitSign +- A GNU/Linux system. ## Status WIP. + +## Usage + +Initialise the build environment: + +```bash +git clone https://git.palaiologos.rocks/Palaiologos/ti83-sdk +cd ti83-sdk +make init +make +``` + +Run the pre-supplied program: + +```bash +make try +``` diff --git a/tool/rabbitsign-src/app8x.c b/tool/rabbitsign-src/app8x.c new file mode 100644 index 0000000..d114178 --- /dev/null +++ b/tool/rabbitsign-src/app8x.c @@ -0,0 +1,445 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "rabbitsign.h" +#include "internal.h" +#include "md5.h" + +/* + * Check/fix Flash app header and data. + * + * This function checks various parts of the application header which, + * if incorrect, are known to cause applications to be rejected by the + * calculator. Depending on the flags, this function will also fix + * incorrect header fields. + * + * Note that this function can also add padding to the end of the app. + * The entire signature must be stored on one page, so if there is not + * enough room on the final page of the app, an extra page needs to be + * added to hold the signature. + * + * In addition, some versions of the boot code have a bug which + * results in incorrect MD5 hashes for applications that are 55 bytes + * long mod 64; this function will add an extra padding byte to avoid + * that case. + */ +int rs_repair_ti8x_app(RSProgram* app, /* app to repair */ + unsigned int flags) /* flags */ +{ + unsigned long length, hdrstart, hdrsize, fieldstart, fieldsize, i; + unsigned char* hdr; + unsigned char dummy = 0; + int e, pagecount, addedpage = 0; + + /* Various parts of the OS, as well as other software on the + calculator and PC, expect that every application begins with the + bytes 80 0F -- a "long" field. Some things may work for apps + with an 80 0E (or even 80 0D) field, but not everything. Please + stick with 80 0F. */ + + if (app->length < 6 + || app->data[0] != 0x80 + || app->data[1] != 0x0f) { + rs_error(NULL, app, "no app header found"); + return RS_ERR_MISSING_HEADER; + } + + /* Determine application length */ + + length = app->length; + rs_get_field_size(app->data, &hdrstart, &hdrsize); + + /* If requested, remove the old signature (truncate the application + to its stated length.) */ + + if (flags & RS_REMOVE_OLD_SIGNATURE) { + if (length < hdrstart + hdrsize) { + rs_warning(NULL, app, "provided app data too short"); + } + else { + if (length > hdrstart + hdrsize + 96) + rs_warning(NULL, app, "re-signing discards %lu bytes", + length - hdrstart - hdrsize); + length = hdrstart + hdrsize; + } + } + else if (hdrsize && hdrstart + hdrsize != length) { + rs_warning(NULL, app, "application length incorrect"); + rs_warning(NULL, app, "(perhaps you meant to use -r?)"); + } + + /* If necessary, add an extra page to ensure that the signature + doesn't span a page boundary. */ + + if (((length + 69 + 0x3fff) >> 14) != ((length + 0x3fff) >> 14)) { + if (flags & (RS_ZEALOUSLY_PAD_APP | RS_IGNORE_ALL_WARNINGS)) { + rs_warning(NULL, app, "adding an extra page to hold app signature"); + length = ((length + 0x4000) & ~0x3fff) + 1; + addedpage = 1; + } + else { + rs_error(NULL, app, "application ends too close to a page boundary"); + return RS_ERR_FINAL_PAGE_TOO_LONG; + } + } + + if ((e = rs_program_set_length(app, length))) + return e; + + /* If the length is 55 mod 64, add an extra byte. (Note that, with + 512-bit keys, this can never cause a page overflow.) We use zero + for the padding value, rather than FF, so that our output matches + that of other tools. */ + + if ((length % 64) == 55) { + length++; + rs_message(2, NULL, app, "adding an extra byte due to boot code bugs"); + if ((e = rs_program_append_data(app, &dummy, 1))) + return e; + } + + /* Set app size header to the correct value */ + + hdrsize = length - hdrstart; + if (rs_set_field_size(app->data, hdrsize)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application length header too small"); + else { + rs_error(NULL, app, "application length header too small"); + return RS_ERR_FIELD_TOO_SMALL; + } + } + + /* Check/fix page count. This field is required to be present and + contain the correct number of pages. It must be one byte long + (some parts of the OS don't even check the length and assume it + is one byte long.) */ + + hdr = app->data + hdrstart; + if (hdrsize > 128) + hdrsize = 128; + + if (rs_find_app_field(0x8080, hdr, hdrsize, + NULL, &fieldstart, &fieldsize)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no page count field"); + else { + rs_error(NULL, app, "application has no page count field"); + return RS_ERR_MISSING_PAGE_COUNT; + } + } + else if (fieldsize != 1) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has an invalid page count field"); + else { + rs_error(NULL, app, "application has an invalid page count field"); + return RS_ERR_INCORRECT_PAGE_COUNT; + } + } + else { + pagecount = ((length + 0x3fff) >> 14); + + if (flags & RS_FIX_PAGE_COUNT) { + hdr[fieldstart] = pagecount; + } + else if (addedpage && hdr[fieldstart] == pagecount - 1) { + hdr[fieldstart] = pagecount; + } + else if (hdr[fieldstart] != pagecount) { + if (flags & RS_IGNORE_ALL_WARNINGS) { + rs_warning(NULL, app, + "application has an incorrect page count (actual: %lu)", + ((length + 0x3fff) >> 14)); + hdr[fieldstart] = pagecount; + } + else { + rs_error(NULL, app, + "application has an incorrect page count (actual: %lu)", + ((length + 0x3fff) >> 14)); + return RS_ERR_INCORRECT_PAGE_COUNT; + } + } + } + + /* Check for key ID. This field is required to be present; it + determines which public key is used for validation. (The + contents of this field are usually thought of as a big-endian + integer, but to be more precise, they're really treated as a + binary string.) */ + + if (rs_find_app_field(0x8010, hdr, hdrsize, NULL, NULL, NULL)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no key ID"); + else { + rs_error(NULL, app, "application has no key ID"); + return RS_ERR_MISSING_KEY_ID; + } + } + + /* Check for date stamp. This seems to be required -- the OS will + use it to update its last-known date stamp if necessary -- and + should consist of an 032x field containing an 090x field, + followed by an 020x field containing the date stamp signature. + (The contents of the latter only matter if the date stamp is + "new.") */ + + if (rs_find_app_field(0x0320, hdr, hdrsize, + NULL, &fieldstart, &fieldsize)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no date stamp"); + else { + rs_error(NULL, app, "application has no date stamp"); + return RS_ERR_MISSING_DATE_STAMP; + } + } + else if (rs_find_app_field(0x0900, hdr + fieldstart, fieldsize, + NULL, NULL, NULL)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no date stamp"); + else { + rs_error(NULL, app, "application has no date stamp"); + return RS_ERR_MISSING_DATE_STAMP; + } + } + else if (hdr[fieldstart + fieldsize] != 0x02 + || (hdr[fieldstart + fieldsize + 1] & 0xf0) != 0) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no date stamp signature"); + else { + rs_error(NULL, app, "application has no date stamp signature"); + return RS_ERR_MISSING_DATE_STAMP; + } + } + + /* Check for program image field. This field indicates the end of + the header and the start of application code. Note, however, + that the OS handles this field in an exceedingly broken way. To + be safe, this must always be the last field of the header, and + should always be written as 80 7F followed by four length bytes. + The length bytes may be anything you like -- they're ignored. */ + + if (rs_find_app_field(0x8070, hdr, hdrsize, + NULL, NULL, NULL)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no program image field"); + else { + rs_error(NULL, app, "application has no program image field"); + return RS_ERR_MISSING_PROGRAM_IMAGE; + } + } + + /* Check for invalid pages (those beginning with FF.) An OS bug + means that such pages will end up being erased completely if + defragmenting requires the application to be moved in Flash. */ + + e = RS_SUCCESS; + + for (i = 0; i < app->length; i += 0x4000) { + if (app->data[i] == 0xff) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "page %ld begins with FFh", (i >> 14)); + else { + rs_error(NULL, app, "page %ld begins with FFh", (i >> 14)); + e = RS_ERR_INVALID_PROGRAM_DATA; + } + } + } + + return e; +} + +/* + * Compute signature for a Flash app. + * + * The app header should be checked and/or repaired by + * rs_repair_ti8x_app() prior to calling this function. + * + * There are four equally valid Rabin signatures for any application; + * rootnum determines which of the four should be used. + */ +int rs_sign_ti8x_app(RSProgram* app, /* app to sign */ + RSKey* key, /* signing key */ + int rootnum) /* signature number */ +{ + md5_uint32 hash[4]; + mpz_t hashv, sigv; + int f; + unsigned int lastpagelength; + unsigned char sigdata[512]; + size_t siglength; + int e; + + /* Check if app length is risky */ + + if ((app->length % 64) == 55) { + rs_warning(NULL, app, "application has length 55 mod 64"); + rs_warning(NULL, app, "(this will fail to validate on TI-83+ BE)"); + } + + /* Compute signature */ + + md5_buffer((char*) app->data, app->length, hash); + + mpz_init(hashv); + mpz_init(sigv); + + mpz_import(hashv, 16, -1, 1, 0, 0, hash); + rs_message(2, NULL, app, "hash = %ZX", hashv); + + if ((e = rs_sign_rabin(sigv, &f, hashv, rootnum, key))) { + mpz_clear(hashv); + mpz_clear(sigv); + return e; + } + + rs_message(2, NULL, app, "sig = %ZX", sigv); + rs_message(2, NULL, app, "f = %d", f); + + /* Write the square root value as an 022D field... */ + + sigdata[0] = 0x02; + sigdata[1] = 0x2d; + mpz_export(sigdata + 3, &siglength, -1, 1, 0, 0, sigv); + sigdata[2] = siglength & 0xff; + siglength += 3; + + mpz_clear(hashv); + mpz_clear(sigv); + + /* ...and append the f value as a big integer */ + + if (f == 0) { + sigdata[siglength++] = 0; + } + else { + sigdata[siglength++] = 1; + sigdata[siglength++] = f; + } + + /* Add padding, but not too much (it seems to make some link + programs happier) */ + + lastpagelength = app->length & 0x3fff; + + while (siglength < 96 && (lastpagelength + siglength) < 0x3fff) + sigdata[siglength++] = 0xff; + + return rs_program_append_data(app, sigdata, siglength); +} + +/* + * Validate a Flash app signature. + */ +int rs_validate_ti8x_app(const RSProgram* app, /* app to validate */ + const RSKey* key) /* signing key */ +{ + unsigned long length, hdrstart, hdrsize, fieldstart, fieldsize, i; + const unsigned char *hdr, *sig; + md5_uint32 hash[4]; + mpz_t hashv, sigv; + int f, e, e2 = RS_SUCCESS; + + if (app->length < 6) { + rs_error(NULL, app, "no app header found"); + return RS_ERR_MISSING_HEADER; + } + + rs_get_field_size(app->data, &hdrstart, &hdrsize); + length = hdrstart + hdrsize; + hdr = app->data + hdrstart; + if (hdrsize > 128) + hdrsize = 128; + + if (((length + 0x3fff) >> 14) != ((app->length + 0x3fff) >> 14) + || length + 4 > app->length || length + 96 < app->length) { + rs_error(NULL, app, "incorrect application length"); + return RS_ERR_INCORRECT_PROGRAM_SIZE; + } + + if (rs_find_app_field(0x8070, hdr, hdrsize, + NULL, NULL, NULL)) { + rs_warning(NULL, app, "application has no program image field"); + e2 = RS_ERR_MISSING_PROGRAM_IMAGE; + } + + if (rs_find_app_field(0x8080, hdr, hdrsize, + NULL, &fieldstart, &fieldsize)) { + rs_warning(NULL, app, "application has no no page count field"); + e2 = RS_ERR_MISSING_PAGE_COUNT; + } + else if (fieldsize != 1) { + rs_warning(NULL, app, "application has an invalid page count field"); + e2 = RS_ERR_INCORRECT_PAGE_COUNT; + } + else if (hdr[fieldstart] != ((length + 0x3fff) >> 14)) { + rs_warning(NULL, app, "application has an incorrect page count field"); + e2 = RS_ERR_INCORRECT_PAGE_COUNT; + } + + if ((length % 64) == 55) { + rs_warning(NULL, app, "application has length 55 mod 64"); + rs_warning(NULL, app, "(this will fail to validate on TI-83+ BE)"); + e2 = RS_ERR_INVALID_PROGRAM_SIZE; + } + + for (i = 0; i < app->length; i += 0x4000) { + if (app->data[i] == 0xff) { + rs_warning(NULL, app, "page %ld begins with FFh", (i >> 14)); + e2 = RS_ERR_INVALID_PROGRAM_DATA; + } + } + + md5_buffer((char*) app->data, length, &hash); + + sig = app->data + length; + if (sig[0] != 0x02 || sig[1] != 0x2d) { + rs_error(NULL, app, "application does not have a Rabin signature"); + return RS_ERR_MISSING_RABIN_SIGNATURE; + } + rs_get_field_size(sig, &fieldstart, &fieldsize); + + mpz_init(sigv); + mpz_init(hashv); + + mpz_import(hashv, 16, -1, 1, 0, 0, hash); + rs_message(2, NULL, app, "hash = %ZX", hashv); + + mpz_import(sigv, fieldsize, -1, 1, 0, 0, sig + fieldstart); + rs_message(2, NULL, app, "sig = %ZX", sigv); + + if (sig[fieldstart + fieldsize] == 0) + f = 0; + else + f = sig[fieldstart + fieldsize + 1]; + rs_message(2, NULL, app, "f = %d", f); + + e = rs_validate_rabin(sigv, f, hashv, key); + if (e == RS_SIGNATURE_INCORRECT) + rs_message(0, NULL, app, "application signature incorrect"); + + mpz_clear(sigv); + mpz_clear(hashv); + return (e ? e : e2); +} + diff --git a/tool/rabbitsign-src/app9x.c b/tool/rabbitsign-src/app9x.c new file mode 100644 index 0000000..9d0dda9 --- /dev/null +++ b/tool/rabbitsign-src/app9x.c @@ -0,0 +1,297 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "rabbitsign.h" +#include "internal.h" +#include "md5.h" + +/* + * Check/fix app/OS header and data. + * + * (This is something of a work in progress; a lot more + * experimentation would be useful to determine what exactly is + * required of app and OS headers on the 68k calculators.) + */ +static int repair_app(RSProgram* app, /* app to repair */ + unsigned int flags, /* flags */ + unsigned int type) /* field type */ +{ + unsigned long length, hdrstart, hdrsize, fieldhead, + fieldstart, fieldsize; + unsigned char *hdr; + int e; + + if (app->length < 6 + || app->data[0] != type + || (app->data[1] & 0xf0) != 0) { + rs_error(NULL, app, "no app header found"); + return RS_ERR_MISSING_HEADER; + } + + /* Determine application length */ + + length = app->length; + rs_get_field_size(app->data, &hdrstart, &hdrsize); + + /* If requested, remove the old signature (truncate the application + to its stated length.) */ + + if (flags & RS_REMOVE_OLD_SIGNATURE) { + if (length < hdrstart + hdrsize) { + rs_warning(NULL, app, "provided app data too short"); + } + else { + if (length > hdrstart + hdrsize + 67) + rs_warning(NULL, app, "re-signing discards %lu bytes", + length - hdrstart - hdrsize); + length = hdrstart + hdrsize; + } + } + else if (hdrsize && hdrstart + hdrsize != length) { + rs_warning(NULL, app, "application length incorrect"); + rs_warning(NULL, app, "(perhaps you meant to use -r?)"); + } + + if ((e = rs_program_set_length(app, length))) + return e; + + /* Set app size header to the correct value */ + + hdrsize = length - hdrstart; + if (rs_set_field_size(app->data, hdrsize)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "cannot set application length"); + else { + rs_error(NULL, app, "cannot set application length"); + return RS_ERR_FIELD_TOO_SMALL; + } + } + + /* Check for key ID */ + + hdr = app->data + hdrstart; + if (hdrsize > 128) + hdrsize = 128; + + if (rs_find_app_field((type << 8) | 0x10, hdr, hdrsize, + NULL, NULL, NULL)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no key ID"); + else { + rs_error(NULL, app, "application has no key ID"); + return RS_ERR_MISSING_KEY_ID; + } + } + + /* Check for date stamp (note: I haven't actually tested whether + this is required, but it always seems to be present in both 68k + apps and OSes, and it is required for TI-83+ apps) */ + + if (rs_find_app_field(0x0320, hdr, hdrsize, + NULL, &fieldstart, &fieldsize)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no date stamp"); + else { + rs_error(NULL, app, "application has no date stamp"); + return RS_ERR_MISSING_DATE_STAMP; + } + } + else if (rs_find_app_field(0x0900, hdr + fieldstart, fieldsize, + NULL, NULL, NULL)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no date stamp"); + else { + rs_error(NULL, app, "application has no date stamp"); + return RS_ERR_MISSING_DATE_STAMP; + } + } + else if (hdr[fieldstart + fieldsize] != 0x02 + || (hdr[fieldstart + fieldsize + 1] & 0xf0) != 0) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no date stamp signature"); + else { + rs_error(NULL, app, "application has no date stamp signature"); + return RS_ERR_MISSING_DATE_STAMP; + } + } + + /* Check for program image field and fix length */ + + if (rs_find_app_field((type << 8) | 0x70, hdr, hdrsize, + &fieldhead, &fieldstart, &fieldsize)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application has no program image field"); + else { + rs_error(NULL, app, "application has no program image field"); + return RS_ERR_MISSING_PROGRAM_IMAGE; + } + } + else { + if ((fieldstart + hdrstart) % 2) { + /* The OS appears to align apps so the start of the app header + is at an even address; if the application code itself is at + an odd address, bad stuff will happen. */ + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, app, "application header is not a multiple of 2 bytes"); + else { + rs_error(NULL, app, "application header is not a multiple of 2 bytes"); + return RS_ERR_MISALIGNED_PROGRAM_IMAGE; + } + } + + if (fieldsize && fieldstart + fieldsize != length - hdrstart) + rs_warning(NULL, app, "program image length incorrect"); + + if (rs_set_field_size(hdr + fieldhead, length - hdrstart - fieldstart)) { + rs_error(NULL, app, "cannot set program image length"); + return RS_ERR_FIELD_TOO_SMALL; + } + } + + return RS_SUCCESS; +} + +/* + * Check/fix Flash app header and data. + */ +int rs_repair_ti9x_app(RSProgram* app, /* app to repair */ + unsigned int flags) /* flags */ +{ + return repair_app(app, flags, 0x81); +} + +/* + * Check/fix OS header and data. + */ +int rs_repair_ti9x_os(RSProgram* app, /* app to repair */ + unsigned int flags) /* flags */ +{ + return repair_app(app, flags, 0x80); +} + +/* + * Compute signature for a 68k app/OS. + * + * The app header should be checked and/or repaired by + * rs_repair_ti9x_app() prior to calling this function. + */ +int rs_sign_ti9x_app(RSProgram* app, /* app to sign */ + RSKey* key) /* signing key */ +{ + md5_uint32 hash[4]; + mpz_t hashv, sigv; + unsigned char sigdata[512]; + size_t siglength; + int e; + + md5_buffer((char*) app->data, app->length, &hash); + + mpz_init(hashv); + mpz_init(sigv); + + mpz_import(hashv, 16, -1, 1, 0, 0, hash); + rs_message(2, NULL, app, "hash = %ZX", hashv); + + if ((e = rs_sign_rsa(sigv, hashv, key))) { + mpz_clear(hashv); + mpz_clear(sigv); + return e; + } + + rs_message(2, NULL, app, "sig = %ZX", sigv); + + sigdata[0] = 0x02; + sigdata[1] = 0x0d; + mpz_export(sigdata + 3, &siglength, -1, 1, 0, 0, sigv); + sigdata[2] = siglength & 0xff; + siglength += 3; + + return rs_program_append_data(app, sigdata, siglength); +} + +/* + * Validate app/OS signature. + */ +int rs_validate_ti9x_app(const RSProgram* app, /* app to validate */ + const RSKey* key) /* signing key */ +{ + unsigned long length, hdrstart, hdrsize, fieldstart, fieldsize; + const unsigned char *hdr, *sig; + md5_uint32 hash[4]; + mpz_t hashv, sigv; + int e, e2 = RS_SUCCESS; + + if (app->length < 6) { + rs_error(NULL, app, "no app header found"); + return RS_ERR_MISSING_HEADER; + } + + rs_get_field_size(app->data, &hdrstart, &hdrsize); + length = hdrstart + hdrsize; + hdr = app->data + hdrstart; + if (hdrsize > 128) + hdrsize = 128; + + if (length + 4 > app->length || length + 67 < app->length) { + rs_error(NULL, app, "incorrect application length"); + return RS_ERR_INCORRECT_PROGRAM_SIZE; + } + + if (rs_find_app_field((app->data[0] << 8) | 0x70, hdr, hdrsize, + NULL, &fieldstart, &fieldsize)) { + rs_warning(NULL, app, "application has no program image field"); + e2 = RS_ERR_MISSING_PROGRAM_IMAGE; + } + else if ((fieldstart + hdrstart) % 2) { + rs_warning(NULL, app, "application header is not a multiple of 2 bytes"); + e2 = RS_ERR_MISALIGNED_PROGRAM_IMAGE; + } + + md5_buffer((char*) app->data, length, &hash); + + sig = app->data + length; + if (sig[0] != 0x02 || (sig[1] & 0xf0) != 0x00) { + rs_error(NULL, app, "application does not have an RSA signature"); + return RS_ERR_MISSING_RSA_SIGNATURE; + } + rs_get_field_size(sig, &fieldstart, &fieldsize); + + mpz_init(sigv); + mpz_init(hashv); + + mpz_import(hashv, 16, -1, 1, 0, 0, hash); + rs_message(2, NULL, app, "hash = %ZX", hashv); + + mpz_import(sigv, fieldsize, -1, 1, 0, 0, sig + fieldstart); + rs_message(2, NULL, app, "sig = %ZX", sigv); + + e = rs_validate_rsa(sigv, hashv, key); + if (e == RS_SIGNATURE_INCORRECT) + rs_message(0, NULL, app, "application signature incorrect"); + + mpz_clear(sigv); + mpz_clear(hashv); + return (e ? e : e2); +} + diff --git a/tool/rabbitsign-src/apps.c b/tool/rabbitsign-src/apps.c new file mode 100644 index 0000000..896674d --- /dev/null +++ b/tool/rabbitsign-src/apps.c @@ -0,0 +1,85 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "rabbitsign.h" +#include "internal.h" + +/* + * Check/fix program header and data. + */ +int rs_repair_program(RSProgram* prgm, /* app to repair */ + unsigned int flags) /* flags */ +{ + if (rs_calc_is_ti8x(prgm->calctype)) { + if (prgm->datatype == RS_DATA_OS) + return rs_repair_ti8x_os(prgm, flags); + else if (prgm->datatype == RS_DATA_APP) + return rs_repair_ti8x_app(prgm, flags); + } + + if (rs_calc_is_ti9x(prgm->calctype)) { + if (prgm->datatype == RS_DATA_OS) + return rs_repair_ti9x_os(prgm, flags); + else if (prgm->datatype == RS_DATA_APP) + return rs_repair_ti9x_app(prgm, flags); + } + + rs_error(NULL, prgm, "calc/data type (%X/%X) unrecognized", + prgm->calctype, prgm->datatype); + return RS_ERR_UNKNOWN_PROGRAM_TYPE; +} + +/* + * Add a signature to the program. + */ +int rs_sign_program(RSProgram* prgm, /* app to sign */ + RSKey* key, /* signing key */ + int rootnum) /* signature number */ +{ + if (rs_calc_is_ti8x(prgm->calctype)) { + if (prgm->datatype == RS_DATA_OS) + return rs_sign_ti8x_os(prgm, key); + else if (prgm->datatype == RS_DATA_APP) + return rs_sign_ti8x_app(prgm, key, rootnum); + } + + return rs_sign_ti9x_app(prgm, key); +} + +/* + * Validate program signature. + */ +int rs_validate_program(const RSProgram* prgm, /* app to validate */ + const RSKey* key) /* signing key */ +{ + if (rs_calc_is_ti8x(prgm->calctype)) { + if (prgm->datatype == RS_DATA_OS) + return rs_validate_ti8x_os(prgm, key); + else if (prgm->datatype == RS_DATA_APP) + return rs_validate_ti8x_app(prgm, key); + } + + return rs_validate_ti9x_app(prgm, key); +} + diff --git a/tool/rabbitsign-src/autokey.c b/tool/rabbitsign-src/autokey.c new file mode 100644 index 0000000..23b4a5f --- /dev/null +++ b/tool/rabbitsign-src/autokey.c @@ -0,0 +1,241 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" +#include "autokeys.h" + +/* + * Get key ID for the given program. + */ +unsigned long rs_program_get_key_id(const RSProgram* prgm) +{ + const unsigned char* hdr; + unsigned long hdrstart, hdrsize; + + if (prgm->header_length > 0) { + hdr = prgm->header; + hdrsize = prgm->header_length; + } + else if (prgm->length > 0) { + hdr = prgm->data; + hdrsize = prgm->length; + if (hdrsize > 128) + hdrsize = 128; + } + else + return 0; + + rs_get_field_size(hdr, &hdrstart, NULL); + hdrsize -= hdrstart; + + if (hdr[0] == 0x81) + return rs_get_numeric_field(0x8110, hdr + hdrstart, hdrsize); + else + return rs_get_numeric_field(0x8010, hdr + hdrstart, hdrsize); +} + +/* + * Try to load key from a file. + */ +static int try_key_file(RSKey* key, /* key structure to store result */ + const char* a, /* first path element */ + const char* b, /* second path element */ + const char* c) /* third path element */ +{ + char* s; + FILE* f; + int e; + + s = rs_malloc(strlen(a) + strlen(b) + strlen(c) + 1); + if (!s) + return RS_ERR_OUT_OF_MEMORY; + strcpy(s, a); + strcat(s, b); + strcat(s, c); + + f = fopen(s, "rt"); + if (!f) { + rs_free(s); + return RS_ERR_KEY_NOT_FOUND; + } + + if ((e = rs_read_key_file(key, f, s, 1))) { + fclose(f); + rs_free(s); + return e; + } + + fclose(f); + rs_free(s); + return RS_SUCCESS; +} + +/* + * Try to locate a given key file. + */ +static int find_key_file(RSKey* key, /* key structure to + store result */ + const char* filename) /* file name to search + for */ +{ + const char* p; + int e; + + e = try_key_file(key, "", "", filename); + if (e != RS_ERR_KEY_NOT_FOUND) + return e; + + if ((p = getenv("RABBITSIGN_KEY_DIR"))) { +#if defined(__MSDOS__) || defined(__WIN32__) + e = try_key_file(key, p, "\\", filename); +#else + e = try_key_file(key, p, "/", filename); +#endif + if (e != RS_ERR_KEY_NOT_FOUND) + return e; + } + +#if defined(__MSDOS__) || defined(__WIN32__) + if ((p = getenv("TI83PLUSDIR"))) { + e = try_key_file(key, p, "\\Utils\\", filename); + if (e != RS_ERR_KEY_NOT_FOUND) + return e; + } +#endif + +#ifdef SHARE_DIR + e = try_key_file(key, SHARE_DIR, "", filename); + if (e != RS_ERR_KEY_NOT_FOUND) + return e; +#endif + + return RS_ERR_KEY_NOT_FOUND; +} + +/* + * Find key file for the given ID. + */ +int rs_key_find_for_id(RSKey* key, /* key structure to store + result */ + unsigned long keyid, /* key ID to search for */ + int publiconly) /* 1 = search for public + key only */ +{ + static const char* fmts[] = { "%02lx.%s", "%02lX.%s", + "%04lx.%s", "%04lX.%s", NULL }; + char buf[16]; + int i, e; + + mpz_set_ui(key->p, 0); + mpz_set_ui(key->q, 0); + mpz_set_ui(key->qinv, 0); + mpz_set_ui(key->d, 0); + + if (keyid > 0xFF) + sprintf(buf, "%04lX", keyid); + else + sprintf(buf, "%02lX", keyid); + + for (i = 0; known_priv_keys[i].n; i++) { + if (keyid == known_priv_keys[i].id) { + if ((e = rs_parse_key_value(key->n, known_priv_keys[i].n))) + return e; + + if (known_priv_keys[i].p + && (e = rs_parse_key_value(key->p, known_priv_keys[i].p))) + return e; + if (known_priv_keys[i].q + && (e = rs_parse_key_value(key->q, known_priv_keys[i].q))) + return e; + if (known_priv_keys[i].d + && (e = rs_parse_key_value(key->d, known_priv_keys[i].d))) + return e; + + rs_message(2, key, NULL, "Loaded builtin private key %s:", buf); + rs_message(2, key, NULL, " n = %ZX", key->n); + if (mpz_sgn(key->p)) + rs_message(2, key, NULL, " p = %ZX", key->p); + if (mpz_sgn(key->q)) + rs_message(2, key, NULL, " q = %ZX", key->q); + if (mpz_sgn(key->d)) + rs_message(2, key, NULL, " d = %ZX", key->d); + + key->id = keyid; + return 0; + } + } + + if (publiconly) { + for (i = 0; known_pub_keys[i].n; i++) { + if (keyid == known_pub_keys[i].id) { + if ((e = rs_parse_key_value(key->n, known_pub_keys[i].n))) + return e; + + rs_message(2, key, NULL, "Loaded builtin public key %s:", buf); + rs_message(2, key, NULL, " n = %ZX", key->n); + + key->id = keyid; + return 0; + } + } + } + + for (i = 0; fmts[i]; i++) { + sprintf(buf, fmts[i], keyid, "key"); + e = find_key_file(key, buf); + if (e != RS_ERR_KEY_NOT_FOUND) { + if (e == 0 && !key->id) + key->id = keyid; + return e; + } + } + + if (publiconly) { + for (i = 0; fmts[i]; i++) { + sprintf(buf, fmts[i], keyid, "pub"); + e = find_key_file(key, buf); + if (e != RS_ERR_KEY_NOT_FOUND) { + if (e == 0 && !key->id) + key->id = keyid; + return e; + } + } + } + + rs_error(NULL, NULL, "cannot find key file %s", buf); + return RS_ERR_KEY_NOT_FOUND; +} diff --git a/tool/rabbitsign-src/autokeys.h b/tool/rabbitsign-src/autokeys.h new file mode 100644 index 0000000..9880eab --- /dev/null +++ b/tool/rabbitsign-src/autokeys.h @@ -0,0 +1,47 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +struct pubkeyinfo { + unsigned long id; + const char* n; +} known_pub_keys[] = + {{ 0x0101, "406104CDFAD955D41F1ECCB9B622007FE8BC75E8B28DA178334755FEF27C564D47B04FD82498C163B762991C68CF64E29236BC41A4C1BCB9793B6EE965407C74BC" }, + { 0x0102, "4085F11FF810591B84875FDE4C92A5961CDD233A9B7ED76E8CFF65128C71420FCCC80E375DC8D2A8551AE2BEB9FD41654CE7B0A95E32BE9997750407904560BEFC" }, + { 0x0103, "40ED0236FD3D8B0CBA88C1CECFA549F8A838CDBAC487F9253F6C67F20C9627984FEA0D0A0BA035424C7B9F5E702286CEDCC66D2DA93320F7071BF2C93C59DE6B91" }, + { 0x010A, "4005D1EB8485AA14C983FFA04031B27C89950C3D7F4181FE603A353F48DE0933DFE1173BDD2E14FEB7325BAA35A12F21804DCFD30E56119C1305D348A77BCF448F" }, + { 0x01, "40F78D55E9A40A92D11D5C1446BFCEA74C8368BE1A81B6BA1596970E6D5932D933B86FF3CEA6B381CE65F5E383BDB02C82F33B8190375D0B40DEF2F1FE3CCA49AD" }, + { 0x02, "4081396D55C0989BC949FA30821FFE61C9441EDC3827D0E89EEE16DDEF697634B8E10B8B7F42FE7CC1A7478606D6D09F6FE96365E71E3D2AAA7C8D91068F1DFAF3" }, + { 0x03, "40E7C21F66BD1116F2F4F691121F3330060E24C8C7A1858D49636E24E80015F3AA25C2F6033AB39067D453945ABD8A5F4CFAFADABAF8BA2BFB88895A04B5D47689" }, + { 0x04, "408FE528B340EB1C88B505B2354BAADF47F3616D92CB532E7E5A2A0DFF1C4E4283CEEA2B2F7AD5F28B7E4BE4F3F4C99CABA0D98A8E5F2BE15E2AAC7CED0940EF82" }, + { 0x08, "40110510EE17B0A300E2BB27441F266843EDB541BAC1077AC203CF18ABB7800F8F0E259495F80D863C49C4EE7E9FB1FE03488A140C7CD5A54CE148C8CE22B00783" }, + { 0x0A, "40B11C71D4EA2C13C9AB2E501C6085FEC87FF3B88BFD783EAC43351E1B10F65AD31C79C1268F75051DC8FC008EBF593AE5912E8B653975C13127E2B60A0BEF5FEF" }, + { 0, 0 }}; + +struct privkeyinfo { + unsigned long id; + const char* n; + const char* p; + const char* q; + const char* d; +} known_priv_keys[] = + {{ 0x0104, "40AD2431DA2297E4175EAC61A3154FA3D847115794DD330AB7FF36BA59FEDA195FEA7C16743BD7BCED8A0DA885E5E5C34D5BF20D0AB3EF9181ED39BA2C4D898E87", + "205B2E54E9B5C1FE26CE93261478D3873F3FC41BFFF1F5F934D7A5793A43C1C21C", "2197F7707B94079B73858720BF6D4909AB3BEDA1BA9B93112B041340A16ED597B604", 0 }, + { 0x05, "406BABF27E9BF1826FD46CBF934E3360EF1F1D3D09D6C74E9DF78049D01A42F584BD383A10E64330C2EE6F1B1C5162789E91E94677900F85D98E7D99F49B30A2BF", + /* "20F59BA0274F1CA6231A882B053AAD9A2B80EBE9D2B6E9FD1CDCFCE1AD9D9414D3", "20DFED657A28DE2BFF75DE4F1AEBB7555859779DA38A671B7C76F81B50F02A6AE8", */ + 0, 0, "40E131D6636091E0F0EB3F6444FA2DABB7744FD4DDCF54018AD906C38A0789180D05C7A9275A9149819B05F279F357CEF3A0C53855AF90992572E0F09E3DC2B970" }, + { 0, 0, 0, 0, 0 }}; diff --git a/tool/rabbitsign-src/cmdline.c b/tool/rabbitsign-src/cmdline.c new file mode 100644 index 0000000..af4cb45 --- /dev/null +++ b/tool/rabbitsign-src/cmdline.c @@ -0,0 +1,112 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +#if !defined(strchr) && !defined(HAVE_STRCHR) && defined(HAVE_INDEX) +# define strchr index +#endif + +/* + * Parse and return next command line option. + */ +int rs_parse_cmdline(int argc, char** argv, const char* optstring, + int* i, int* j, const char** arg) +{ + char c; + char* p; + + if (*i >= argc) + return RS_CMDLINE_FINISHED; + + if (argv[*i][0] != '-' || argv[*i][1] == 0) { + *arg = argv[*i]; + (*i)++; + *j = 1; + return RS_CMDLINE_FILENAME; + } + + if (argv[*i][1] == '-') { + if (!strcasecmp(argv[*i], "--help")) { + (*i)++; + *j = 1; + return RS_CMDLINE_HELP; + } + else if (!strcasecmp(argv[*i], "--version")) { + (*i)++; + *j = 1; + return RS_CMDLINE_VERSION; + } + else { + rs_error(NULL, NULL, "unrecognized option %s (try --help)", argv[*i]); + return RS_CMDLINE_ERROR; + } + } + + c = argv[*i][*j]; + + if (c == ':' || !(p = strchr(optstring, c))) { + rs_error(NULL, NULL, "unrecognized option -%c (try --help)", c); + return RS_CMDLINE_ERROR; + } + + if (p[1] == ':') { + if (argv[*i][*j + 1]) { + *arg = &argv[*i][*j + 1]; + (*i)++; + *j = 1; + return c; + } + else { + (*i) += 2; + *j = 1; + if (*i > argc) { + rs_error(NULL, NULL, "-%c: requires an argument", c); + return RS_CMDLINE_ERROR; + } + *arg = argv[*i - 1]; + return c; + } + } + else { + if (argv[*i][*j + 1]) { + (*j)++; + } + else { + (*i)++; + *j = 1; + } + *arg = NULL; + return c; + } +} diff --git a/tool/rabbitsign-src/error.c b/tool/rabbitsign-src/error.c new file mode 100644 index 0000000..031f2a0 --- /dev/null +++ b/tool/rabbitsign-src/error.c @@ -0,0 +1,131 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +static const char* progname; +static int verbose; +static RSMessageFunc errorfunc, messagefunc; +static void *errorfuncdata, *messagefuncdata; + +void rs_set_progname(s) + const char* s; +{ + progname = s; +} + +void rs_set_verbose(v) + int v; +{ + verbose = v; +} + +void rs_set_error_func(RSMessageFunc func, void* data) +{ + errorfunc = func; + errorfuncdata = data; +} + +void rs_set_message_func(RSMessageFunc func, void* data) +{ + messagefunc = func; + messagefuncdata = data; +} + +static void print_message(const RSKey* key, const RSProgram* prgm, + const char* msg) +{ + if (prgm && prgm->filename) + fprintf(stderr, "%s: ", prgm->filename); + else if (key && key->filename) + fprintf(stderr, "%s: ", key->filename); + else if (progname) + fprintf(stderr, "%s: ", progname); + fputs(msg, stderr); + fputc('\n', stderr); +} + +/* Display a critical error */ +void rs_error(const RSKey* key, const RSProgram* prgm, const char* fmt, ...) +{ + char msg[512]; + va_list ap; + + va_start(ap, fmt); + strcpy(msg, "error: "); + rs_vsnprintf(msg + 7, sizeof(msg) - 7, fmt, ap); + va_end(ap); + + if (errorfunc) + (*errorfunc)(key, prgm, msg, errorfuncdata); + else + print_message(key, prgm, msg); +} + +/* Display a warning message */ +void rs_warning(const RSKey* key, const RSProgram* prgm, const char* fmt, ...) +{ + char msg[512]; + va_list ap; + + va_start(ap, fmt); + strcpy(msg, "warning: "); + rs_vsnprintf(msg + 9, sizeof(msg) - 9, fmt, ap); + va_end(ap); + + if (errorfunc) + (*errorfunc)(key, prgm, msg, errorfuncdata); + else + print_message(key, prgm, msg); +} + +/* Display an informative message */ +void rs_message(int level, const RSKey* key, const RSProgram* prgm, + const char* fmt, ...) +{ + char msg[512]; + va_list ap; + + if (level > verbose) + return; + + va_start(ap, fmt); + rs_vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + + if (messagefunc) + (*messagefunc)(key, prgm, msg, messagefuncdata); + else + print_message(key, prgm, msg); +} diff --git a/tool/rabbitsign-src/graphlink.c b/tool/rabbitsign-src/graphlink.c new file mode 100644 index 0000000..06e6e3f --- /dev/null +++ b/tool/rabbitsign-src/graphlink.c @@ -0,0 +1,118 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef TM_IN_SYS_TIME +# include +# else +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +#define BCD(x) ((x) + 6 * ((x)/10)) + +/* + * Write a TIFL header to a file. + */ +int rs_write_tifl_header(FILE* outfile, /* file to write to */ + int is_hex, /* is file in hex format? */ + int major, /* major version # */ + int minor, /* minor version # */ + int month, /* current month */ + int day, /* current day */ + int year, /* current year */ + const char* name, /* name of program */ + int calctype, /* calculator type */ + int datatype, /* data type */ + unsigned long filesize) /* size of data */ +{ + unsigned char buf[78]; + time_t t; + struct tm* tm; + + memset(buf, 0, 78); + + strcpy((char*) buf, "**TIFL**"); + + buf[8] = major; + buf[9] = minor; + + if (is_hex) { + buf[10] = 0x01; + buf[11] = 0x88; + } + else { + buf[10] = 0; + buf[11] = 0; + } + + if (!month && !day && !year) { + time(&t); + tm = localtime(&t); + month = tm->tm_mon + 1; + day = tm->tm_mday; + year = tm->tm_year + 1900; + } + + buf[12] = BCD(month); + buf[13] = BCD(day); + buf[14] = BCD(year / 100); + buf[15] = BCD(year % 100); + + buf[16] = strlen(name); + if (buf[16] > 8) + buf[16] = 8; + + strncpy((char*) buf + 17, name, 8); + + buf[48] = calctype; + buf[49] = datatype; + + buf[74] = filesize & 0xff; + buf[75] = (filesize >> 8) & 0xff; + buf[76] = (filesize >> 16) & 0xff; + buf[77] = (filesize >> 24) & 0xff; + + if (fwrite(buf, 1, 78, outfile) != 78) { + rs_error(NULL, NULL, "file I/O error"); + return RS_ERR_FILE_IO; + } + + return RS_SUCCESS; +} + diff --git a/tool/rabbitsign-src/header.c b/tool/rabbitsign-src/header.c new file mode 100644 index 0000000..36bbe5b --- /dev/null +++ b/tool/rabbitsign-src/header.c @@ -0,0 +1,169 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "rabbitsign.h" +#include "internal.h" + +/* + * Get length of a header field. + */ +void rs_get_field_size (const unsigned char* data, /* Data */ + unsigned long* fieldstart, /* Offset to start + of field + contents */ + unsigned long* fieldsize) /* Length of field + contents */ +{ + switch (data[1] & 0x0f) { + case 0x0D: + if (fieldstart) *fieldstart = 3; + if (fieldsize) *fieldsize = data[2]; + break; + + case 0x0E: + if (fieldstart) *fieldstart = 4; + if (fieldsize) *fieldsize = ((data[2] << 8) | data[3]); + break; + + case 0x0F: + if (fieldstart) *fieldstart = 6; + if (fieldsize) { + *fieldsize = (((unsigned long) data[2] << 24) + | ((unsigned long) data[3] << 16) + | ((unsigned long) data[4] << 8) + | (unsigned long) data[5]); + } + break; + + default: + if (fieldstart) *fieldstart = 2; + if (fieldsize) *fieldsize = (data[1] & 0x0f); + break; + } +} + +/* Set length of a header field. */ +int rs_set_field_size (unsigned char* data, + unsigned long fieldsize) +{ + switch (data[1] & 0x0f) { + case 0x0D: + if (fieldsize > 0xff) + return -1; + data[2] = fieldsize; + return 0; + + case 0x0E: + if (fieldsize > 0xfffful) + return -1; + data[2] = (fieldsize >> 8) & 0xff; + data[3] = fieldsize & 0xff; + return 0; + + case 0x0F: + if (fieldsize > 0xfffffffful) + return -1; + data[2] = (fieldsize >> 24) & 0xff; + data[3] = (fieldsize >> 16) & 0xff; + data[4] = (fieldsize >> 8) & 0xff; + data[5] = fieldsize & 0xff; + return 0; + + default: + if (fieldsize > 0x0C) + return -1; + data[1] = (data[1] & 0xf0) | fieldsize; + return 0; + } +} + +/* + * Find a given header field in the data. + */ +int rs_find_app_field(unsigned int type, /* Type of field to + search for (e.g., + 0x8040 to search + for the name) */ + const unsigned char* data, /* Data to search */ + unsigned long length, /* Maximum length of + data to search */ + unsigned long* fieldhead, /* Offset to field + type bytes, if + found */ + unsigned long* fieldstart, /* Offset to start of + field contents, if + found */ + unsigned long* fieldsize) /* Length of field + contents, if + found */ +{ + unsigned char b1, b2; + unsigned long pos = 0; + unsigned long fstart, fsize; + + b1 = ((type >> 8) & 0xff); + b2 = (type & 0xf0); + + while (pos < length) { + if (data[pos] == b1 && (data[pos + 1] & 0xf0) == b2) { + rs_get_field_size(data + pos, &fstart, fieldsize); + if (fieldhead) *fieldhead = pos; + if (fieldstart) *fieldstart = pos + fstart; + return 0; + } + + rs_get_field_size(data + pos, &fstart, &fsize); + pos += fstart + fsize; + } + + return -1; +} + +/* + * Get value of a numeric header field. + * + * Return 0 if field is not found, or if its contents are longer than + * 4 bytes. + */ +unsigned long rs_get_numeric_field (unsigned int type, + const unsigned char* data, + unsigned long length) +{ + unsigned long fstart, fsize, value; + + if (rs_find_app_field(type, data, length, NULL, &fstart, &fsize)) + return 0; + + if (fsize > 4) + return 0; + + value = 0; + while (fsize > 0) { + value <<= 8; + value |= data[fstart]; + fstart++; + fsize--; + } + return value; +} diff --git a/tool/rabbitsign-src/input.c b/tool/rabbitsign-src/input.c new file mode 100644 index 0000000..2f0ce37 --- /dev/null +++ b/tool/rabbitsign-src/input.c @@ -0,0 +1,520 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +/* + * Determine the type of an unknown program, if possible. + */ +static void guess_type(RSProgram* prgm, int is_hex) +{ + const unsigned char* hdr; + unsigned long hdrstart, hdrsize, keyid, fieldstart, fieldsize; + + /* Z80 OSes have a detached program header */ + + if (prgm->header_length > 2 && prgm->header[0] == 0x80) { + rs_get_field_size(prgm->header, &hdrstart, NULL); + hdr = prgm->header + hdrstart; + hdrsize = prgm->header_length - hdrstart; + keyid = rs_get_numeric_field(0x8010, hdr, hdrsize); + + prgm->datatype = RS_DATA_OS; + + if ((keyid & 0xff) == 0x02) { + prgm->calctype = RS_CALC_TI73; + } + else { + prgm->calctype = RS_CALC_TI83P; + } + } + else if (prgm->length > 2) { + rs_get_field_size(prgm->data, &hdrstart, NULL); + hdr = prgm->data + hdrstart; + hdrsize = prgm->length - hdrstart; + if (hdrsize > 128) + hdrsize = 128; + + /* Z80 apps and 68k OSes have field type 0x8000 */ + + if (prgm->data[0] == 0x80 && (prgm->data[1] & 0xf0) == 0x00) { + keyid = rs_get_numeric_field(0x8010, hdr, hdrsize); + + switch (keyid & 0xff) { + case 0x02: + prgm->calctype = RS_CALC_TI73; + prgm->datatype = RS_DATA_APP; + break; + + case 0x04: + case 0x0A: + prgm->calctype = RS_CALC_TI83P; + prgm->datatype = RS_DATA_APP; + break; + + case 0x03: + case 0x09: + prgm->calctype = RS_CALC_TI89; + prgm->datatype = RS_DATA_OS; + break; + + case 0x01: + case 0x08: + prgm->calctype = RS_CALC_TI92P; + prgm->datatype = RS_DATA_OS; + break; + + default: + if (is_hex) { + prgm->calctype = RS_CALC_TI83P; + prgm->datatype = RS_DATA_APP; + } + break; + } + } + + /* 68k apps have field type 0x8100 */ + + else if (prgm->data[0] == 0x81 && (prgm->data[1] & 0xf0) == 0x00) { + keyid = rs_get_numeric_field(0x8110, hdr, hdrsize); + prgm->datatype = RS_DATA_APP; + + switch (keyid & 0xff) { + case 0x03: + case 0x09: + prgm->calctype = RS_CALC_TI89; + break; + + case 0x01: + case 0x08: + prgm->calctype = RS_CALC_TI92P; + break; + } + } + + /* Certificates have field type 0x0300 */ + + else if (prgm->data[0] == 0x03 && (prgm->data[1] & 0xf0) == 0x00) { + prgm->datatype = RS_DATA_CERT; + + if (!rs_find_app_field(0x0400, hdr, hdrsize, + NULL, &fieldstart, &fieldsize) + && fieldsize >= 1) { + switch (hdr[fieldstart]) { + case 0x02: + prgm->calctype = RS_CALC_TI73; + break; + + case 0x04: + case 0x0A: + prgm->calctype = RS_CALC_TI83P; + break; + + case 0x03: + case 0x09: + prgm->calctype = RS_CALC_TI89; + break; + + case 0x01: + case 0x08: + prgm->calctype = RS_CALC_TI92P; + break; + } + } + } + } +} + +/* + * Read the contents of a binary file into an RSProgram. + */ +static int read_file_binary(RSProgram* prgm, + FILE* f, + unsigned long filesize) +{ + unsigned char buf[1024]; + size_t count; + + if (filesize) { + while (filesize > 0) { + if (filesize > 1024) + count = fread(buf, 1, 1024, f); + else + count = fread(buf, 1, filesize, f); + + if (count > 0) + rs_program_append_data(prgm, buf, count); + else + break; + + filesize -= count; + } + } + else { + do { + count = fread(buf, 1, 1024, f); + if (count > 0) { + rs_program_append_data(prgm, buf, count); + } + } while (count > 0); + } + + if (!prgm->calctype || !prgm->datatype) + guess_type(prgm, 0); + return RS_SUCCESS; +} + +/* + * Find a given page in the list of page numbers (or add it to the + * end.) + */ +static int getpageidx(RSProgram* prgm, /* program */ + unsigned int pagenum) /* page number */ +{ + int i; + unsigned int* array; + + for (i = 0; i < prgm->npagenums; i++) + if (prgm->pagenums[i] == pagenum) + return i; + + if (!(array = rs_realloc(prgm->pagenums, (i + 1) * sizeof(unsigned int)))) + return 0; + prgm->pagenums = array; + prgm->npagenums = i + 1; + prgm->pagenums[i] = pagenum; + return i; +} + +/* + * Read an Intel/TI hex file into an RSProgram. + * + * Note that the first ':' is assumed to have been read already. + */ +static int read_file_hex(RSProgram* prgm, + FILE* f, + unsigned int flags) +{ + int c; + unsigned int nbytes, addr, rectype, sum, i, b, value; + unsigned int pagenum = 0, pageidx = 0, lastaddr = 0; + unsigned long offset; + unsigned char data[256]; + unsigned char* sigp; + int nparts = 0; + int possibly_os_header = 1; + + rs_free(prgm->pagenums); + if (!(prgm->pagenums = rs_malloc(sizeof(unsigned int)))) + return RS_ERR_OUT_OF_MEMORY; + prgm->pagenums[0] = 0; + prgm->npagenums = 1; + + while (!feof(f) && !ferror(f)) { + if (3 > fscanf(f, "%2X%4X%2X", &nbytes, &addr, &rectype)) { + rs_error(NULL, prgm, "invalid hex data (following %X:%X)", + pagenum, lastaddr); + return RS_ERR_HEX_SYNTAX; + } + + /* Read data bytes */ + + sum = nbytes + addr + (addr >> 8) + rectype; + value = 0; + for (i = 0; i < nbytes; i++) { + if (1 > fscanf(f, "%2X", &b)) { + rs_error(NULL, prgm, "invalid hex data (at %X:%X)", + pagenum, addr); + return RS_ERR_HEX_SYNTAX; + } + data[i] = b; + sum += b; + value = (value << 8) + b; + } + + /* Read checksum */ + + c = fgetc(f); + if (c == 'X') { + c = fgetc(f); + if (c != 'X') { + rs_error(NULL, prgm, "invalid hex data (at %X:%X)", + pagenum, addr); + return RS_ERR_HEX_SYNTAX; + } + } + else { + ungetc(c, f); + if (1 > fscanf(f, "%2X", &b)) { + rs_error(NULL, prgm, "invalid hex data (at %X:%X)", + pagenum, addr); + return RS_ERR_HEX_SYNTAX; + } + sum += b; + if (sum & 0xff) + rs_warning(NULL, prgm, "incorrect checksum (at %X:%X)", + pagenum, addr); + } + + if (rectype == 0 && nbytes > 0) { + /* Record type 0: program data */ + + if (addr & 0xff00) + possibly_os_header = 0; + + addr &= 0x3fff; + + /* if program does not start at addr 0000 (or 4000), assume + unsorted */ + if (addr && prgm->length == 0) + flags &= ~RS_INPUT_SORTED; + + if ((flags & RS_INPUT_SORTED) && !addr && lastaddr) { + /* automatically switch to next page */ + pagenum++; + pageidx = getpageidx(prgm, pagenum); + if (!pageidx) + return RS_ERR_OUT_OF_MEMORY; + } + else if (addr < lastaddr) + flags &= ~RS_INPUT_SORTED; + + if (nparts == 2 && prgm->header_length) { + /* Reading an OS signature */ + if (addr + nbytes > prgm->signature_length) { + if (!(sigp = rs_realloc(prgm->signature, addr + nbytes))) + return RS_ERR_OUT_OF_MEMORY; + + prgm->signature = sigp; + if (addr > prgm->signature_length) { + memset(prgm->signature + prgm->signature_length, 0xff, + addr - prgm->signature_length); + } + prgm->signature_length = addr + nbytes; + } + memcpy(prgm->signature + addr, data, nbytes); + } + else { + /* Reading normal program data */ + offset = ((unsigned long) pageidx << 14) | addr; + if (offset + nbytes <= prgm->length) { + memcpy(prgm->data + offset, data, nbytes); + } + else { + rs_program_set_length(prgm, offset); + rs_program_append_data(prgm, data, nbytes); + } + } + + lastaddr = addr; + } + else if (rectype == 1) { + /* Record type 1: "end of file" */ + nparts++; + if (nparts == 3 && prgm->header_length) + break; + } + else if (rectype == 2 || rectype == 4) { + /* Record type 2 or 4: extended address */ + possibly_os_header = 0; + flags &= ~RS_INPUT_SORTED; + if (nparts < 2) { + pagenum = value; + pageidx = getpageidx(prgm, pagenum); + if (pagenum && !pageidx) + return RS_ERR_OUT_OF_MEMORY; + } + } + + do { + c = fgetc(f); + } while (c == '\n' || c == '\r' || c == ' '); + + if (c == EOF) + break; + else if (c != ':') { + if (rectype == 1) + break; + else { + rs_error(NULL, prgm, "invalid hex data (following %X:%X)", + pagenum, lastaddr); + return RS_ERR_HEX_SYNTAX; + } + } + + if (rectype == 1 && nparts == 1 && prgm->length > 0 + && possibly_os_header) { + /* Just finished reading OS header */ + flags &= ~RS_INPUT_SORTED; + pagenum = pageidx = 0; + + rs_free(prgm->header); + if (!(prgm->header = rs_malloc(prgm->length))) + return RS_ERR_OUT_OF_MEMORY; + + memcpy(prgm->header, prgm->data, prgm->length); + prgm->header_length = prgm->length; + prgm->length = 0; + possibly_os_header = 0; + } + } + + if (!prgm->calctype || !prgm->datatype) + guess_type(prgm, 1); + return RS_SUCCESS; +} + +/* + * Check if calc/data type matches expected type (or any recognized + * type, if none was specified.) + */ +static int check_tifl_type(int calctype, + int datatype, + int calctype_expected, + int datatype_expected) +{ + if (calctype_expected) { + if (calctype_expected != calctype) + return 0; + } + else { + if (calctype != RS_CALC_TI73 && calctype != RS_CALC_TI83P + && calctype != RS_CALC_TI89 && calctype != RS_CALC_TI92P) + return 0; + } + + if (datatype_expected) { + if (datatype_expected != datatype) + return 0; + } + else { + if (datatype != RS_DATA_APP && datatype != RS_DATA_OS) + return 0; + } + + return 1; +} + +/* + * Read program contents from a file. + * + * Various file formats are supported: + * + * - Raw binary (must begin with the value 0x80 or 0x81) + * - Plain Intel/TI hex + * - Binary TIFL (89k, 89u, ...) + * - Hex TIFL (8xk, 8xu, ...) + * + * Note: on platforms where it matters, all input files must be opened + * in "binary" mode. + */ +int rs_read_program_file(RSProgram* prgm, /* program */ + FILE* f, /* file */ + const char* fname, /* file name */ + unsigned int flags) /* option flags */ +{ + int c; + unsigned char tiflbuf[78]; + unsigned long tiflsize, i; + int e; + + rs_program_set_length(prgm, 0); + prgm->header_length = 0; + prgm->signature_length = 0; + prgm->npagenums = 0; + + rs_free(prgm->filename); + prgm->filename = rs_strdup(fname); + if (fname && !prgm->filename) + return RS_ERR_OUT_OF_MEMORY; + + if (flags & RS_INPUT_BINARY) + return read_file_binary(prgm, f, 0); + + c = fgetc(f); + if (c == 0x80 || c == 0x81) { + tiflbuf[0] = c; + if ((e = rs_program_append_data(prgm, tiflbuf, 1))) + return e; + return read_file_binary(prgm, f, 0); + } + + while (!feof(f) && !ferror(f)) { + if (c == ':') { + return read_file_hex(prgm, f, flags); + } + else if (c == '*') { + if (fread(tiflbuf, 1, 78, f) < 78 + || strncmp((char*) tiflbuf, "*TIFL**", 7)) { + rs_error(NULL, prgm, "unknown input file format"); + return RS_ERR_UNKNOWN_FILE_FORMAT; + } + + tiflsize = ((unsigned long) tiflbuf[73] + | ((unsigned long) tiflbuf[74] << 8) + | ((unsigned long) tiflbuf[75] << 16) + | ((unsigned long) tiflbuf[76] << 24)); + + if (check_tifl_type(tiflbuf[47], tiflbuf[48], + prgm->calctype, prgm->datatype)) { + prgm->calctype = tiflbuf[47]; + prgm->datatype = tiflbuf[48]; + + if (tiflbuf[77] == ':') + return read_file_hex(prgm, f, 0); + else { + if ((e = rs_program_append_data(prgm, tiflbuf + 77, 1))) + return e; + return read_file_binary(prgm, f, tiflsize ? tiflsize - 1 : 0); + } + } + else { + /* extra data (license, certificate, etc.) -- ignore */ + if (fseek(f, tiflsize - 1, SEEK_CUR)) { + for (i = 0; i < tiflsize - 1; i++) { + if (fgetc(f) == EOF) { + rs_error(NULL, prgm, "unexpected EOF"); + return RS_ERR_UNKNOWN_FILE_FORMAT; + } + } + } + } + } + + c = fgetc(f); + } + + rs_error(NULL, prgm, "unknown input file format"); + return RS_ERR_UNKNOWN_FILE_FORMAT; +} + diff --git a/tool/rabbitsign-src/internal.h b/tool/rabbitsign-src/internal.h new file mode 100644 index 0000000..d3fc1b5 --- /dev/null +++ b/tool/rabbitsign-src/internal.h @@ -0,0 +1,111 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __RABBITSIGN_INTERNAL_H__ +#define __RABBITSIGN_INTERNAL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/**** Memory management (mem.c) ****/ + +#define rs_malloc(nnn) rs_realloc(0, (nnn)) +#define rs_free(ppp) rs_realloc((ppp), 0) +void* rs_realloc (void* ptr, unsigned long count) RS_ATTR_MALLOC; +char* rs_strdup (const char* str) RS_ATTR_MALLOC; + + +/**** Rabin signature functions (rabin.c) ****/ + +/* Compute a Rabin signature and the useful value of f. */ +RSStatus rs_sign_rabin (mpz_t res, int* f, const mpz_t hash, + int rootnum, RSKey* key); + +/* Check that the given Rabin signature is valid. */ +RSStatus rs_validate_rabin (const mpz_t sig, int f, const mpz_t hash, + const RSKey* key); + + +/**** RSA signature functions (rsa.c) ****/ + +/* Compute an RSA signature. */ +RSStatus rs_sign_rsa (mpz_t res, const mpz_t hash, RSKey* key); + +/* Check that the given RSA signature is valid. */ +RSStatus rs_validate_rsa (const mpz_t sig, const mpz_t hash, + const RSKey* key); + + +/**** TIFL file output (graphlink.c) ****/ + +/* Write TIFL header to a file. */ +RSStatus rs_write_tifl_header (FILE* f, int is_hex, int major, int minor, + int month, int day, int year, + const char* name, int calctype, int datatype, + unsigned long filesize); + + +/**** Type <-> string conversions (typestr.c) ****/ + +/* Get default file suffix for a given calc/data type. */ +const char* rs_type_to_suffix (RSCalcType calctype, RSDataType datatype, + int hexonly); + +/* Get implied calc/data type for a given file suffix. */ +int rs_suffix_to_type (const char* suff, RSCalcType* calctype, + RSDataType* datatype); + +/* Get a human-readable description of a calculator type. */ +const char* rs_calc_type_to_string (RSCalcType calctype); + +/* Get a human-readable description of a data type. */ +const char* rs_data_type_to_string (RSDataType datatype); + + +/**** Command line option parsing (cmdline.c) ****/ + +#define RS_CMDLINE_FINISHED 0 +#define RS_CMDLINE_FILENAME '#' +#define RS_CMDLINE_HELP '!' +#define RS_CMDLINE_VERSION '@' +#define RS_CMDLINE_ERROR '?' + +int rs_parse_cmdline(int argc, char** argv, const char* optstring, + int* i, int* j, const char** arg); + + +/**** Error/message logging (error.c) ****/ + +/* Display an error message */ +void rs_error (const RSKey* key, const RSProgram* prgm, + const char* fmt, ...) RS_ATTR_PRINTF(3,4); + +/* Display a warning message */ +void rs_warning (const RSKey* key, const RSProgram* prgm, + const char* fmt, ...) RS_ATTR_PRINTF(3,4); + +/* Display an informational message */ +void rs_message (int level, const RSKey* key, const RSProgram* prgm, + const char* fmt, ...) /*RS_ATTR_PRINTF(4,5)*/; + +#ifdef __cplusplus +} +#endif + +#endif /* __RABBITSIGN_INTERNAL_H__ */ diff --git a/tool/rabbitsign-src/keys.c b/tool/rabbitsign-src/keys.c new file mode 100644 index 0000000..d92f36b --- /dev/null +++ b/tool/rabbitsign-src/keys.c @@ -0,0 +1,234 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +/* + * Create a new key. + */ +RSKey* rs_key_new() +{ + RSKey* key = rs_malloc(sizeof(RSKey)); + + if (!key) + return NULL; + + key->filename = NULL; + key->id = 0; + mpz_init(key->n); + mpz_init(key->p); + mpz_init(key->q); + mpz_init(key->qinv); + mpz_init(key->d); + + return key; +} + +/* + * Free a key. + */ +void rs_key_free(RSKey* key) +{ + if (!key) + return; + + rs_free(key->filename); + mpz_clear(key->n); + mpz_clear(key->p); + mpz_clear(key->q); + mpz_clear(key->qinv); + mpz_clear(key->d); + rs_free(key); +} + +/* + * Parse a number written in TI's hexadecimal key format. + */ +static int parse_value(mpz_t dest, /* mpz to store result */ + const char* str) /* string to parse */ +{ + unsigned int count, b, i; + int n; + unsigned char buf[256]; + + if (1 > sscanf(str, "%2X%n", &count, &n) || n != 2 + || (count * 2 + 2) > strlen(str)) + return 1; + + for (i = 0; i < count; i++) { + if (1 > sscanf(str + 2 + 2 * i, "%2X%n", &b, &n) || n != 2) + return 1; + buf[i] = b; + } + + mpz_import(dest, i, -1, 1, 0, 0, buf); + return 0; +} + +/* + * Read key from a file. + * + * Two formats of key file are supported: + * + * "Rabin" style (the type used by the TI-83 Plus SDK) consists of + * three lines: the public key (n) followed by its two factors (p and + * q.) + * + * "RSA" style (the type used by the TI-89/92 Plus SDK) also consists + * of three lines: the key ID, the public key (n), and the signing + * exponent (d). + * + * In either case, if we are only interested in validating signatures, + * the private key may be omitted. + * + * Note that "Rabin" style key files can be used to generate RSA + * signatures, but not vice versa. + */ +int rs_read_key_file(RSKey* key, /* key structure */ + FILE* f, /* file to read */ + const char* fname, /* file name */ + int verify) /* 1 = check key validity */ +{ + char buf[1024]; + mpz_t tmp; + + rs_free(key->filename); + key->filename = rs_strdup(fname); + if (fname && !key->filename) + return RS_ERR_OUT_OF_MEMORY; + + if (!fgets(buf, sizeof(buf), f)) { + rs_error(key, NULL, "invalid key file syntax"); + return RS_ERR_KEY_SYNTAX; + } + + if (strlen(buf) < 11) { + if (1 > sscanf(buf, "%lX", &key->id)) { + rs_error(key, NULL, "invalid key file syntax"); + return RS_ERR_KEY_SYNTAX; + } + + if (!fgets(buf, sizeof(buf), f) + || parse_value(key->n, buf)) { + rs_error(key, NULL, "invalid key file syntax"); + return RS_ERR_KEY_SYNTAX; + } + + if (!fgets(buf, sizeof(buf), f) + || parse_value(key->d, buf)) + mpz_set_ui(key->d, 0); + else if (verify) { + /* We can't truly verify the key without factoring n (which is + possible, given d, but would take a bit of work.) Instead, + test the key by performing a single RSA encryption and + decryption. */ + mpz_init(tmp); + mpz_set_ui(tmp, 17); + mpz_powm(tmp, tmp, tmp, key->n); + mpz_powm(tmp, tmp, key->d, key->n); + if (mpz_cmp_ui(tmp, 17)) { + mpz_clear(tmp); + rs_error(key, NULL, "private key incorrect (de != 1 mod phi(n))"); + return RS_ERR_INVALID_KEY; + } + mpz_clear(tmp); + } + + mpz_set_ui(key->p, 0); + mpz_set_ui(key->q, 0); + mpz_set_ui(key->qinv, 0); + } + else { + if (parse_value(key->n, buf)) { + rs_error(key, NULL, "invalid key file"); + return RS_ERR_KEY_SYNTAX; + } + + if (!fgets(buf, sizeof(buf), f) + || parse_value(key->p, buf) + || !fgets(buf, sizeof(buf), f) + || parse_value(key->q, buf)) { + mpz_set_ui(key->p, 0); + mpz_set_ui(key->q, 0); + } + else if (verify) { + /* Verify that p * q = n (of course, that doesn't guarantee that + these are the only factors of n.) */ + mpz_init(tmp); + mpz_mul(tmp, key->p, key->q); + if (mpz_cmp(tmp, key->n)) { + mpz_clear(tmp); + rs_error(key, NULL, "private key incorrect (pq != n)"); + return RS_ERR_INVALID_KEY; + } + mpz_clear(tmp); + } + + mpz_set_ui(key->qinv, 0); + mpz_set_ui(key->d, 0); + key->id = 0; + } + + if (mpz_sgn(key->p) && mpz_sgn(key->q)) { + rs_message(2, key, NULL, "Loaded Rabin/RSA private key:"); + rs_message(2, key, NULL, " n = %ZX", key->n); + rs_message(2, key, NULL, " p = %ZX", key->p); + rs_message(2, key, NULL, " q = %ZX", key->q); + } + else if (mpz_sgn(key->d)) { + rs_message(2, key, NULL, "Loaded RSA private key:"); + rs_message(2, key, NULL, " n = %ZX", key->n); + rs_message(2, key, NULL, " d = %ZX", key->d); + } + else { + rs_message(2, key, NULL, "Loaded public key:"); + rs_message(2, key, NULL, " n = %ZX", key->n); + } + + return RS_SUCCESS; +} + +/* + * Parse a number written in TI's hexadecimal key format. + */ +int rs_parse_key_value(mpz_t dest, /* mpz to store result */ + const char* str) /* string to parse */ +{ + if (parse_value(dest, str)) { + rs_error(NULL, NULL, "invalid key value syntax"); + return RS_ERR_KEY_SYNTAX; + } + else { + return RS_SUCCESS; + } +} diff --git a/tool/rabbitsign-src/md5.c b/tool/rabbitsign-src/md5.c new file mode 100644 index 0000000..d742c54 --- /dev/null +++ b/tool/rabbitsign-src/md5.c @@ -0,0 +1,419 @@ +/* md5.c - Functions to compute MD5 message digest of files or memory blocks + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Written by Ulrich Drepper , 1995. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#if STDC_HEADERS || defined _LIBC +# include +# include +#else +# ifndef HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#include "md5.h" + +#ifdef _LIBC +# include +# if __BYTE_ORDER == __BIG_ENDIAN +# define WORDS_BIGENDIAN 1 +# endif +#endif + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (ctx) + struct md5_ctx *ctx; +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_read_ctx (ctx, resbuf) + const struct md5_ctx *ctx; + void *resbuf; +{ + ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_finish_ctx (ctx, resbuf) + struct md5_ctx *ctx; + void *resbuf; +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +md5_stream (stream, resblock) + FILE *stream; + void *resblock; +{ + /* Important: BLOCKSIZE must be a multiple of 64. */ +#define BLOCKSIZE 4096 + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + do + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + } + while (sum < BLOCKSIZE && n != 0); + if (n == 0 && ferror (stream)) + return 1; + + /* If end of file is reached, end the loop. */ + if (n == 0) + break; + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block (buffer, BLOCKSIZE, &ctx); + } + + /* Add the last bytes if necessary. */ + if (sum > 0) + md5_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx (&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (buffer, len, resblock) + const char *buffer; + size_t len; + void *resblock; +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx (&ctx, resblock); +} + + +void +md5_process_bytes (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (left_over + add > 64) + { + md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx); + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + (left_over + add) & 63); + ctx->buflen = (left_over + add) & 63; + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len > 64) + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + memcpy (ctx->buffer, buffer, len); + ctx->buflen = len; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +void +md5_process_block (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} diff --git a/tool/rabbitsign-src/md5.h b/tool/rabbitsign-src/md5.h new file mode 100644 index 0000000..ad97efc --- /dev/null +++ b/tool/rabbitsign-src/md5.h @@ -0,0 +1,146 @@ +/* md5.h - Declaration of functions and data types used for MD5 sum + computing library functions. + Copyright (C) 1995, 1996 Free Software Foundation, Inc. + NOTE: The canonical source of this file is maintained with the GNU C + Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _MD5_H +#define _MD5_H 1 + +#include + +#if defined HAVE_LIMITS_H || _LIBC +# include +#endif + +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + doing that would require that the configure script compile and *run* + the resulting executable. Locally running cross-compiled executables + is usually not possible. */ + +#ifdef _LIBC +# include +typedef u_int32_t md5_uint32; +#else +# if defined __STDC__ && __STDC__ +# define UINT_MAX_32_BITS 4294967295U +# else +# define UINT_MAX_32_BITS 0xFFFFFFFF +# endif + +/* If UINT_MAX isn't defined, assume it's a 32-bit type. + This should be valid for all systems GNU cares about because + that doesn't include 16-bit systems, and only modern systems + (that certainly have ) have 64+-bit integral types. */ + +# ifndef UINT_MAX +# define UINT_MAX UINT_MAX_32_BITS +# endif + +# if UINT_MAX == UINT_MAX_32_BITS + typedef unsigned int md5_uint32; +# else +# if USHRT_MAX == UINT_MAX_32_BITS + typedef unsigned short md5_uint32; +# else +# if ULONG_MAX == UINT_MAX_32_BITS + typedef unsigned long md5_uint32; +# else + /* The following line is intended to evoke an error. + Using #error is not portable enough. */ + "Cannot determine unsigned 32-bit data type." +# endif +# endif +# endif +#endif + +#undef __P +#if defined (__STDC__) && __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128]; +}; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +extern void md5_init_ctx __P ((struct md5_ctx *ctx)); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void md5_process_block __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void md5_process_bytes __P ((const void *buffer, size_t len, + struct md5_ctx *ctx)); + +/* Process the remaining bytes in the buffer and put result from CTX + in first 16 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf)); + + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf)); + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +extern int md5_stream __P ((FILE *stream, void *resblock)); + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *md5_buffer __P ((const char *buffer, size_t len, void *resblock)); + +#endif diff --git a/tool/rabbitsign-src/mem.c b/tool/rabbitsign-src/mem.c new file mode 100644 index 0000000..d65064b --- /dev/null +++ b/tool/rabbitsign-src/mem.c @@ -0,0 +1,73 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +void* rs_realloc(void* ptr, unsigned long count) +{ + void* p; + + if (!count) { + if (ptr) { + free(ptr); + } + return NULL; + } + + if (ptr) + p = realloc(ptr, count); + else + p = malloc(count); + if (!p) + rs_error(NULL, NULL, "out of memory (need %lu bytes)", count); + return p; +} + +char* rs_strdup(const char* str) +{ + int n; + char* p; + + if (!str) + return NULL; + + n = strlen(str); + p = rs_malloc(n + 1); + if (p) + memcpy(p, str, n + 1); + return p; +} diff --git a/tool/rabbitsign-src/mpz.c b/tool/rabbitsign-src/mpz.c new file mode 100644 index 0000000..2a38ef5 --- /dev/null +++ b/tool/rabbitsign-src/mpz.c @@ -0,0 +1,1026 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_ASSERT_H +# include +#else +# define assert(xxx) if (!(xxx)) { \ + fprintf(stderr, "mpz: assertion \"%s\" failed\n", #xxx); \ + abort(); \ + } +#endif + +#include "mpz.h" + +/* + * This file contains multiple-precision arithmetic functions. These + * are equivalent to the corresponding GMP functions in the ways they + * are used by RabbitSign. + * + * HOWEVER, they are not by any means complete. They do not meet the + * specifications of the GMP functions; for the sake of portability + * and compactness they are vastly less efficient; and they may even + * have bugs. + */ + +/* +#define DBG(args...) if (1) \ + do { \ + gmp_fprintf(stderr, "mpz: " args); \ + fputc('\n', stderr); \ + } while (0) +*/ + +#define IDX(nnn, iii) (nnn)->m[iii] + +/* \ + (*(__extension__ ({ \ + int _ii = (iii); \ + const struct _mpz* _nn = (nnn); \ + assert(_nn->size <= _nn->size_alloc); \ + assert(_ii >= 0); \ + assert(((unsigned) _ii) < (_nn->size)); \ + &(_nn->m[_ii]); }))) +*/ + +static void* xrealloc(p, n) + void* p; + size_t n; +{ + void* res; + + if (n <= 0) + n = 1; + + if (p) + res = realloc(p, n); + else + res = malloc(n); + + if (!res) { + fprintf(stderr,"mpz: out of memory (need %lu bytes)\n", + (unsigned long) n); + abort(); + } + return res; +} + +static inline void allocate_mpz(x) + mpz_t x; +{ + if (x->size_alloc < x->size) { + x->size_alloc = x->size; + x->m = (limb_t*) xrealloc(x->m, x->size_alloc * sizeof(limb_t)); + } +} + +static inline void zero_mpz(x) + mpz_t x; +{ + size_t i; + for (i = 0; i < x->size; i++) + IDX(x, i) = 0; +} + +static inline void copyref_mpz(dest, src) + mpz_t dest; + const mpz_t src; +{ + dest->size = src->size; + dest->size_alloc = src->size_alloc; + dest->m = src->m; + dest->sign = src->sign; +} + +static inline void reduce_mpz(x) + mpz_t x; +{ + while (x->size > 0 && IDX(x, (x->size) - 1) == 0) + x->size--; +} + +/**************** Init / Clear ****************/ + +void mpz_init(x) + mpz_t x; +{ + x->size = 0; + x->size_alloc = 0; + x->m = (limb_t*)0; + x->sign = 1; +} + +void mpz_clear(x) + mpz_t x; +{ + if (x->m) + free(x->m); + mpz_init(x); +} + +/**************** Setting ****************/ + +void mpz_set(dest, src) + mpz_t dest; + const mpz_t src; +{ + size_t i; + + dest->size = src->size; + allocate_mpz(dest); + dest->sign = src->sign; + + for (i = 0; i < src->size; i++) + IDX(dest, i) = IDX(src, i); +} + +void mpz_set_ui(dest, a) + mpz_t dest; + unsigned int a; +{ + if (a) { + dest->size = 1; + allocate_mpz(dest); + IDX(dest, 0) = a; + } + else + dest->size = 0; + dest->sign = 1; +} + +unsigned int mpz_get_ui(a) + const mpz_t a; +{ + return IDX(a, 0); +} + +static void mpz_swap(a, b) + mpz_t a; + mpz_t b; +{ + mpz_t temp; + copyref_mpz(temp, a); + copyref_mpz(a, b); + copyref_mpz(b, temp); +} + +/**************** Import / Export ****************/ + +void mpz_import(dest, count, order, size, endian, nails, op) + mpz_t dest; + size_t count; + int order; /* must be -1 (little endian structure) */ + int size; /* must be 1 (bytes) */ + int endian; /* must be 0 (native endian words, doesn't matter for bytes) */ + size_t nails; /* must be 0 (no nails) */ + const void* op; +{ + size_t i, j; + + assert(order == -1); + assert(size == 1); + assert(endian == 0); + assert(nails == 0); + + dest->size = (count + LIMB_BYTES - 1) / LIMB_BYTES; + allocate_mpz(dest); + dest->sign = 1; + + for (i = 0; i < dest->size; i++) { + IDX(dest, i) = 0; + for (j = 0; j < LIMB_BYTES && ((i * LIMB_BYTES) + j) < count; j++) { + IDX(dest, i) |= ((unsigned char*)op)[(i * LIMB_BYTES) + j] << 8 * j; + } + } +} + +void mpz_export(dest, count, order, size, endian, nails, op) + void* dest; + size_t* count; + int order; /* must be -1 (little endian structure) */ + int size; /* must be 1 (bytes) */ + int endian; /* must be 0 (native endian words, doesn't matter for bytes) */ + size_t nails; /* must be 0 (no nails) */ + const mpz_t op; +{ + size_t i, j; + + assert(order == -1); + assert(size == 1); + assert(endian == 0); + assert(nails == 0); + + for (i = 0; i < op->size; i++) { + for (j = 0; j < LIMB_BYTES; j++) { + ((unsigned char*)dest)[(i * LIMB_BYTES) + j] = IDX(op, i) >> 8 * j; + } + } + *count = op->size * LIMB_BYTES; +} + +/**************** Comparison ****************/ + +int mpz_sgn(a) + const mpz_t a; +{ + size_t i = a->size; + + while (i > 0 && IDX(a, i - 1) == 0) + i--; + + if (i == 0) + return 0; + else + return a->sign; +} + +static int mpz_cmpabs(a, b) + const mpz_t a; + const mpz_t b; +{ + size_t sa = a->size; + size_t sb = b->size; + + while (sa > 0 && IDX(a, sa - 1) == 0) + sa--; + while (sb > 0 && IDX(b, sb - 1) == 0) + sb--; + + if (sa > sb) + return 1; + else if (sb > sa) + return -1; + + while (sa > 0) { + if (IDX(a, sa - 1) > IDX(b, sa - 1)) + return 1; + else if (IDX(a, sa - 1) < IDX(b, sa - 1)) + return -1; + sa--; + } + + return 0; +} + +int mpz_cmp(a, b) + const mpz_t a; + const mpz_t b; +{ + size_t sa = a->size; + size_t sb = b->size; + + while (sa > 0 && IDX(a, sa - 1) == 0) + sa--; + while (sb > 0 && IDX(b, sb - 1) == 0) + sb--; + + if (sa == 0 && sb == 0) + return 0; + else if (sa == 0) + return -(b->sign); + else if (sb == 0) + return a->sign; + else if (a->sign != b->sign) + return a->sign; + + return a->sign * mpz_cmpabs(a, b); +} + +int mpz_cmp_ui(a, b) + const mpz_t a; + unsigned int b; +{ + size_t sa = a->size; + + while (sa > 0 && IDX(a, sa - 1) == 0) + sa--; + + if (sa == 0 && b == 0) + return 0; + + if (sa == 1 && a->sign == 1) { + if (IDX(a, 0) > b) + return 1; + else if (IDX(a, 0) < b) + return -1; + else + return 0; + } + + return (a->sign); +} + +/**************** Addition / Subtraction ****************/ + +static void mpz_addabs(dest, a, b) + mpz_t dest; /* != a, b */ + const mpz_t a; + const mpz_t b; +{ + size_t i; + double_limb_t carry = 0; + + if (a->size > b->size) + dest->size = a->size + 1; + else + dest->size = b->size + 1; + allocate_mpz(dest); + + assert(dest != a); + assert(dest != b); + + for (i = 0; i < a->size && i < b->size; i++) { + carry += IDX(a, i); + carry += IDX(b, i); + IDX(dest, i) = carry & LIMB_MASK; + carry >>= LIMB_BITS; + } + + for (; i < a->size; i++) { + carry += IDX(a, i); + IDX(dest, i) = carry & LIMB_MASK; + carry >>= LIMB_BITS; + } + + for (; i < b->size; i++) { + carry += IDX(b, i); + IDX(dest, i) = carry & LIMB_MASK; + carry >>= LIMB_BITS; + } + IDX(dest, i) = carry; +} + +static void mpz_subabs(dest, a, b) + mpz_t dest; /* != b */ + const mpz_t a; + const mpz_t b; /* must be <= a */ +{ + size_t i; + signed_double_limb_t carry = 0; + dest->size = a->size; + allocate_mpz(dest); + + assert(dest != b); + + for (i = 0; i < a->size && i < b->size; i++) { + carry += IDX(a, i); + carry -= IDX(b, i); + IDX(dest, i) = carry & LIMB_MASK; + carry >>= LIMB_BITS; + } + + for (; i < a->size; i++) { + carry += IDX(a, i); + IDX(dest, i) = carry & LIMB_MASK; + carry >>= LIMB_BITS; + } + + assert(carry == 0); +} + +void mpz_add(dest, a, b) + mpz_t dest; + const mpz_t a; + const mpz_t b; +{ + mpz_t temp; + mpz_init(temp); + + if (a->sign == b->sign) { + temp->sign = a->sign; + mpz_addabs(temp, a, b); + } + else if (mpz_cmpabs(a, b) > 0) { + temp->sign = a->sign; + mpz_subabs(temp, a, b); + } + else { + temp->sign = b->sign; + mpz_subabs(temp, b, a); + } + + reduce_mpz(temp); + mpz_clear(dest); + copyref_mpz(dest, temp); +} + +void mpz_sub(dest, a, b) + mpz_t dest; + const mpz_t a; + const mpz_t b; +{ + mpz_t temp; + mpz_init(temp); + + if (a->sign != b->sign) { + temp->sign = a->sign; + mpz_addabs(temp, a, b); + } + else if (mpz_cmpabs(a, b) > 0) { + temp->sign = a->sign; + mpz_subabs(temp, a, b); + } + else { + temp->sign = -(b->sign); + mpz_subabs(temp, b, a); + } + + reduce_mpz(temp); + mpz_clear(dest); + copyref_mpz(dest, temp); +} + +void mpz_add_ui(dest, a, b) + mpz_t dest; + const mpz_t a; + unsigned int b; +{ + size_t i; + mpz_t temp; + mpz_init(temp); + + temp->size = a->size + 1; + temp->sign = a->sign; + allocate_mpz(temp); + + for (i = 0; i < a->size; i++) { + IDX(temp, i) = IDX(a, i) + b; + b = (IDX(temp, i) < IDX(a, i)) ? 1 : 0; + } + IDX(temp, i) = b; + + reduce_mpz(temp); + mpz_clear(dest); + copyref_mpz(dest, temp); +} + +void mpz_sub_ui(dest, a, b) + mpz_t dest; + const mpz_t a; + unsigned int b; +{ + size_t i; + mpz_t temp; + mpz_init(temp); + + temp->size = a->size; + temp->sign = a->sign; + allocate_mpz(temp); + + for (i = 0; i < a->size; i++) { + IDX(temp, i) = IDX(a, i) - b; + b = (IDX(temp, i) > IDX(a, i)) ? 1 : 0; + } + assert(b == 0); + + reduce_mpz(temp); + mpz_clear(dest); + copyref_mpz(dest, temp); +} + +/**************** Multiplication ****************/ + +void mpz_mul(dest, a, b) + mpz_t dest; + const mpz_t a; + const mpz_t b; +{ + double_limb_t carry = 0, newcarry; + size_t i, j, k; + mpz_t temp; + mpz_init(temp); + + temp->size = a->size + b->size; + temp->sign = a->sign * b->sign; + allocate_mpz(temp); + zero_mpz(temp); + + for (i = 0; i < a->size; i++) { + for (j = 0; j < b->size; j++) { + carry = IDX(a, i); + carry *= IDX(b, j); + for (k = i + j; k < temp->size && carry; k++) { + newcarry = carry + IDX(temp, k); + if (newcarry < carry) { + IDX(temp, k) = newcarry & LIMB_MASK; + carry = (newcarry >> LIMB_BITS) + LIMB_MASK + 1; + } + else { + IDX(temp, k) = newcarry & LIMB_MASK; + carry = (newcarry >> LIMB_BITS); + } + } + assert(carry == 0); + } + } + + reduce_mpz(temp); + mpz_clear(dest); + copyref_mpz(dest, temp); +} + +void mpz_mul_ui(dest, a, b) + mpz_t dest; + const mpz_t a; + unsigned int b; /* must be fairly small */ +{ + double_limb_t carry = 0; + size_t i; + mpz_t temp; + mpz_init(temp); + + temp->size = a->size + 1; + temp->sign = a->sign; + allocate_mpz(temp); + + for (i = 0; i < a->size; i++) { + carry += (double_limb_t) IDX(a, i) * b; + IDX(temp, i) = carry & LIMB_MASK; + carry >>= LIMB_BITS; + } + IDX(temp, i) = carry & LIMB_MASK; + + reduce_mpz(temp); + mpz_clear(dest); + copyref_mpz(dest, temp); +} + +/**************** Division ****************/ + +void mpz_fdiv_q_2exp(dest, a, b) + mpz_t dest; + const mpz_t a; + unsigned int b; /* must be <= LIMB_BITS */ +{ + size_t i; + mpz_t temp; + mpz_init(temp); + + assert(b <= LIMB_BITS); + + temp->size = a->size; + temp->sign = a->sign; + allocate_mpz(temp); + + if (a->size > 0) { + for (i = 0; i < (a->size - 1); i++) { + IDX(temp, i) = (((IDX(a, i) >> b) | (IDX(a, i + 1) << (LIMB_BITS - b))) + & LIMB_MASK); + } + IDX(temp, a->size - 1) = IDX(a, a->size - 1) >> b; + } + + reduce_mpz(temp); + mpz_clear(dest); + copyref_mpz(dest, temp); +} + +/**************** Division / Modulus ****************/ + +static void mpz_setbit(dest, n) + mpz_t dest; + unsigned int n; +{ + size_t i, j; + + i = n / LIMB_BITS + 1; + + if (dest->size < i) { + j = dest->size; + dest->size = i; + allocate_mpz(dest); + while (j < i) { + IDX(dest, j) = 0; + j++; + } + } + + IDX(dest, i - 1) |= (1 << (n % LIMB_BITS)); +} + +static void mpz_fdiv_qr(q, r, num, den) + mpz_t q; + mpz_t r; + const mpz_t num; + const mpz_t den; +{ + size_t shiftct = 0; + size_t i; + mpz_t remainder; + mpz_t quotient; + mpz_t shifted; /* shifted = mod * 2^(shiftct) */ + + mpz_init(remainder); + mpz_init(shifted); + if (q) { + mpz_init(quotient); + quotient->sign = num->sign * den->sign; + } + + shifted->size = num->size; + allocate_mpz(shifted); + + mpz_set(remainder, num); + mpz_set(shifted, den); + + reduce_mpz(shifted); + assert(shifted->size > 0); + + while (mpz_cmpabs(remainder, shifted) > 0) { + shifted->size++; + allocate_mpz(shifted); + for (i = shifted->size - 1; i > 0; i--) + IDX(shifted, i) = IDX(shifted, i - 1); + IDX(shifted, 0) = 0; + shiftct += LIMB_BITS; + } + + while (shiftct != 0) { + if (mpz_cmpabs(remainder, shifted) >= 0) { + mpz_subabs(remainder, remainder, shifted); + reduce_mpz(remainder); + if (q) + mpz_setbit(quotient, shiftct); + } + mpz_fdiv_q_2exp(shifted, shifted, 1); + shiftct--; + } + + if (mpz_cmpabs(remainder, den) >= 0) { + mpz_subabs(remainder, remainder, den); + if (q) + mpz_setbit(quotient, 0); + } + + if (mpz_sgn(remainder) == -1) { + mpz_add(remainder, remainder, den); + if (q) + mpz_sub_ui(quotient, quotient, 1); + } + + mpz_clear(shifted); + reduce_mpz(remainder); + mpz_clear(r); + copyref_mpz(r, remainder); + + if (q) { + reduce_mpz(quotient); + mpz_clear(q); + copyref_mpz(q, quotient); + } +} + +void mpz_mod(dest, a, mod) + mpz_t dest; + const mpz_t a; + const mpz_t mod; +{ + mpz_fdiv_qr(NULL, dest, a, mod); +} + +/**************** Modular exponent ****************/ + +void mpz_powm(dest, base, exp, mod) + mpz_t dest; + const mpz_t base; + const mpz_t exp; + const mpz_t mod; +{ + mpz_t exp_bits; + mpz_t base_power; + mpz_t temp; + mpz_init(exp_bits); + mpz_init(base_power); + mpz_init(temp); + + mpz_set(exp_bits, exp); + mpz_set(base_power, base); + mpz_set_ui(temp, 1); + + reduce_mpz(exp_bits); + assert(exp_bits->sign == 1 || exp_bits->size == 0); + + while (exp_bits->size > 0) { + if (IDX(exp_bits, 0) & 1) { + mpz_mul(temp, temp, base_power); + mpz_mod(temp, temp, mod); + } + mpz_mul(base_power, base_power, base_power); + mpz_mod(base_power, base_power, mod); + mpz_fdiv_q_2exp(exp_bits, exp_bits, 1); + } + + mpz_clear(exp_bits); + mpz_clear(base_power); + reduce_mpz(temp); + mpz_clear(dest); + copyref_mpz(dest, temp); +} + +/**************** Legendre symbol ****************/ + +int mpz_legendre(a, p) + const mpz_t a; + const mpz_t p; +{ + int x; + mpz_t exp; + mpz_t pow; + mpz_init(exp); + mpz_init(pow); + + mpz_set(exp, p); + mpz_sub_ui(exp, exp, 1); + mpz_fdiv_q_2exp(exp, exp, 1); + mpz_powm(pow, a, exp, p); + + if (pow->size == 1 && IDX(pow, 0) == 1) + x = 1; + else if (pow->size == 0) + x = 0; + else + x = -1; + + mpz_clear(exp); + mpz_clear(pow); + + return x; +} + +/**************** GCD ****************/ + +static void mpz_gcdext_main(g, ai, bi, a, b) + mpz_t g; + mpz_t ai; + mpz_t bi; + const mpz_t a; + const mpz_t b; +{ + mpz_t rem_last, rem_cur; + mpz_t ai_last, ai_cur; + mpz_t bi_last, bi_cur; + mpz_t q, temp; + + mpz_init(rem_last); + mpz_init(rem_cur); + mpz_init(q); + + mpz_set(rem_last, a); + mpz_set(rem_cur, b); + + if (ai) { + mpz_init(ai_last); + mpz_init(ai_cur); + mpz_set_ui(ai_last, 1); + } + + if (bi) { + mpz_init(bi_last); + mpz_init(bi_cur); + mpz_set_ui(bi_cur, 1); + } + + assert(a->sign == 1 && a->size > 0); + assert(b->sign == 1 && b->size > 0); + + while (1) { + mpz_fdiv_qr(q, rem_last, rem_last, rem_cur); + mpz_swap(rem_last, rem_cur); + if (!mpz_sgn(rem_cur)) + break; + + if (ai) { + mpz_init(temp); + mpz_mul(temp, q, ai_cur); + mpz_sub(temp, ai_last, temp); + mpz_clear(ai_last); + copyref_mpz(ai_last, ai_cur); + copyref_mpz(ai_cur, temp); + } + + if (bi) { + mpz_init(temp); + mpz_mul(temp, q, bi_cur); + mpz_sub(temp, bi_last, temp); + mpz_clear(bi_last); + copyref_mpz(bi_last, bi_cur); + copyref_mpz(bi_cur, temp); + } + } + + mpz_clear(g); + copyref_mpz(g, rem_last); + mpz_clear(rem_cur); + mpz_clear(q); + + if (ai) { + mpz_clear(ai); + copyref_mpz(ai, ai_cur); + mpz_clear(ai_last); + } + if (bi) { + mpz_clear(bi); + copyref_mpz(bi, bi_cur); + mpz_clear(bi_last); + } +} + +void mpz_gcdext(g, ai, bi, a, b) + mpz_t g; + mpz_t ai; + mpz_t bi; + const mpz_t a; + const mpz_t b; +{ + if (mpz_cmpabs(a, b) > 0) + mpz_gcdext_main(g, ai, bi, a, b); + else + mpz_gcdext_main(g, bi, ai, b, a); +} + +/**************** Output ****************/ + +#define PUTCH(bbb, sss, nnn, ccc) do { \ + if ((sss) > 1) { \ + *(bbb) = (ccc); \ + (bbb)++; \ + (sss)--; \ + } \ + (nnn)++; \ + } while (0) + +static int putnum(char** buf, size_t* size, unsigned long value, + unsigned int base) +{ + unsigned long s; + unsigned int d; + int count = 0; + + if (value == 0) { + PUTCH(*buf, *size, count, '0'); + } + else { + s = value / base; + if (s) + count = putnum(buf, size, value / base, base); + d = value % base; + if (d < 10) + PUTCH(*buf, *size, count, d + '0'); + else + PUTCH(*buf, *size, count, d + 'A' - 10); + } + + return count; +} + +/* Supported conversions: + %% %s %c %d %i %o %u %X %ld %li %lo %lu %lX %ZX + */ + +int rs_vsnprintf(char* buf, size_t size, const char* fmt, va_list ap) +{ + int count = 0; + int argtype, convtype; + const char* strval; + long longval; + struct _mpz *mpval; + limb_t v, d; + size_t i; + int j; + + while (fmt[0]) { + if (fmt[0] != '%') { + PUTCH(buf, size, count, fmt[0]); + fmt++; + } + else if (fmt[1] == '%') { + PUTCH(buf, size, count, '%'); + fmt += 2; + } + else { + if (fmt[1] == 'l' || fmt[1] == 'Z') { + argtype = fmt[1]; + convtype = fmt[2]; + fmt += 3; + } + else { + argtype = 0; + convtype = fmt[1]; + fmt += 2; + } + + if (!convtype) + break; + + if (convtype == 's') { + /* string argument */ + strval = va_arg(ap, const char *); + if (!strval) + strval = "(NULL)"; + while (*strval) { + PUTCH(buf, size, count, *strval); + strval++; + } + } + else if (convtype == 'c' || convtype == 'd' || convtype == 'i' + || convtype == 'u' || convtype == 'x' || convtype == 'X') { + if (argtype == 'Z') { + /* mpz argument -- always print in hexadecimal */ + mpval = va_arg(ap, struct _mpz *); + + if (mpval->sign < 0) + PUTCH(buf, size, count, '-'); + + v = IDX(mpval, mpval->size - 1); + count += putnum(&buf, &size, v, 16); + + for (i = mpval->size - 1; i > 0; i--) { + v = IDX(mpval, i - 1); + for (j = LIMB_BITS - 4; j >= 0; j -= 4) { + d = ((v >> j) & 0x0f); + if (d < 10) + PUTCH(buf, size, count, d + '0'); + else + PUTCH(buf, size, count, d + 'A' - 10); + } + } + } + else { + /* int or long argument */ + if (argtype == 'l') + longval = va_arg(ap, long); + else + longval = va_arg(ap, int); + + if (convtype == 'c') { + PUTCH(buf, size, count, (char) (unsigned char) longval); + } + else if (convtype == 'x' || convtype == 'X') { + count += putnum(&buf, &size, longval, 16); + } + else if (convtype == 'o') { + count += putnum(&buf, &size, longval, 8); + } + else if (convtype == 'u' || longval >= 0) { + count += putnum(&buf, &size, longval, 10); + } + else { + PUTCH(buf, size, count, '-'); + count += putnum(&buf, &size, -longval, 10); + } + } + } + else { + fprintf(stderr, "*** ERROR: mpz: unsupported conversion '%%%c'", + convtype); + if (argtype) + PUTCH(buf, size, count, argtype); + PUTCH(buf, size, count, convtype); + } + } + } + + if (size != 0) + *buf = 0; + + return count; +} + +int rs_snprintf(char* buf, size_t size, const char* fmt, ...) +{ + va_list ap; + int count; + + va_start(ap, fmt); + count = rs_vsnprintf(buf, size, fmt, ap); + va_end(ap); + + return count; +} + diff --git a/tool/rabbitsign-src/mpz.h b/tool/rabbitsign-src/mpz.h new file mode 100644 index 0000000..2eb2667 --- /dev/null +++ b/tool/rabbitsign-src/mpz.h @@ -0,0 +1,123 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __RABBITSIGN_MPZ_H__ +#define __RABBITSIGN_MPZ_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#if (SIZEOF_INT != 0) && (SIZEOF_LONG >= 2 * SIZEOF_INT) +typedef unsigned int limb_t; +typedef unsigned long double_limb_t; +typedef signed long signed_double_limb_t; +#else +# if (SIZEOF_SHORT != 0) && (SIZEOF_INT >= 2 * SIZEOF_SHORT) +typedef unsigned short limb_t; +typedef unsigned int double_limb_t; +typedef signed int signed_double_limb_t; +# else +typedef unsigned short limb_t; +typedef unsigned long double_limb_t; +typedef signed long signed_double_limb_t; +# endif +#endif + +#define LIMB_BITS (sizeof(limb_t)*8) +#define LIMB_BYTES (sizeof(limb_t)) +#define LIMB_MASK ((((double_limb_t) 1) << LIMB_BITS) - 1) + +struct _mpz { + size_t size; + size_t size_alloc; + limb_t* m; + int sign; +}; + +typedef struct _mpz mpz_t[1]; + +#undef __P +#ifdef PROTOTYPES +# define __P(x) x +#else +# define __P(x) () +#endif + +void mpz_init __P((mpz_t x)); +void mpz_clear __P((mpz_t x)); + +/* Set */ +void mpz_set __P((mpz_t dest, const mpz_t src)); +void mpz_set_ui __P((mpz_t dest, unsigned int a)); +unsigned int mpz_get_ui __P((const mpz_t a)); + +/* Import/export: assume order=-1, size=1, endian=0, nails=0 */ +void mpz_import __P((mpz_t dest, size_t count, int order, int size, + int endian, size_t nails, const void* op)); +void mpz_export __P((void* dest, size_t* count, int order, int size, + int endian, size_t nails, const mpz_t op)); + +/* Check sign */ +int mpz_sgn __P((const mpz_t a)); + +/* Compare */ +int mpz_cmp __P((const mpz_t a, const mpz_t b)); +int mpz_cmp_ui __P((const mpz_t a, unsigned int b)); + +/* Add */ +void mpz_add __P((mpz_t dest, const mpz_t a, const mpz_t b)); +void mpz_add_ui __P((mpz_t dest, const mpz_t a, unsigned int b)); + +/* Subtract */ +void mpz_sub __P((mpz_t dest, const mpz_t a, const mpz_t b)); +void mpz_sub_ui __P((mpz_t dest, const mpz_t a, unsigned int b)); + +/* Multiply */ +void mpz_mul __P((mpz_t dest, const mpz_t a, const mpz_t b)); +void mpz_mul_ui __P((mpz_t dest, const mpz_t a, unsigned int b)); + +/* Divide: requires b <= LIMB_BITS */ +void mpz_fdiv_q_2exp __P((mpz_t dest, const mpz_t a, unsigned int b)); + +/* Modulus */ +void mpz_mod __P((mpz_t dest, const mpz_t a, const mpz_t mod)); + +/* Modular exponent */ +void mpz_powm __P((mpz_t dest, const mpz_t base, const mpz_t exp, + const mpz_t mod)); + +/* Legendre symbol */ +int mpz_legendre __P((const mpz_t a, const mpz_t p)); + +/* Extended GCD */ +void mpz_gcdext __P((mpz_t g, mpz_t ai, mpz_t bi, + const mpz_t a, const mpz_t b)); + +/* Output */ +int rs_snprintf __P((char* buf, size_t size, const char* fmt, ...)); + +#ifdef va_start +int rs_vsnprintf __P((char* buf, size_t size, const char* fmt, va_list ap)); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tool/rabbitsign-src/os8x.c b/tool/rabbitsign-src/os8x.c new file mode 100644 index 0000000..b5e9ced --- /dev/null +++ b/tool/rabbitsign-src/os8x.c @@ -0,0 +1,298 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" +#include "md5.h" + +/* + * Check/fix OS header fields and data. + * + * The OS header is much simpler than an application header, and its + * correctness is not as crucial to validation. The most important + * parts of the OS header are the key ID and (for newer calculators) + * the hardware compatibility level. There is no date stamp required. + * The page count is not required, and if present, is used only to + * display the transfer percentage (when using the 84+ boot code.) + * + * TI only sets the OS and program image size fields in their TI-73 OS + * headers. (Bizarrely, they are set in the true OS header, but not + * in the fake OS header that is transferred to page 1A. Furthermore, + * the OS size field is incorrect.) In any case, these fields appear + * to be ignored by all versions of the boot code. + */ +int rs_repair_ti8x_os(RSProgram* os, /* OS */ + unsigned int flags) /* flags */ +{ + unsigned long hdrstart, hdrsize, fieldhead, fieldstart, + fieldsize, ossize; + unsigned char* hdr; + int i; + + /* Pad the OS to a multiple of 16384. (While strictly speaking we + could get away with only padding each page to a multiple of 256, + such "partial OSes" are not supported by most linking + software.) */ + + rs_program_set_length(os, ((os->length + 0x3fff) & ~0x3fff)); + + /* If no OS header was provided in the input, try to get a header + from page 1A instead */ + + if (os->header_length < 6 + || os->header[0] != 0x80 + || os->header[1] != 0x0f) { + for (i = 0; i < os->npagenums; i++) { + if (os->pagenums[i] == 0x1a) { + rs_free(os->header); + if (!(os->header = rs_malloc(256))) + return RS_ERR_OUT_OF_MEMORY; + memcpy(os->header, os->data + ((unsigned long) i << 14), 256); + os->header_length = 256; + break; + } + } + } + + /* Clear old header/signature (not done on the TI-73 because + official TI-73 OSes contain a fake header; I don't recommend + doing this for third-party OSes) */ + + if (os->calctype != RS_CALC_TI73) + for (i = 0; i < os->npagenums; i++) + if (os->pagenums[i] == 0x1a) + memset(os->data + ((unsigned long) i << 14), 0xff, 512); + + /* Fix header size. OS headers must always begin with an 800x field + and end with an 807x field (TI always uses 800F and 807F, as for + apps; I'm not sure whether it's required.) */ + + if (os->header_length < 6 + || os->header[0] != 0x80 + || (os->header[1] & 0xf0) != 0) { + rs_error(NULL, os, "no OS header found"); + return RS_ERR_MISSING_HEADER; + } + + rs_get_field_size(os->header, &hdrstart, NULL); + hdr = os->header + hdrstart; + hdrsize = os->header_length - hdrstart; + + if (rs_find_app_field(0x8070, hdr, hdrsize, + &fieldhead, &fieldstart, &fieldsize)) { + rs_error(NULL, os, "OS header has no program image field"); + return RS_ERR_MISSING_PROGRAM_IMAGE; + } + + hdrsize = fieldstart; + os->header_length = hdrstart + hdrsize; + + if ((os->header_length % 64) == 55) { + if (flags & RS_IGNORE_ALL_WARNINGS) { + rs_warning(NULL, os, "OS header has length 55 mod 64"); + rs_warning(NULL, os, "(this will fail to validate on TI-83+ BE)"); + } + else { + rs_error(NULL, os, "OS header has length 55 mod 64"); + rs_error(NULL, os, "(this will fail to validate on TI-83+ BE)"); + return RS_ERR_INVALID_PROGRAM_SIZE; + } + } + + /* Fix OS / OS image sizes if requested */ + + if (flags & RS_FIX_OS_SIZE) { + ossize = os->length + hdrsize; + if (rs_set_field_size(os->header, ossize)) { + rs_error(NULL, os, "cannot set OS length"); + return RS_ERR_FIELD_TOO_SMALL; + } + + if (rs_set_field_size(hdr + fieldhead, os->length)) { + rs_error(NULL, os, "cannot set OS image length"); + return RS_ERR_FIELD_TOO_SMALL; + } + } + + /* Check for key ID */ + + if (rs_find_app_field(0x8010, hdr, hdrsize, NULL, NULL, NULL)) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, os, "OS header has no key ID field"); + else { + rs_error(NULL, os, "OS header has no key ID field"); + return RS_ERR_MISSING_KEY_ID; + } + } + + /* Check/fix page count */ + + if (rs_find_app_field(0x8080, hdr, hdrsize, + NULL, &fieldstart, &fieldsize)) { + if (os->length != 14 * 0x4000L) { + rs_warning(NULL, os, "OS header has no page count field"); + } + } + else if (fieldsize != 1) { + rs_warning(NULL, os, "OS header has an invalid page count field"); + } + else if (flags & RS_FIX_PAGE_COUNT) { + hdr[fieldstart] = os->length >> 14; + } + else if (hdr[fieldstart] != (os->length >> 14)) { + rs_warning(NULL, os, "OS header has an incorrect page count field"); + } + + /* Check and reset validation flag bytes */ + + if (os->data[0x56] != 0xff && os->data[0x56] != 0x5a) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, os, "OS has invalid data at 0056h"); + else { + rs_error(NULL, os, "OS has invalid data at 0056h"); + return RS_ERR_INVALID_PROGRAM_DATA; + } + } + + if (os->data[0x56] == 0x5a) + os->data[0x56] = 0xff; + + if (os->data[0x57] != 0xff && os->data[0x57] != 0xa5) { + if (flags & RS_IGNORE_ALL_WARNINGS) + rs_warning(NULL, os, "OS has invalid data at 0057h"); + else { + rs_error(NULL, os, "OS has invalid data at 0057h"); + return RS_ERR_INVALID_PROGRAM_DATA; + } + } + + if (os->data[0x57] == 0xff) + os->data[0x57] = 0xa5; + + return RS_SUCCESS; +} + +/* + * Compute signature for an OS. + */ +int rs_sign_ti8x_os(RSProgram* os, /* OS */ + RSKey* key) /* signing key */ +{ + struct md5_ctx ctx; + md5_uint32 hash[4]; + mpz_t hashv, sigv; + unsigned char sigdata[512]; + size_t siglength; + int e; + + md5_init_ctx(&ctx); + md5_process_bytes(os->header, os->header_length, &ctx); + md5_process_bytes(os->data, os->length, &ctx); + md5_finish_ctx(&ctx, hash); + + mpz_init(hashv); + mpz_init(sigv); + + mpz_import(hashv, 16, -1, 1, 0, 0, hash); + rs_message(2, NULL, os, "hash = %ZX", hashv); + + if ((e = rs_sign_rsa(sigv, hashv, key))) { + mpz_clear(hashv); + mpz_clear(sigv); + return e; + } + + rs_message(2, NULL, os, "sig = %ZX", sigv); + + sigdata[0] = 0x02; + sigdata[1] = 0x0d; + mpz_export(sigdata + 3, &siglength, -1, 1, 0, 0, sigv); + sigdata[2] = siglength & 0xff; + siglength += 3; + + while (siglength < 96) + sigdata[siglength++] = 0xff; + + rs_free(os->signature); + if (!(os->signature = rs_malloc(siglength))) + return RS_ERR_OUT_OF_MEMORY; + + memcpy(os->signature, sigdata, siglength); + os->signature_length = siglength; + return RS_SUCCESS; +} + +/* + * Validate OS signature. + */ +int rs_validate_ti8x_os(const RSProgram* os, + const RSKey* key) +{ + unsigned long fieldstart, fieldsize; + struct md5_ctx ctx; + md5_uint32 hash[4]; + mpz_t hashv, sigv; + int e; + + if (os->signature_length < 3) { + rs_error(NULL, os, "OS does not have a signature"); + return RS_ERR_MISSING_RSA_SIGNATURE; + } + + if (os->signature[0] != 0x02 || (os->signature[1] & 0xf0) != 0x00) { + rs_error(NULL, os, "OS does not have an RSA signature"); + return RS_ERR_MISSING_RSA_SIGNATURE; + } + rs_get_field_size(os->signature, &fieldstart, &fieldsize); + + md5_init_ctx(&ctx); + md5_process_bytes(os->header, os->header_length, &ctx); + md5_process_bytes(os->data, os->length, &ctx); + md5_finish_ctx(&ctx, hash); + + mpz_init(hashv); + mpz_init(sigv); + + mpz_import(hashv, 16, -1, 1, 0, 0, hash); + rs_message(2, NULL, os, "hash = %ZX", hashv); + + mpz_import(sigv, fieldsize, -1, 1, 0, 0, os->signature + fieldstart); + rs_message(2, NULL, os, "sig = %ZX", sigv); + + e = rs_validate_rsa(sigv, hashv, key); + if (e == RS_SIGNATURE_INCORRECT) + rs_message(0, NULL, os, "OS signature incorrect"); + + mpz_clear(hashv); + mpz_clear(sigv); + return e; +} diff --git a/tool/rabbitsign-src/output.c b/tool/rabbitsign-src/output.c new file mode 100644 index 0000000..6004b9b --- /dev/null +++ b/tool/rabbitsign-src/output.c @@ -0,0 +1,39 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "rabbitsign.h" + +/* + * Write program contents to a file. + */ +int rs_write_program_file(const RSProgram* prgm, FILE* f, + int month, int day, int year, + unsigned int flags) +{ + if (rs_calc_is_ti8x(prgm->calctype) + && (prgm->datatype == RS_DATA_OS || prgm->datatype == RS_DATA_APP)) + return rs_write_ti8x_file(prgm, f, month, day, year, flags); + else + return rs_write_ti9x_file(prgm, f, month, day, year, flags); +} diff --git a/tool/rabbitsign-src/output8x.c b/tool/rabbitsign-src/output8x.c new file mode 100644 index 0000000..d08f061 --- /dev/null +++ b/tool/rabbitsign-src/output8x.c @@ -0,0 +1,248 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +/* + * Write a single record to an Intel hex file. + */ +static int write_hex_record(FILE* outfile, /* output file */ + unsigned int nbytes, /* number of bytes */ + unsigned int addr, /* address */ + unsigned int type, /* record type */ + unsigned char* data, /* data */ + unsigned int flags, /* flags */ + int final) +{ + char buf[256]; + unsigned int i; + unsigned int sum; + + sum = nbytes + addr + (addr >> 8) + type; + sprintf(buf, ":%02X%04X%02X", nbytes, addr, type); + + for (i = 0; i < nbytes; i++) { + sprintf(buf + 9 + 2 * i, "%02X", data[i]); + sum += data[i]; + } + + sum = ((-sum) & 0xff); + + sprintf(buf + 9 + 2 * i, "%02X", sum); + + if (!final) { + if (flags & RS_OUTPUT_APPSIGN) + strcpy(buf + 11 + 2 * i, "\n"); + else + strcpy(buf + 11 + 2 * i, "\r\n"); + } + + if (fputs(buf, outfile) == EOF) { + rs_error(NULL, NULL, "file I/O error"); + return RS_ERR_FILE_IO; + } + + return RS_SUCCESS; +} + +/* + * Write a chunk of data to an Intel hex file. + */ +static int write_hex_data(FILE* outfile, /* output file */ + unsigned long length, /* number of bytes */ + unsigned long addr, /* starting address */ + unsigned char* data, /* data */ + unsigned int flags) +{ + unsigned int count; + int e; + + while (length > 0) { + if (length < 0x20) + count = length; + else + count = 0x20; + + if ((e = write_hex_record(outfile, count, addr, 0, data, flags, 0))) + return e; + + length -= count; + addr += count; + data += count; + } + + return RS_SUCCESS; +} + +/* + * Write program to a .73k/.73u/.8xk/.8xu or .app file. + * + * If month = day = year = 0, use the current time. + * + * Note: on platforms where it matters, all output files must be + * opened in "binary" mode. + */ +int rs_write_ti8x_file(const RSProgram* prgm, /* program */ + FILE* outfile, /* output file */ + int month, /* timestamp month */ + int day, /* timestamp day */ + int year, /* timestamp year*/ + unsigned int flags) /* flags */ +{ + const unsigned char *hdr; + unsigned long hdrstart, hdrsize, fieldstart, fieldsize; + int major, minor, i; + unsigned long npages, nrecords, hexsize; + char name[9]; + unsigned int pagenum, addr; + unsigned long count; + unsigned char pnbuf[2]; + int e; + + if (!(flags & RS_OUTPUT_HEX_ONLY)) { + if (prgm->header_length) { + hdr = prgm->header; + hdrsize = prgm->header_length; + } + else { + hdr = prgm->data; + hdrsize = prgm->length; + } + + if (hdrsize >= 6) { + rs_get_field_size(hdr, &hdrstart, NULL); + hdr += hdrstart; + hdrsize -= hdrstart; + if (hdrsize > 128) + hdrsize = 128; + + major = rs_get_numeric_field(0x8020, hdr, hdrsize); + minor = rs_get_numeric_field(0x8030, hdr, hdrsize); + + if (prgm->datatype == RS_DATA_OS) { + if (prgm->calctype == RS_CALC_TI73) + strcpy(name, "BASECODE"); + else + strcpy(name, "basecode"); + } + else if (!rs_find_app_field(0x8040, hdr, hdrsize, + NULL, &fieldstart, &fieldsize)) { + if (fieldsize > 8) + fieldsize = 8; + strncpy(name, (char*) hdr + fieldstart, fieldsize); + name[fieldsize] = 0; + } + else { + name[0] = 0; + } + } + else { + major = minor = 0; + name[0] = 0; + } + + npages = ((prgm->length + 0x3fff) >> 14); + nrecords = 1 + npages + ((prgm->length + 0x1f) >> 5); + + if (prgm->header_length) + nrecords += 1 + ((prgm->header_length + 0x1f) >> 5); + if (prgm->signature_length) + nrecords += 1 + ((prgm->signature_length + 0x1f) >> 5); + + if (flags & RS_OUTPUT_APPSIGN) { + hexsize = (npages * 4 + + prgm->length * 2 + + prgm->header_length * 2 + + prgm->signature_length * 2 + + nrecords * 12 - 1); + } + else { + hexsize = (npages * 4 + + prgm->length * 2 + + prgm->header_length * 2 + + prgm->signature_length * 2 + + nrecords * 13 - 2); + } + + if ((e = rs_write_tifl_header(outfile, 1, major, minor, + month, day, year, name, + prgm->calctype, prgm->datatype, + hexsize))) + return e; + } + + if (prgm->header_length) { + if ((e = write_hex_data(outfile, prgm->header_length, 0, + prgm->header, flags))) + return e; + if ((e = write_hex_record(outfile, 0, 0, 1, NULL, flags, 0))) + return e; + } + + for (i = 0; ((unsigned long) i << 14) < prgm->length; i++) { + if (i < prgm->npagenums) + pagenum = prgm->pagenums[i]; + else + pagenum = i; + + if (pagenum == 0 && prgm->header_length) + addr = 0; + else + addr = 0x4000; + + pnbuf[0] = (pagenum >> 8) & 0xff; + pnbuf[1] = pagenum & 0xff; + + if ((e = write_hex_record(outfile, 2, 0, 2, pnbuf, flags, 0))) + return e; + + count = prgm->length - i * 0x4000; + if (count > 0x4000) + count = 0x4000; + + if ((e = write_hex_data(outfile, count, addr, + prgm->data + i * 0x4000, flags))) + return e; + } + + if (prgm->signature_length) { + if ((e = write_hex_record(outfile, 0, 0, 1, NULL, flags, 0))) + return e; + if ((e = write_hex_data(outfile, prgm->signature_length, 0, + prgm->signature, flags))) + return e; + } + + return write_hex_record(outfile, 0, 0, 1, NULL, flags, 1); +} + diff --git a/tool/rabbitsign-src/output9x.c b/tool/rabbitsign-src/output9x.c new file mode 100644 index 0000000..d765abe --- /dev/null +++ b/tool/rabbitsign-src/output9x.c @@ -0,0 +1,96 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +/* + * Write program to a .89k/.89u/.9xk/.9xu file. + * + * If month = day = year = 0, use the current time. + * + * Note: on platforms where it matters, all output files must be + * opened in "binary" mode. + */ +int rs_write_ti9x_file(const RSProgram* prgm, /* program */ + FILE* outfile, /* output file */ + int month, /* timestamp month */ + int day, /* timestamp day */ + int year, /* timestamp year*/ + unsigned int flags RS_ATTR_UNUSED) +{ + const unsigned char *hdr; + unsigned long hdrstart, hdrsize, fieldstart, fieldsize; + char name[9]; + int e; + + if (prgm->length >= 6) { + rs_get_field_size(prgm->data, &hdrstart, &hdrsize); + hdr = prgm->data + hdrstart; + if (hdrsize > 128) + hdrsize = 128; + + if (prgm->datatype == RS_DATA_OS) { + strcpy(name, "basecode"); + } + else if (!rs_find_app_field(0x8140, hdr, hdrsize, + NULL, &fieldstart, &fieldsize)) { + if (fieldsize > 8) + fieldsize = 8; + strncpy(name, (char*) hdr + fieldstart, fieldsize); + name[fieldsize] = 0; + } + else { + name[0] = 0; + } + } + else { + name[0] = 0; + } + + /* Note: the "version" header fields used in TI's 68k apps and + OSes seem to have no relation to the actual version numbers. */ + + if ((e = rs_write_tifl_header(outfile, 0, 0, 0, + month, day, year, name, + prgm->calctype, prgm->datatype, + prgm->length))) + return e; + + if (fwrite(prgm->data, 1, prgm->length, outfile) != prgm->length) { + rs_error(NULL, NULL, "file I/O error"); + return RS_ERR_FILE_IO; + } + + return RS_SUCCESS; +} + diff --git a/tool/rabbitsign-src/program.c b/tool/rabbitsign-src/program.c new file mode 100644 index 0000000..e7cf140 --- /dev/null +++ b/tool/rabbitsign-src/program.c @@ -0,0 +1,187 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +/* + * Create a new program. + */ +RSProgram* rs_program_new() +{ + RSProgram* prgm = rs_malloc(sizeof(RSProgram)); + + if (!prgm) + return NULL; + + prgm->filename = NULL; + prgm->calctype = 0; + prgm->datatype = 0; + prgm->data = NULL; + prgm->length = 0; + prgm->length_a = 0; + prgm->header = NULL; + prgm->header_length = 0; + prgm->signature = NULL; + prgm->signature_length = 0; + prgm->pagenums = NULL; + prgm->npagenums = 0; + + return prgm; +} + +/* + * Create a new program by wrapping an existing data buffer. + * + * If buffer_size is zero, data will be copied from the source buffer + * into the new program. + * + * If buffer_size is nonzero, then the source buffer must have been + * allocated using malloc(); buffer_size is the total amount of memory + * allocated. The data will not be copied, and the new program object + * assumes ownership of the buffer. + */ +RSProgram* rs_program_new_with_data(RSCalcType ctype, /* calc type */ + RSDataType dtype, /* data type */ + void* data, /* source buffer */ + unsigned long length, /* length of data */ + unsigned long buffer_size) /* amount of + memory + allocated */ +{ + RSProgram* prgm = rs_program_new(); + + if (!prgm) + return NULL; + + prgm->calctype = ctype; + prgm->datatype = dtype; + + if (data) { + if (buffer_size) { + prgm->data = data; + prgm->length = length; + prgm->length_a = buffer_size; + } + else { + if (rs_program_append_data(prgm, data, length)) { + rs_program_free(prgm); + return NULL; + } + } + } + + return prgm; +} + +/* + * Free program data. + */ +void rs_program_free(RSProgram* prgm) +{ + if (!prgm) + return; + + rs_free(prgm->filename); + rs_free(prgm->data); + rs_free(prgm->header); + rs_free(prgm->signature); + rs_free(prgm->pagenums); + rs_free(prgm); +} + +/* + * Truncate or extend program. + * + * If length is less than the program's current length, the program is + * truncated. If length is greater than the current size of the + * program, additional space is added. The extra space is padded with + * 0xFF, with the exception of bytes that fall at the start of a page. + */ +int rs_program_set_length(RSProgram* prgm, /* program */ + unsigned long length) /* new length of program */ +{ + unsigned long length_a, i; + unsigned char* dptr; + + if (length <= prgm->length) { + prgm->length = length; + return RS_SUCCESS; + } + else { + if (length > prgm->length_a) { + length_a = length + 16384; + + dptr = rs_realloc(prgm->data, length_a); + if (!dptr) + return RS_ERR_OUT_OF_MEMORY; + prgm->data = dptr; + prgm->length_a = length_a; + } + + memset(prgm->data + prgm->length, 0xff, length - prgm->length); + + for (i = ((prgm->length + 0x3fff) & ~0x3fff); + i < length; + i += 0x4000) + prgm->data[i] = 0x42; + + prgm->length = length; + return RS_SUCCESS; + } +} + +/* + * Add data to the end of the program. + */ +int rs_program_append_data(RSProgram* prgm, /* program */ + const unsigned char* data, /* data */ + unsigned long length) /* size of data */ +{ + unsigned long nlength, length_a; + unsigned char* dptr; + + nlength = prgm->length + length; + if (nlength > prgm->length_a) { + length_a = nlength + 16384; + + dptr = rs_realloc(prgm->data, length_a); + if (!dptr) + return RS_ERR_OUT_OF_MEMORY; + prgm->data = dptr; + prgm->length_a = length_a; + } + + memcpy(prgm->data + prgm->length, data, length); + prgm->length = nlength; + return RS_SUCCESS; +} diff --git a/tool/rabbitsign-src/rabbitsign.c b/tool/rabbitsign-src/rabbitsign.c new file mode 100644 index 0000000..94877de --- /dev/null +++ b/tool/rabbitsign-src/rabbitsign.c @@ -0,0 +1,463 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STDLIB_H +# include +#endif + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +#if !defined(strcasecmp) && !defined(HAVE_STRCASECMP) +# ifdef HAVE_STRICMP +# define strcasecmp stricmp +# else +# define strcasecmp strcmp +# endif +#endif + +#if !defined(strrchr) && !defined(HAVE_STRRCHR) && defined(HAVE_RINDEX) +# define strrchr rindex +#endif + +static const char* getbasename(const char* f) +{ + const char *p; + + if ((p = strrchr(f, '/'))) + f = p + 1; + +#if defined(__MSDOS__) || defined(__WIN32__) + if ((p = strrchr(f, '\\'))) + f = p + 1; +#endif + + return f; +} + +static const char* usage[]={ + "Usage: %s [options] app-file ...\n", + "Where options may include:\n", + " -a: appsign compatibility mode (Unix-style output)\n", + " -b: read raw binary data (default: auto-detect)\n", + " -c: check existing app signatures rather than signing\n", + " -f: force signing despite errors\n", + " -g: write app in GraphLink (XXk) format\n", + " -k KEYFILE: use specified key file\n", + " -K NUM: use specified key ID (hexadecimal)\n", + " -n: do not alter the app header\n", + " -o OUTFILE: write to specified output file (default is .app\n", + " or .8xk)\n", + " -p: fix the pages field if found\n", + " -P: add an extra page if necessary\n", + " -q: suppress warning messages\n", + " -r: re-sign a previously signed app (i.e. strip off all\n", + " data beyond that indicated by the size header)\n", + " -R R: specify the root to use (0, 1, 2, or 3) (default is 0)\n", + " -t TYPE: specify program type (e.g. 8xk, 73u)\n", + " -u: assume plain hex input is unsorted (default is sorted)\n", + " -v: be verbose (-vv for even more verbosity)\n", + " --help: describe options\n", + " --version: print version info\n", + NULL}; + +int main(int argc, char** argv) +{ + unsigned int flags = (RS_INPUT_SORTED | RS_OUTPUT_HEX_ONLY); + + int rootnum = 0; /* which of the four valid signatures + to generate + + 0 = standard (r,s) + 1 = (-r,s) + 2 = (r,-s) + 3 = (-r,-s) */ + + int rawmode = 0; /* 0 = fix app headers + 1 = sign "raw" data */ + + int valmode = 0; /* 0 = sign apps + 1 = validate apps */ + + const char* infilename; /* file name for input */ + + const char* outfilename = NULL; /* file name for output */ + + const char* keyfilename = NULL; /* file name for key */ + + int verbose = 0; /* -1 = quiet (errors only) + 0 = default (warnings + errors) + 1 = verbose (print file names / status) + 2 = very verbose (details of computation) */ + + static const char optstring[] = "abcfgk:K:no:pPqrR:t:uv"; + const char *progname; + int i, j, c, e; + const char* arg; + char *tempname; + + FILE* infile; + FILE* outfile; + RSKey* key; + RSProgram* prgm; + unsigned long keyid = 0, appkeyid; + RSCalcType ctype = RS_CALC_UNKNOWN; + RSDataType dtype = RS_DATA_UNKNOWN; + + char *ptr; + const char *ext; + int invalidapps = 0; + + progname = getbasename(argv[0]); + rs_set_progname(progname); + + if (argc == 1) { + fprintf(stderr, usage[0], progname); + for (i = 1; usage[i]; i++) + fputs(usage[i], stderr); + return 5; + } + + i = j = 1; + while ((c = rs_parse_cmdline(argc, argv, optstring, &i, &j, &arg))) { + switch (c) { + case RS_CMDLINE_HELP: + printf(usage[0], progname); + for (i = 1; usage[i]; i++) + fputs(usage[i], stdout); + return 0; + + case RS_CMDLINE_VERSION: + printf("rabbitsign\n"); + fputs("Copyright (C) 2009 Benjamin Moody\n", stdout); + fputs("This program is free software. ", stdout); + fputs("There is NO WARRANTY of any kind.\n", stdout); + return 0; + + case 'o': + outfilename = arg; + break; + + case 'k': + keyfilename = arg; + break; + + case 'K': + if (!sscanf(arg, "%lx", &keyid)) { + fprintf(stderr, "%s: -K: invalid argument %s\n", progname, arg); + return 5; + } + break; + + case 'b': + flags |= RS_INPUT_BINARY; + break; + + case 'u': + flags &= ~RS_INPUT_SORTED; + break; + + case 'f': + flags |= RS_IGNORE_ALL_WARNINGS; + break; + + case 'g': + flags &= ~RS_OUTPUT_HEX_ONLY; + break; + + case 'a': + flags |= RS_OUTPUT_APPSIGN; + break; + + case 'R': + if (!sscanf(arg, "%d", &rootnum)) { + fprintf(stderr, "%s: -R: invalid argument %s\n", progname, arg); + return 5; + } + break; + + case 't': + if (rs_suffix_to_type(arg, &ctype, &dtype)) { + fprintf(stderr, "%s: unrecognized file type %s\n", progname, arg); + return 5; + } + break; + + case 'n': + rawmode = 1; + break; + + case 'r': + flags |= RS_REMOVE_OLD_SIGNATURE; + break; + + case 'P': + flags |= RS_ZEALOUSLY_PAD_APP; + break; + + case 'p': + flags |= RS_FIX_PAGE_COUNT; + break; + + case 'c': + valmode = 1; + break; + + case 'v': + verbose++; + break; + + case 'q': + verbose--; + break; + + case RS_CMDLINE_FILENAME: + break; + + case RS_CMDLINE_ERROR: + return 5; + + default: + fprintf(stderr, "%s: internal error: unknown option -%c\n", + progname, c); + abort(); + } + } + + rs_set_verbose(verbose); + + if (outfilename && (ptr = strrchr(outfilename, '.')) + && !rs_suffix_to_type(ptr + 1, NULL, NULL)) + flags &= ~RS_OUTPUT_HEX_ONLY; + + + /* Read key file (if manually specified) */ + + key = rs_key_new(); + + if (keyfilename) { + infile = fopen(keyfilename, "rb"); + if (!infile) { + perror(keyfilename); + rs_key_free(key); + return 3; + } + if (rs_read_key_file(key, infile, keyfilename, 1)) { + fclose(infile); + rs_key_free(key); + return 3; + } + fclose(infile); + } + else if (keyid) { + if (rs_key_find_for_id(key, keyid, valmode)) { + rs_key_free(key); + return 3; + } + } + + /* Process applications */ + + i = j = 1; + while ((c = rs_parse_cmdline(argc, argv, optstring, &i, &j, &arg))) { + if (c != RS_CMDLINE_FILENAME) + continue; + + /* Read input file */ + + if (strcmp(arg, "-")) { + infilename = arg; + infile = fopen(arg, "rb"); + if (!infile) { + perror(arg); + rs_key_free(key); + return 4; + } + } + else { + infilename = "(standard input)"; + infile = stdin; + } + + prgm = rs_program_new(); + + if (ctype && dtype) { + prgm->calctype = ctype; + prgm->datatype = dtype; + } + else if ((ptr = strrchr(infilename, '.'))) { + rs_suffix_to_type(ptr + 1, &prgm->calctype, &prgm->datatype); + } + + if (rs_read_program_file(prgm, infile, infilename, flags)) { + rs_program_free(prgm); + rs_key_free(key); + if (infile != stdin) + fclose(infile); + return 4; + } + if (infile != stdin) + fclose(infile); + + /* Read key file (if automatic) */ + + if (!keyfilename && !keyid) { + appkeyid = rs_program_get_key_id(prgm); + if (!appkeyid) { + fprintf(stderr, "%s: unable to determine key ID\n", infilename); + rs_program_free(prgm); + rs_key_free(key); + return 3; + } + + if (appkeyid != key->id) { + if (rs_key_find_for_id(key, appkeyid, valmode)) { + rs_program_free(prgm); + rs_key_free(key); + return 3; + } + } + } + + if (valmode) { + /* Validate application */ + if (verbose > 0) + fprintf(stderr, "Validating %s %s %s...\n", + rs_calc_type_to_string(prgm->calctype), + rs_data_type_to_string(prgm->datatype), + infilename); + + if (rs_validate_program(prgm, key)) + invalidapps++; + } + else { + /* Sign application */ + if (verbose > 0) + fprintf(stderr, "Signing %s %s %s...\n", + rs_calc_type_to_string(prgm->calctype), + rs_data_type_to_string(prgm->datatype), + infilename); + + if (!rawmode) { + if ((e = rs_repair_program(prgm, flags))) { + if (!(flags & RS_IGNORE_ALL_WARNINGS) + && e < RS_ERR_CRITICAL) + fprintf(stderr, "(use -f to override)\n"); + rs_program_free(prgm); + rs_key_free(key); + return 2; + } + } + if (rs_sign_program(prgm, key, rootnum)) { + rs_program_free(prgm); + rs_key_free(key); + return 2; + } + + /* Generate output file name */ + + if (outfilename) { + if (strcmp(outfilename, "-")) { + outfile = fopen(outfilename, "wb"); + if (!outfile) { + perror(outfilename); + rs_program_free(prgm); + rs_key_free(key); + return 4; + } + } + else { + outfile = stdout; + } + } + else if (infile == stdin) { + outfile = stdout; + } + else { + ext = rs_type_to_suffix(prgm->calctype, prgm->datatype, + (flags & RS_OUTPUT_HEX_ONLY)); + + tempname = rs_malloc(strlen(infilename) + 32); + if (!tempname) { + rs_program_free(prgm); + rs_key_free(key); + return 4; + } + strcpy(tempname, infilename); + + ptr = strrchr(tempname, '.'); + if (!ptr) { + strcat(tempname, "."); + strcat(tempname, ext); + } + else if (strcasecmp(ptr + 1, ext)) { + strcpy(ptr + 1, ext); + } + else { + strcpy(ptr, "-signed."); + strcat(ptr, ext); + } + + outfile = fopen(tempname, "wb"); + if (!outfile) { + perror(tempname); + rs_free(tempname); + rs_program_free(prgm); + rs_key_free(key); + return 4; + } + rs_free(tempname); + } + + /* Write signed application to output file */ + + if (rs_write_program_file(prgm, outfile, 0, 0, 0, flags)) { + if (outfile != stdout) + fclose(outfile); + rs_program_free(prgm); + rs_key_free(key); + return 4; + } + + if (outfile != stdout) + fclose(outfile); + } + rs_program_free(prgm); + } + + rs_key_free(key); + + if (invalidapps) + return 1; + else + return 0; +} diff --git a/tool/rabbitsign-src/rabbitsign.h b/tool/rabbitsign-src/rabbitsign.h new file mode 100644 index 0000000..6c2b7b1 --- /dev/null +++ b/tool/rabbitsign-src/rabbitsign.h @@ -0,0 +1,342 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __RABBITSIGN_H__ +#define __RABBITSIGN_H__ + +#ifdef HAVE_GMP_H +# include +# define rs_snprintf gmp_snprintf +# define rs_vsnprintf gmp_vsnprintf +#else +# include "mpz.h" +#endif + +#if __GNUC__ >= 3 +# define RS_ATTR_PURE __attribute__((pure)) +# define RS_ATTR_MALLOC __attribute__((malloc)) +# define RS_ATTR_UNUSED __attribute__((unused)) +# define RS_ATTR_PRINTF(f,i) __attribute__((format(printf,f,i))) +#else +# define RS_ATTR_PURE +# define RS_ATTR_MALLOC +# define RS_ATTR_UNUSED +# define RS_ATTR_PRINTF(f,i) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Calculator types */ +typedef enum _RSCalcType { + RS_CALC_UNKNOWN = 0, + RS_CALC_TI73 = 0x74, + RS_CALC_TI83P = 0x73, + RS_CALC_TI89 = 0x98, + RS_CALC_TI92P = 0x88 +} RSCalcType; + +#define rs_calc_is_ti8x(ttt) ((ttt) == RS_CALC_TI73 || (ttt) == RS_CALC_TI83P) +#define rs_calc_is_ti9x(ttt) ((ttt) == RS_CALC_TI89 || (ttt) == RS_CALC_TI92P) + +/* Data types */ +typedef enum _RSDataType { + RS_DATA_UNKNOWN = 0, + RS_DATA_OS = 0x23, + RS_DATA_APP = 0x24, + RS_DATA_CERT = 0x25 +} RSDataType; + +/* Flags for app signing */ +typedef enum _RSRepairFlags { + RS_IGNORE_ALL_WARNINGS = 1, + RS_REMOVE_OLD_SIGNATURE = 2, /* Remove existing signature */ + RS_FIX_PAGE_COUNT = 4, /* Fix page count header field */ + RS_FIX_OS_SIZE = 8, /* Fix size in OS header */ + RS_ZEALOUSLY_PAD_APP = 16 /* Pad application with an extra + page if necessary */ +} RSRepairFlags; + +/* Flags for file input */ +typedef enum _RSInputFlags { + RS_INPUT_BINARY = 32, /* Assume input is raw binary + data */ + RS_INPUT_SORTED = 64 /* Assume plain hex input is sorted + (implicit page switch) */ +} RSInputFlags; + +/* Flags for file output */ +typedef enum _RSOutputFlags { + RS_OUTPUT_HEX_ONLY = 128, /* Write plain hex (.app) format */ + RS_OUTPUT_APPSIGN = 256 /* Write hex data in + appsign-compatible format */ +} RSOutputFlags; + +/* Encryption key structure */ +typedef struct _RSKey { + char* filename; /* Filename */ + unsigned long id; /* Key ID */ + mpz_t n; /* Modulus (public key) */ + mpz_t p; /* First factor */ + mpz_t q; /* Second factor */ + mpz_t qinv; /* q^-1 mod p (for Rabin) + (rs_sign_rabin() will calculate + this based on p and q, if + needed) */ + mpz_t d; /* Signing exponent (for RSA) + (rs_sign_rsa() will calculate this + based on p and q, if needed) */ +} RSKey; + +/* Program data structure */ +typedef struct _RSProgram { + char* filename; /* Filename */ + RSCalcType calctype; /* Calculator type */ + RSDataType datatype; /* Program data type */ + unsigned char* data; /* Program data */ + unsigned long length; /* Length of program data */ + unsigned long length_a; /* Size of buffer allocated */ + + /* Additional metadata (only used by TI-8x OS) */ + unsigned char* header; /* OS header */ + unsigned int header_length; /* Length of OS header */ + unsigned char* signature; /* OS signature */ + unsigned int signature_length; /* Length of OS signature */ + unsigned int* pagenums; /* List of page numbers */ + int npagenums; /* Number of page numbers */ +} RSProgram; + +/* Status codes */ +typedef enum _RSStatus { + RS_SUCCESS = 0, + + RS_ERR_MISSING_PAGE_COUNT, + RS_ERR_MISSING_KEY_ID, + RS_ERR_MISSING_DATE_STAMP, + RS_ERR_MISSING_PROGRAM_IMAGE, + RS_ERR_MISALIGNED_PROGRAM_IMAGE, + RS_ERR_INVALID_PROGRAM_DATA, + RS_ERR_INVALID_PROGRAM_SIZE, + RS_ERR_INCORRECT_PAGE_COUNT, + RS_ERR_FINAL_PAGE_TOO_LONG, + RS_ERR_FIELD_TOO_SMALL, + + RS_ERR_CRITICAL = 1000, + + RS_ERR_OUT_OF_MEMORY, + RS_ERR_FILE_IO, + RS_ERR_HEX_SYNTAX, + RS_ERR_UNKNOWN_FILE_FORMAT, + RS_ERR_UNKNOWN_PROGRAM_TYPE, + RS_ERR_MISSING_HEADER, + RS_ERR_MISSING_RABIN_SIGNATURE, + RS_ERR_MISSING_RSA_SIGNATURE, + RS_ERR_INCORRECT_PROGRAM_SIZE, + RS_ERR_KEY_NOT_FOUND, + RS_ERR_KEY_SYNTAX, + RS_ERR_INVALID_KEY, + RS_ERR_MISSING_PUBLIC_KEY, + RS_ERR_MISSING_PRIVATE_KEY, + RS_ERR_UNSUITABLE_RABIN_KEY, + RS_ERR_UNSUITABLE_RSA_KEY, + + RS_SIGNATURE_INCORRECT = -1 +} RSStatus; + + +/**** Key handling (keys.c) ****/ + +/* Create a new key. */ +RSKey* rs_key_new (void) RS_ATTR_MALLOC; + +/* Free a key. */ +void rs_key_free (RSKey* key); + +/* Read key from a file. */ +RSStatus rs_read_key_file (RSKey* key, FILE* f, + const char* fname, int verify); + +/* Parse a number written in TI's hexadecimal key format. */ +RSStatus rs_parse_key_value (mpz_t dest, const char* str); + + +/**** Program data manipulation (program.c) ****/ + +/* Create a new program. */ +RSProgram* rs_program_new (void) RS_ATTR_MALLOC; + +/* Create a new program from an existing data buffer. */ +RSProgram* rs_program_new_with_data (RSCalcType ctype, RSDataType dtype, + void* data, unsigned long length, + unsigned long buffer_size) + RS_ATTR_MALLOC; + +/* Free program data. */ +void rs_program_free (RSProgram* prgm); + +/* Truncate or extend program. */ +RSStatus rs_program_set_length (RSProgram* prgm, unsigned long length); + +/* Add data to the end of the program. */ +RSStatus rs_program_append_data (RSProgram* prgm, const unsigned char* data, + unsigned long length); + + +/**** Search for key file (autokey.c) ****/ + +/* Get key ID for the given program. */ +unsigned long rs_program_get_key_id (const RSProgram* prgm) RS_ATTR_PURE; + +/* Find key file for the given ID. */ +RSStatus rs_key_find_for_id (RSKey* key, unsigned long keyid, int publiconly); + + +/**** Program signing and validation (apps.c) ****/ + +/* Check/fix program header and data. */ +RSStatus rs_repair_program (RSProgram* prgm, RSRepairFlags flags); + +/* Add a signature to the program. */ +RSStatus rs_sign_program (RSProgram* prgm, RSKey* key, int rootnum); + +/* Validate program signature. */ +RSStatus rs_validate_program (const RSProgram* prgm, const RSKey* key); + + +/**** TI-73/83+/84+ app signing (app8x.c) ****/ + +/* Check/fix Flash app header and data. */ +RSStatus rs_repair_ti8x_app (RSProgram* app, RSRepairFlags flags); + +/* Add a signature to a Flash app. */ +RSStatus rs_sign_ti8x_app (RSProgram* app, RSKey* key, int rootnum); + +/* Validate Flash app signature. */ +RSStatus rs_validate_ti8x_app (const RSProgram* app, const RSKey* key); + + +/**** TI-73/83+/84+ OS signing (os8x.c) ****/ + +/* Check/fix OS header and data. */ +RSStatus rs_repair_ti8x_os (RSProgram* os, RSRepairFlags flags); + +/* Add a signature to an OS. */ +RSStatus rs_sign_ti8x_os (RSProgram* os, RSKey* key); + +/* Validate OS signature. */ +RSStatus rs_validate_ti8x_os (const RSProgram* os, const RSKey* key); + + +/**** TI-89/92+ app/OS signing (app9x.c) ****/ + +/* Check/fix Flash app header and data. */ +RSStatus rs_repair_ti9x_app (RSProgram* app, RSRepairFlags flags); + +/* Check/fix OS header and data. */ +RSStatus rs_repair_ti9x_os (RSProgram* app, RSRepairFlags flags); + +/* Add a signature to a 68k app/OS. */ +RSStatus rs_sign_ti9x_app (RSProgram* app, RSKey* key); + +/* Validate app/OS signature. */ +RSStatus rs_validate_ti9x_app (const RSProgram* app, const RSKey* key); + +#define rs_sign_ti9x_os rs_sign_ti9x_app +#define rs_validate_ti9x_os rs_validate_ti9x_app + + +/**** File input (input.c) ****/ + +/* Read program contents from a file. */ +RSStatus rs_read_program_file (RSProgram* prgm, FILE* f, + const char* fname, RSInputFlags flags); + + +/**** File output (output.c) ****/ + +/* Write program contents to a file. */ +RSStatus rs_write_program_file(const RSProgram* prgm, FILE* f, + int month, int day, int year, + RSOutputFlags flags); + + +/**** Hex file output (output8x.c) ****/ + +/* Write program to a .73k/.73u/.8xk/.8xu or .app file. */ +RSStatus rs_write_ti8x_file (const RSProgram* prgm, FILE* f, + int month, int day, int year, + RSOutputFlags flags); + + +/**** Binary file output (output9x.c) ****/ + +/* Write program to a .89k/.89u/.9xk/.9xu file. */ +RSStatus rs_write_ti9x_file (const RSProgram* prgm, FILE* f, + int month, int day, int year, + RSOutputFlags flags); + + +/**** App header/certificate utility functions (header.c) ****/ + +/* Get length of a header field. */ +void rs_get_field_size (const unsigned char* data, + unsigned long* fieldstart, + unsigned long* fieldsize); + +/* Set length of a header field. */ +int rs_set_field_size (unsigned char* data, + unsigned long fieldsize); + +/* Find a given header field in the data. */ +int rs_find_app_field (unsigned int type, + const unsigned char* data, + unsigned long length, + unsigned long* fieldhead, + unsigned long* fieldstart, + unsigned long* fieldsize); + +/* Get value of a numeric header field. */ +unsigned long rs_get_numeric_field (unsigned int type, + const unsigned char* data, + unsigned long length) RS_ATTR_PURE; + + +/**** Error/message logging (error.c) ****/ + +typedef void (*RSMessageFunc) (const RSKey*, const RSProgram*, + const char*, void*); + +/* Set program name */ +void rs_set_progname (const char* s); + +/* Set verbosity level */ +void rs_set_verbose (int v); + +/* Set error logging function */ +void rs_set_error_func (RSMessageFunc func, void* data); + +/* Set message logging function */ +void rs_set_message_func (RSMessageFunc func, void* data); + + +#ifdef __cplusplus +} +#endif + +#endif /* __RABBITSIGN_H__ */ diff --git a/tool/rabbitsign-src/rabin.c b/tool/rabbitsign-src/rabin.c new file mode 100644 index 0000000..aaa599c --- /dev/null +++ b/tool/rabbitsign-src/rabin.c @@ -0,0 +1,425 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "rabbitsign.h" +#include "internal.h" + +/* + * Compute square root of x modulo p, where p === 3 (mod 4). + * + * (Assume that (x|p) = 1.) + * + * Notice that: + * + * p = 4k + 3 + * + * x^[(p-1)/2] = x^(2k+1) = (x|p) = 1 + * + * x^(2k+2) = x + * + * [x^(k+1)]^2 = x + * + * so x^(k+1) = x^[(p+1)/4] is a square root of x. + */ +static void mpz_sqrtm_3 (mpz_t res, /* mpz to store result */ + const mpz_t x, /* number to get square root of */ + const mpz_t p) /* prime modulus === 3 (mod 4) */ +{ + mpz_add_ui(res, p, 1); + mpz_fdiv_q_2exp(res, res, 2); /* (p + 1)/4 */ + mpz_powm(res, x, res, p); +} + + +/* + * Compute square root of x modulo p, where p === 5 (mod 8). + * + * (Assume that (x|p) = 1.) + * + * Notice that: + * + * p = 4k + 1 + * + * x^[(p-1)/2] = x^(2k) = (x|p) = 1 + * + * x^[(k+1)/2]^2 * x^(4k-1) = x^(5k) = x^k + * + * Since x^k^2 = 1, x^k = +/- 1. + * + * CASE 1: + * If x^k = 1, x^[(k+1)/2]^2 = x, so x^[(k+1)/2] = x^[(p+3)/8] is + * the square root of x. + * + * CASE 2: + * Otherwise, x^[(k+1)/2]^2 = -x; we need to find a square root of + * -1. + * + * Since (2|p) = -1, 2^[(p-1)/2] = 2^(2k) = -1, so (2^k)^2 = -1 + * + * (x^[(k+1)/2] * 2^k)^2 = -x * -1 = x + * + * so x^[(k+1)/2] * 2^k = x^[(p+3)/8] * 2^[(p-1)/4] is the square + * root of x. + */ +static void mpz_sqrtm_5 (mpz_t res, /* mpz to store result */ + const mpz_t x, /* number to get square root of */ + const mpz_t p) /* prime modulus === 5 (mod 8) */ +{ + mpz_t a, b; + mpz_init(a); + mpz_init(b); + + mpz_add_ui(a, p, 3); + mpz_fdiv_q_2exp(b, a, 3); + mpz_powm(res, x, b, p); /* x ^ (p+3)/8 */ + + /* Check if res^2 = x */ + mpz_mul(a, res, res); + mpz_sub(b, a, x); + mpz_mod(a, b, p); + + if (0 != mpz_sgn(a)) { + mpz_sub_ui(a, p, 1); + mpz_fdiv_q_2exp(b, a, 2); + mpz_set_ui(a, 2); + mpz_powm(a, a, b, p); /* 2 ^ (p-1)/4 */ + mpz_mul(res, res, a); + } + + mpz_clear(a); + mpz_clear(b); +} + + +/* + * Compute square root of x modulo p. + * + * This still won't work with p === 1 mod 8, but then, TI's system + * won't work at all for 50% of apps if one of your factors is 1 mod + * 8. (See the discussion of f values below.) + * + */ +static void mpz_sqrtm (mpz_t res, /* mpz to store result */ + const mpz_t x, /* number to get square root of */ + const mpz_t p) /* prime modulus === 3, 5, or 7 + (mod 8) */ +{ + if ((mpz_get_ui(p) % 8) == 5) + mpz_sqrtm_5(res, x, p); + else + mpz_sqrtm_3(res, x, p); +} + + +/* + * Compute x s.t. x === r (mod p) and x === s (mod q). + * + * We compute this as: + * + * [(r-s) * q^-1 mod p] * q + s + * + */ +static void mpz_crt(mpz_t res, /* mpz to store result */ + const mpz_t r, /* root modulo p */ + const mpz_t s, /* root modulo q */ + const mpz_t p, /* first modulus */ + const mpz_t q, /* second modulus */ + const mpz_t qinv) /* q^(p-2) mod p */ +{ + /* ((r - s) */ + mpz_sub(res, r, s); + + /* * q^-1) */ + mpz_mul(res, res, qinv); + mpz_mod(res, res, p); + + /* * q + s */ + mpz_mul(res, res, q); + mpz_add(res, res, s); +} + +/* + * Compute the T_f transform modulo n. + * + * Because only one quarter of the possible hashes can be signed with + * a given key, we need to transform the hash. First, we want to + * ensure that the result is nonzero, so we shift the hash by 8 bits + * and add a 1 to the end. The resulting number is called m'. + * + * Second, we want to multiply it by a number k whose Legendre symbols + * (k|p) and (k|q) are known, so that (km'|p) = (k|p)(m'|p) = 1 and + * (km'|q) = (k|q)(km'|q) = 1. Since we need both to be true + * simultaneously, regardless of the values of (m'|p) and (m'|q), we + * clearly need four possible values of k. + * + * As it happens, TI's keys all follow a precise format: they all have + * p === 3 and q === 7 (mod 8). As a result, we know that + * + * (-1|p) = (-1|q) = -1 + * + * (2|p) = -1, (2|q) = 1 + * + * So TI has defined the following transformation functions: + * + * T_0(x) = -2x' + * T_1(x) = -x' + * T_2(x) = x' + * T_3(x) = 2x' + * + * where x' = 256x + 1. + * + * In the usual case of p === 3 and q === 7 (mod 8), then, two of the + * possible (T_f(m)|p) will equal 1: + * + * If (m'|p) = 1, then (T_0(m)|p) = (T_2(m)|p) = 1. + * If (m'|p) = -1, then (T_1(m)|p) = (T_3(m)|p) = 1. + * + * Two of the possible (T_f(m)|q) will equal 1: + * + * If (m'|q) = 1, then (T_2(m)|q) = (T_3(m)|q) = 1. + * If (m'|q) = -1, then (T_0(m)|q) = (T_1(m)|q) = 1. + * + * Thus we can choose exactly one f value with + * (T_f(m)|p) = (T_f(m)|q) = 1. + * + * If r === 5 (mod 8) is a prime, (-1|r) = 1, while (2|r) = -1. Thus + * a similar logic holds: + * + * If (m'|r) = 1, then (T_1(m)|r) = (T_2(m)|r) = 1. + * If (m'|r) = -1, then (T_0(m)|r) = (T_3(m)|r) = 1. + * + * So if {p,q} === {3,5}, {5,7}, or {3,7} (mod 8), given any m, we can + * pick an f with (T_f(m)|p) = (T_f(m)|q) = 1. + * + */ +static void applyf(mpz_t res, /* mpz to store result */ + const mpz_t m, /* MD5 hash */ + const mpz_t n, /* public key */ + int f) /* f (0, 1, 2, 3) */ +{ + mpz_mul_ui(res, m, 256); + mpz_add_ui(res, res, 1); + + switch (f) { + case 0: + mpz_add(res, res, res); + case 1: + mpz_sub(res, n, res); + break; + case 2: + break; + case 3: + mpz_add(res, res, res); + break; + } +} + +/* + * Compute the Rabin signature with a given f. + */ +static void rabsigf(mpz_t res, /* mpz to store result */ + const mpz_t m, /* MD5 hash */ + const mpz_t n, /* public key */ + const mpz_t p, /* first factor */ + const mpz_t q, /* second factor */ + const mpz_t qinv, /* q^(p-2) mod p */ + int f, /* f (0, 1, 2, 3) */ + int rootnum) /* root number (0, 1, 2, 3) */ +{ + mpz_t mm; + mpz_t r,s; + + mpz_init(r); + mpz_init(s); + mpz_init(mm); + + applyf(mm, m, n, f); + + mpz_sqrtm(r, mm, p); + mpz_sqrtm(s, mm, q); + + if (rootnum & 1) { + mpz_sub(r, p, r); + } + + if (rootnum & 2) { + mpz_sub(s, q, s); + } + + mpz_crt(res, r, s, p, q, qinv); + + mpz_clear(r); + mpz_clear(s); + mpz_clear(mm); +} + +/* + * Table of f values. + * + * Remember that + * + * f = 0 corresponds to multiplying by -2 + * f = 1 corresponds to multiplying by -1 + * f = 2 corresponds to multiplying by 1 + * f = 3 corresponds to multiplying by 2 + */ +static const int ftab[36] = { + /************* (m'|p) = (m'|q) = 1 */ + /********** (m'|p) = -1, (m'|q) = 1 */ + /****** (m'|p) = 1, (m'|q) = -1 */ + /*** (m'|p) = (m'|q) = -1 */ + + /* p === 3, q === 3 */ + 2, 99, 99,1, /* (-1|p) = (-1|q) = -1 ==> if both -1, multiply by -1 */ + + /* p === 3, q === 5 */ + 2, 1, 0, 3, /* (-1|p) = -1, (-1|q) = 1 ==> if (m'|p) = -1, multiply by -1 */ + /* (-2|p) = 1, (-2|q) = -1 ==> if (m'|q) = -1, multiply by -2 */ + + /* p === 3, q === 7 */ + 2, 3, 0, 1, /* (2|p) = -1, (2|q) = 1 ==> if (m'|p) = -1, multiply by 2 */ + /* (-2|p) = 1, (-2|q) = -1 ==> if (m'|q) = -1, multiply by -2 */ + + /* p === 5, q === 3 */ + 2, 0, 1, 3, + + /* p === 5, q === 5 */ + 2, 99, 99,3, /* (2|p) = (2|q) = -1 ==> if both -1, multiply by 2 */ + + /* p === 5, q === 7 */ + 2, 3, 1, 0, /* (2|p) = -1, (2|q) = 1 ==> if (m'|p) = -1, multiply by 2 */ + /* (-1|p) = 1, (-1|q) = -1 ==> if (m'|q) = -1, multiply by -1 */ + + /* p === 7, q === 3 */ + 2, 0, 3, 1, + + /* p === 7, q === 5 */ + 2, 1, 3, 0, + + /* p === 7, q === 7 */ + 2, 99, 99,1 /* (-1|p) = (-1|q) = -1 ==> if both -1, multiply by -1 */ +}; + +/* + * Compute the Rabin signature and the useful value of f. + */ +int rs_sign_rabin(mpz_t res, /* mpz to store signature */ + int* f, /* f value chosen */ + const mpz_t hash, /* MD5 hash of app */ + int rootnum, /* root number (0, 1, 2, 3) */ + RSKey* key) /* key structure */ +{ + mpz_t mm; + int mLp, mLq; + int pm8, qm8; + + if (!mpz_sgn(key->n)) { + rs_error(key, NULL, "unable to sign: public key missing"); + return RS_ERR_MISSING_PUBLIC_KEY; + } + + if (!mpz_sgn(key->p) || !mpz_sgn(key->q)) { + rs_error(key, NULL, "unable to sign: private key missing"); + return RS_ERR_MISSING_PRIVATE_KEY; + } + + mpz_init(mm); + + /* Calculate q^-1 if necessary */ + + if (!mpz_sgn(key->qinv)) { +#ifndef USE_MPZ_GCDEXT + mpz_sub_ui(mm, key->p, 2); + mpz_powm(key->qinv, key->q, mm, key->p); +#else + mpz_gcdext(mm, key->qinv, NULL, key->q, key->p); + if (mpz_cmp_ui(mm, 1)) { + mpz_clear(mm); + rs_error(key, NULL, "unable to sign: unsuitable key"); + return RS_ERR_UNSUITABLE_RABIN_KEY; + } +#endif + } + + applyf(mm, hash, key->n, 2); + + mLp = mpz_legendre(mm, key->p); + mLq = mpz_legendre(mm, key->q); + + pm8 = mpz_get_ui(key->p) % 8; + qm8 = mpz_get_ui(key->q) % 8; + + if (pm8 == 1 || qm8 == 1 || (pm8 % 2) == 0 || (qm8 % 2) == 0) { + mpz_clear(mm); + rs_error(key, NULL, "unable to sign: unsuitable key"); + return RS_ERR_UNSUITABLE_RABIN_KEY; + } + + *f = ftab[(mLp == 1 ? 0 : 1) + + (mLq == 1 ? 0 : 2) + + (((qm8 - 3) / 2) * 4) + + (((pm8 - 3) / 2) * 12)]; + + if (*f == 99) { + mpz_clear(mm); + rs_error(key, NULL, "unable to sign: unsuitable key"); + return RS_ERR_UNSUITABLE_RABIN_KEY; + } + + rabsigf(res, hash, key->n, key->p, key->q, key->qinv, *f, rootnum); + mpz_clear(mm); + return RS_SUCCESS; +} + +/* Check that the given Rabin signature is valid. */ +int rs_validate_rabin (const mpz_t sig, /* purported signature of app */ + int f, /* f value */ + const mpz_t hash, /* MD5 hash of app */ + const RSKey* key) /* key structure */ +{ + mpz_t a, b; + int result; + + if (!mpz_sgn(key->n)) { + rs_error(key, NULL, "unable to validate: public key missing"); + return RS_ERR_MISSING_PUBLIC_KEY; + } + + if (f < 0 || f > 3) + return RS_SIGNATURE_INCORRECT; + + mpz_init(a); + mpz_init(b); + + mpz_mul(a, sig, sig); + mpz_mod(a, a, key->n); + + applyf(b, hash, key->n, f); + + result = mpz_cmp(a, b); + + mpz_clear(a); + mpz_clear(b); + return (result ? RS_SIGNATURE_INCORRECT : RS_SUCCESS); +} diff --git a/tool/rabbitsign-src/rsa.c b/tool/rabbitsign-src/rsa.c new file mode 100644 index 0000000..05139a9 --- /dev/null +++ b/tool/rabbitsign-src/rsa.c @@ -0,0 +1,137 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "rabbitsign.h" +#include "internal.h" + +#define VALIDATION_EXPONENT 17 + +/* + * Calculate the RSA signing exponent. + * + * The validation exponent, e, is 17 for all TI-related RSA + * signatures. The signing exponent, d, depends on n, and is + * calculated so that e * d === 1 (mod (p-1)(q-1)). + * + * (This means that for any number x, + * + * x^(e * d) = x * x^[k0 * (p-1)] === x * 1 (mod p), + * + * and likewise + * + * x^(e * d) = x * x^[k1 * (q-1)] === x * 1 (mod q). + * + * Therefore (Chinese remainder theorem) x^(e * d) === x (mod n). + * + * Note that there is no way of calculating d without knowing the + * factors of n; this is a key point in the security of RSA.) + */ +static int get_exponent(mpz_t res, /* mpz to store result */ + int e, /* validation exponent */ + const mpz_t p, /* first factor */ + const mpz_t q) /* second fatctor */ +{ + mpz_t a, b; + mpz_init(a); + mpz_init(b); + + mpz_sub_ui(a, p, 1); + mpz_sub_ui(b, q, 1); + mpz_mul(a, a, b); + + mpz_set_ui(b, e); + + mpz_gcdext(b, res, NULL, b, a); + if (mpz_cmp_ui(b, 1)) { + mpz_clear(a); + mpz_clear(b); + return RS_ERR_UNSUITABLE_RSA_KEY; + } + + mpz_mod(res, res, a); + + mpz_clear(a); + mpz_clear(b); + return RS_SUCCESS; +} + +/* + * Compute an RSA signature. + * + * This is simply the hash raised to the d-th power mod n (where d is + * defined above.) + */ +int rs_sign_rsa(mpz_t res, /* mpz to store signature */ + const mpz_t hash, /* MD5 hash of app */ + RSKey* key) /* key structure */ +{ + if (!mpz_sgn(key->n)) { + rs_error(key, NULL, "unable to sign: public key missing"); + return RS_ERR_MISSING_PUBLIC_KEY; + } + + if (!mpz_sgn(key->d)) { + if (!mpz_sgn(key->p) || !mpz_sgn(key->q)) { + rs_error(key, NULL, "unable to sign: private key missing"); + return RS_ERR_MISSING_PRIVATE_KEY; + } + if (get_exponent(key->d, VALIDATION_EXPONENT, key->p, key->q)) { + rs_error(key, NULL, "unable to sign: unsuitable key"); + return RS_ERR_UNSUITABLE_RSA_KEY; + } + } + + mpz_powm(res, hash, key->d, key->n); + return RS_SUCCESS; +} + +/* + * Check that the given RSA signature is valid. + * + * To do this, we raise the signature to the 17th power mod n, and see + * if it matches the hash. + */ +int rs_validate_rsa(const mpz_t sig, /* purported signature of app */ + const mpz_t hash, /* MD5 hash of app */ + const RSKey* key) /* key structure */ +{ + mpz_t e, m; + int result; + + if (!mpz_sgn(key->n)) { + rs_error(key, NULL, "unable to validate: public key missing"); + return RS_ERR_MISSING_PUBLIC_KEY; + } + + mpz_init(e); + mpz_init(m); + + mpz_set_ui(e, VALIDATION_EXPONENT); + mpz_powm(m, sig, e, key->n); + result = mpz_cmp(hash, m); + + mpz_clear(e); + mpz_clear(m); + return (result ? RS_SIGNATURE_INCORRECT : RS_SUCCESS); +} diff --git a/tool/rabbitsign-src/typestr.c b/tool/rabbitsign-src/typestr.c new file mode 100644 index 0000000..00bfb92 --- /dev/null +++ b/tool/rabbitsign-src/typestr.c @@ -0,0 +1,161 @@ +/* + * RabbitSign - Tools for signing TI graphing calculator software + * Copyright (C) 2009 Benjamin Moody + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#ifdef HAVE_STRING_H +# include +#else +# ifdef HAVE_STRINGS_H +# include +# endif +#endif + +#include "rabbitsign.h" +#include "internal.h" + +/* + * Get default file suffix for a given calc/data type. + */ +const char* rs_type_to_suffix(RSCalcType calctype, /* calculator type */ + RSDataType datatype, /* program data type */ + int hexonly) /* 1 = plain hex output */ +{ + if (calctype == RS_CALC_TI73) { + if (datatype == RS_DATA_APP) + return (hexonly ? "app" : "73k"); + else if (datatype == RS_DATA_OS) + return (hexonly ? "hex" : "73u"); + else if (datatype == RS_DATA_CERT) + return "73q"; + } + else if (calctype == RS_CALC_TI83P) { + if (datatype == RS_DATA_APP) + return (hexonly ? "app" : "8xk"); + else if (datatype == RS_DATA_OS) + return (hexonly ? "hex" : "8xu"); + else if (datatype == RS_DATA_CERT) + return "8xq"; + } + else if (calctype == RS_CALC_TI89) { + if (datatype == RS_DATA_APP) + return "89k"; + else if (datatype == RS_DATA_OS) + return "89u"; + else if (datatype == RS_DATA_CERT) + return "89q"; + } + else if (calctype == RS_CALC_TI92P) { + if (datatype == RS_DATA_APP) + return "9xk"; + else if (datatype == RS_DATA_OS) + return "9xu"; + else if (datatype == RS_DATA_CERT) + return "9xq"; + } + + return "sig"; +} + +/* + * Get implied calc/data type for a given file suffix. + */ +int rs_suffix_to_type(const char* suff, /* file suffix (not + including .) */ + RSCalcType* calctype, /* implied calculator + type */ + RSDataType* datatype) /* implied program type */ +{ + int calc, data; + + if (strlen(suff) != 3) + return -1; + + if (suff[0] == '7' && suff[1] == '3') + calc = RS_CALC_TI73; + else if (suff[0] == '8' && (suff[1] == 'x' || suff[1] == 'X')) + calc = RS_CALC_TI83P; + else if (suff[0] == '8' && suff[1] == '9') + calc = RS_CALC_TI89; + else if (suff[0] == '9' && (suff[1] == 'x' || suff[1] == 'X')) + calc = RS_CALC_TI92P; + else if ((suff[0] == 'v' || suff[0] == 'V') && suff[1] == '2') + calc = RS_CALC_TI92P; + else + return -1; + + if (suff[2] == 'k' || suff[2] == 'K') + data = RS_DATA_APP; + else if (suff[2] == 'u' || suff[2] == 'U') + data = RS_DATA_OS; + else if (suff[2] == 'q' || suff[2] == 'Q') + data = RS_DATA_CERT; + else + return -1; + + if (calctype) *calctype = calc; + if (datatype) *datatype = data; + return 0; +} + +/* + * Get a human-readable description of a calculator type. + */ +const char* rs_calc_type_to_string(RSCalcType calctype) +{ + switch (calctype) { + case RS_CALC_TI73: + return "TI-73"; + + case RS_CALC_TI83P: + return "TI-83/84 Plus"; + + case RS_CALC_TI89: + return "TI-89"; + + case RS_CALC_TI92P: + return "TI-92 Plus/Voyage 200"; + + default: + return "unknown"; + } +} + +/* + * Get a human-readable description of a data type. + */ +const char* rs_data_type_to_string(RSDataType datatype) +{ + switch (datatype) { + case RS_DATA_OS: + return "OS"; + + case RS_DATA_APP: + return "application"; + + case RS_DATA_CERT: + return "certificate"; + + default: + return "program"; + } +} diff --git a/tool/tilem-src/CHANGELOG b/tool/tilem-src/CHANGELOG new file mode 100644 index 0000000..78a8f7f --- /dev/null +++ b/tool/tilem-src/CHANGELOG @@ -0,0 +1,316 @@ +/* This file only contains the changelog for the gui/doc/data, some things could be missing (core changes...)*/ +/* This changelog was started when I started to work on TilEm2 */ + +/* contra-sh : +* ---18/08/09--- +* - Draw the TI83 +* - Copy from testemu.c : Create the savname, Open the Rom, Get the model, Draw the lcdscreen (no animation for the moment) +* - Function event OnDestroy +* - Modification of the Makefile (I hope it's good !? If you can control this... thx) +* - LEARNING HOW COMMIT !! :D +* ---19/08/09--- +* - New structure TilemCalcSkin : define the different filenames for the SkinSet (4 pieces). +* - Draw the other models _automatically_ . ;D +* ---20/08/09--- +* - Create skin.c +* - Create gui.h (equivalent of tilem.h for the gui directory) +* - Move the code struct in gui.h and TilemCalcSkin* tilem_guess_skin_set(TilemCalc* calc) into skin.c (only one call in the main file to define the skin set) ;D +* - Detect a keyboard event (function keyboard_event() in skin.c actually).No treatment. +* - Detect an event click on mouse +* ---21/08/09--- +* - Get the x and y values when click on mouse (now it will be easy to know how key is click on the pixmap, todo in priority : detect right click) +* ---24/08/09--- +* - Detect right click. +* ---26/08/09--- +* - New function : TilemKeyMap* tilem_guess_key_map(int id).Choose a TilemKeyMap with an id given in parameter. +* ---27/08/09--- +* - Extract the choice of the key_map from the mouse_event function.Execute only one time and it's more properly (and it will be easier to add the possibility to manage many skins and many keymaps). +* ---01/09/09--- +* - Choose automatically the key_list. The TilemKeyList is already included in the TilemKeyMap structure... +* - New structure TilemKeyCoord (old TilemKeyMap).TilemKeyMap already contains TilemKeyCoord and TilemKeyList... +* ---08/09/09--- +* - New function tilem_set_coord to change the keypad coord. +* - New file event.c to group the GDKevent handling. +* - New function tilem_set_skin to change the skin. +* ---10/09/09--- +* - Add the right click menu :D (0 signal connected without OnDestroy).Largely inspired from Tilem 0.973 and http://www.linux-france.org/article/devl/gtk/gtk_tut-11.html was a great inspiration too. +* ---21/09/09--- +* - Aouch I had seen that the left click doesn't work now! Problem : two callback for the only one button_press_event. (sorry for this version...) +* ---22/09/09--- +* - Correction : only one callback now. mouse_event contains the create_menu and the gtk_menu_popup. (lot of time was spent on this problem)* +* ---23/09/09--- +* - Change TilemKeyCoord to the 82 stats skin.Change Event.c too. +* ---06/10/09--- +* - Beginning the correction : change the method for testing coordinates clicks (one line now) . The coordinates will be imported from a file soon. +* ---20/11/09--- +* - After 1 week to learn Tiemu skin format...I have made my own Tilem skin Generator.Inspired from skinedit.It just generate compatible file with 0 coordinates values, and an default lcd coordinates.Not really necessary but very useful for testing and for learning how this f****** skin format works.It will be called skintool for the moment. +* ---27/11/09--- +* - After blocking a problem for a week grrr... I succeed to adapt the TiEmu skinloader to TilEm (skinops.c and skinops.h).Little modifications +* ---28/11/09--- +* - The mouse_event function now use a SKIN_INFOS struct. So delete TilemKeyCoord struct. +* ---02/12/09--- +* - Add a GtkFileSelection (access by right click menu) and try to load another skin with this method. +* ---03/12/09--- +* - Create a GLOBAL_SKIN_INFOS that contains a KEY_LIST struct (old TilemKeyList) and a SKIN_INFOS struct. +* ---04/12/09--- +* - Delete the TilemKeyCoord, TilemKeyMap, TilemCalcSkin and TilemKeyList structs... +* ---05/12/09--- +* - The GtkWidget *pWindow creation was moved from testemu2.c to event.c .The function is called GtkWidget* Draw_Screen(GLOBAL_SKIN_INFOS *gsi); +* ---07/12/09--- +* - New feature : TilEm could load a skin by the right_click_menu ! It use GtkWidget* ReDraw_Screen(GLOBAL_SKIN_INFOS *gsi) in event.c. WAOUH ! +* ---08/12/09--- +* - Move Draw_Screen, ReDraw_Screen and create_menus in a new screen.c file. Change Makefile. +* ---14/12/09--- +* - New feature : add a popup rom type selector (radio button) at the start of tilem. Showed but not used for the moment. +* - Connect the thread (no animation yet). Thanks to the "battle programming" 's spirit (hey bob ^^) +* ---18/12/09--- +* - Launch the interactivity with emulation core. Could print into the draw area. +* ---09/03/10--- +* - Restart working on this program ;D +* ---11/03/10--- +* - I finally succeeded to connect the core. To print something into the lcd screen ! WahoOO ! This day is a great day ! +* - I succeded to type numbers etc... +* - And now it works very well !! (the "button-release-event" is not catched by pWindow but by pLayout) +* ---15/03/10--- +* - Create the scan_click function.Return wich keys was clicked.Print debug infos. +* - Create the debuginfos.h to group the ifdef for debugging. (different level and different type of debug msg) +* ---17/03/10--- +* - Create the rom_choose_popup function to replace choose_rom_type.It use GtkDialog instead of GtkWindow. +* - rom_choose_popup _freeze_ the system... and get wich radio button is selected. So it will be easy to create the good emu.calc (and choose the default skin). +* ---18/03/10--- +* - Resize the (printed) lcd area (gsi->emu->lcdwin) to fit(perfectly) into the skin. +* - Replace a lot of printf by D****_L*_A* to easily switch what debug infos were printed. +* - Try to make a nice debugging output (frames in ASCII ^^) :p +* - WahooOO , load a skin works perfectly.You can easily change skin _while running_, no error, no warning. +* - Could load automatically the good skin and run the good core using the choose_rom_popup() function and choose_skin_filename() function. +* ---30/03/10--- +* - Create skin for following models : 73, 81, 82, 83+ and 84+. +* - Fix bug in tool.c .Modification of tool.c to create radio button more properly. +* ---31/03/10--- +* - Create skin for following model : 83 . Based on my own calc (take a foto with my iphone 3GS :D) +* ---01/04/10--- +* - New feature : Save calc state and load state. State are stored in a separate dir called sav/ . (using benjamin 's function) +* - New feature : Change view to see only the lcd. I finally choose to add it into a GtkLayout. So you can maximize it, but there was problem with add_event. +* ---02/04/10--- +* - Add popup function to just print error message.You must give the message and gsi as parameter, and it run a modal dialog. +* - Some cleaning. +* ---23/04/10--- +* - Add config.c file to manage config.dat (create, read, modif etc...). +* ---31/05/10--- +* - Start from scratch a totally new debugger :D.Just draw button with nice GtkImages.Actually in essai2 directory. +* - Get and resize pixmaps (png) to 36 * 36 pixels for the debugger. +* ---01/06/10--- +* - Add the debugger to tilem. Load registers values. +* - Add a new feature : switch the top level window "borderless".It can be switch by clicking on right click menu entry. +* ---02/06/10--- +* - Create the GtkTreeView (debugger). +* - Refresh register on click. +* ---05/08/10--- +* - More than one month without working on tilem...Only commit old modif, and skn/README file. +* - Refresh stack on click (number of entry is correct but value not) +* ---06/08/10--- +* - Working on a new system of configuration file.The config.dat is now a binary file (as .skn but totally differennt). At start, a CONFIG_INFOS struct is filling, then when clicking on right menu, this struct is modified, then when you stop tilem, config.dat is writed on disc. +* ---09/08/10--- +* - Correction of the SEG_FAULT (never use sizeof(char*) inside malloc, strlen(char*) is better :P). +* ---10/08/10--- +* - Working on a new config file called romconfig.dat (inspired from config.dat) using to do not ask for model each time tilem is started. +* - It works :D +* ---12/08/10--- +* - NEW feature : Can load a file using libticalcs2 (inspired from the wokr of Benjamin Moody). Basically it works, but it's not OK. (many bugs) +* - Drop the deprecated GtkFileSelection.Use GtkFileChooser instead. :) +* ---13/08/10--- +* - NEW feature : Add the screenshot feature. +* ---17/08/10--- +* - Change the ti84plus.skn (old was really ugly). +* - Add doc/ directory : add romconfig_file_format.txt and skinconfig_file_format.txt. +* ---18/08/10--- +* - Correct the bug in link.c (unlock mutex ...) +* - Start working on macro handling : Always do the same things to load and launch a file into an emulator become quickly noisy for the programmer (1 time, 10 times, 30 times, 50 times...argh!). Simply record a sequence and play it to test a program, this is one solution. (feature request from Guillaume Hoffman gh@gmail.com). +* ---19/08/10--- +* - The macro feature works including loading file (very important). The implementation is very basic (record and read a text file) so many bug could (should?) appear. But I wait to see how it will be use. +* ---20/08/10-- +* - Better implementation of GtkFileChooser (to be cleaner). +* - Some work on macro (no seg fault now ^^). +* - Add a Save state... entry in right click menu (no need to stop tilem to save state) +* - Add a Play macro with GtkFileChooser (to play another macro than play.txt). +* - Fix minor bug in GtkFileChooser (forget to init a char* to NULL). +* ---23/08/10--- +* - Add a new args handler using getop library (add -m for macro and -k for skin). +* ---06/09/10--- +* - NEW feature : could print the lcd content into the terminal. So useless but so funny ;) (feature request from Guillaume Hoffman). +* - The output is saved into a file called lcd_content.txt. +* ---08/09/10--- +* - Could choose wich char to display. This not really works as I want, but this is not a really important feature (works 1/2) +* - Add an option at startup (-l). Could now start in skinless mode. +* ---04/10/10--- +* - Start working on animated gif. Some research on GIF file format. Oops it will be hard, file is encoded (!). +* ---09/10/10--- +* - Creation of 1 little static gif file seems working, but always no LZW encoding. +* ---12/10/10--- +* - Finally I decided to use external source to encode the pix data.I use a file called "gifencode.c" by Hans Dinsen-Hansen and Michael A. Mayer. And it works !!! +* ---14/10/10--- +* - It works ! It works !!! Tilem2 is now able to generate animated gif, functions are currently working (but totally buggy) and it successfully create animated gif :) +* - Need to be integrated (and lot of debug).I commit it just to save it...Wait another commit to really use this feature :P +* ---01/02/11--- +* - Starting to work on a new config file using glibc to do not hard code keypad values. +* - And it works !!!! (but only load one keypad model currently) +* - Add the other models into keylist.ini (but the content is completely false). Change scan_click method (correct a bug) to use kp->nb_of_buttons. Only need to give correct value into the keylist.ini file. For the rest it's seems ok. +* ---07/02/11--- +* - Start to work on config.ini. A new generation config file using GKeyFile library (glib). Add some work in essai4 directory. +* ---08/02/11--- +* - Remove romconfig.c romconfig.h config.c config.h (handle binary config file). Remove ROM_CONFIG_INFOS and CONFIG_INFOS from gsi. +* - Add a new config.c and config.h file to handle config (last rom used, default skin to load, etc...). It uses glib GKeyFile library. +* - Fix the macro bug pointed by Benjamin. +* ---15/02/11--- +* - Replace correct copyright/licence informations into skinops.* .Sorry for the inconvenience. +* ---16/02/11--- +* - Fix an important mistake into the gif creator function (palette should be before gif frame information). +* ---18/02/11--- +* - Connect a timer to automatically add frame to a animated screenshot (using screenshot box). +* ---14/03/11--- +* - Improve default skin choice. +* - Define the skin's basedir into the config.ini. Add a universal getter into config.c . +* ---19/03/11--- +* - Add a bouncy progress bar to show the link status (send file). +* - Add a file saver. +* - Create a new TilemCmdlineArgs structure. +* ---07/04/11--- +* - Drop deprecated gtkitemfactory +* - Prepare GT3 migration +* ---10/04/11--- +* - Benjamin add a configure script and all associated things (Makefile.in, config.h etc...). +* - Fix dependency and a lot of cleaning. +* - Benjamin add a lot of function to handle data directory. +* ---13/04/11--- +* - Completely refactoring the screenshot window. New preview possibility. +* - Add some cool features for screenshot menu (replay from file, preview animation, preview screenshot, 2 gtkfilechooserbutton, change default folders etc...). +* - Remove old pix directory. +* - Benjamin begins to work on new debugger.Add a menu, some basic actions. +* ---15/04/11--- +* - Benjamin add a set of function to handle user defined icons. A lot of improvement on the debugger (add step action, pause). +* - Use tilem logo as default background in the screenshot menu, add some pix into the shared data directory. +* - Remove old debugger pix. +* - Add GtkSpinnerAnimation in the screenshot menu. Improve look and feel (fix fill/expand properties for the box, size of the widgets etc...). +* ---17/04/11--- +* - Benjamin add a ti83p symbol file which allow to replace flag values by theirs labels in the debugger (and more other things). Debugger looks like very good. +* ---19/04/11--- +* - I've added a ti83 symbol file (but it could contains some mistakes...). Flags are correctly printed for my loved ti83 regular ;) +* ---27/04/11--- +* - Root window size could be forced (but the ratio could be strand if you specify stanges values ... Because ration is calculated on the start width and heigth). +* ---02/05/11--- +* - Add the icons for the right click menu (stock icons currently). +* - Export all menu related code in a separate file called menu.c. It could maybe be done by UI in the future, but it works fine as it for the moment ;) +* - Add drag and drop support for loading files :) :) :) :) :) (code is currently really ugly) +* ---09/05/11--- +* - Create a new TilemGuiStateFlags structure ot group the running state of the gui (skinless, recording macro, etc...) and export it into TilemCalcEmu instead GLOBAL_SKIN_INFOS. +* - Completely remove GLOBAL_SKIN_INFOS structure from tilem2. +* ---10/05/11--- +* - Change strutures to look like a object oriented model. But in some case it's not really well implemented. +* ---11/05/11--- +* - Refactor a lot of gif header's code. +* ---12/05/11--- +* - I finally found what's wrong in my gif creation. So I can do multicolor gif now. +* ---14/05/11--- +* - We have finally completely reorganized the code to drop GLOBAL_SKIN_INFOS, and rename a lot of stuff including files (emuwin instead screen by example). +* - Benjamin have imported his nice filedlg functions (filedlg.c) from flashbook ui (see it on SF.net). Now use it for the entire code. +* ---14/05/11--- +* - Refactor the gif creation to separate clearly which value can be modified, which one are magic number etc... Create separate functions for each task. +* - Benjamin begins to work on animation (instead of writing file while recording it). It provides a wonderful objects to handle screenshot. +* ---15/05/11--- +* - Add a doxyfile for generating documentation including callgraphs. +* - Improvement to keybindings (Benjamin). +* ---16/05/11--- +* - Add some preliminary work on get var stuff. Only dirlist and allow user to retrieve a var using cmd line but it works. The list is printed in a tree view. +* - Add multi drag and drop feature. +* ---19/05/11--- +* - Add functions for animations (Benjamin). Could now save a animation into a gif file. +* - Improve screenshot menu by setting some buttons inactives depending the current state of screenshot creation. +* - Add combo box for size to screenshot menu. +* ---21/05/11--- +* - A lot of improvement on screenshot menu (Benjamin). Settings are now independent of screenshot dialog. Default directory for output. +* - Add mnemonic label to screenshot menu buttons (Benjamin). +* ---22/05/11--- +* - Benjamin redesign the entire menu popup to use the better GtkAction system. Now there's keybindings for menu and code is really beautiful and shorter. +* - Add grayscale option to screenshot menu (Benjamin). +* ---23/05/11--- +* - Save the last rom opened and use it at startup if no rom is given. +* - Allow user to load another rom while running. +* - Refactor the entire macro creation to separate creation from writing as TilemAnimation does. Code is cleaner and simply better. +* ---25/05/11--- +* - Add an option to change animation speed. The current printed animation is updated on change. +* ---26/05/11--- +* - Add an option to change foreground and background color. No visual feedback. +* ---27/05/11--- +* - Correct some stuff (see mailing list to know why). And add a palette setter in animation.c and a visual feedback when foreground/background color are changed. +* - Some other minor improvement. +* ---19/09/11--- +* - Restart working on tilem2 after holidays:) +* - Add new file getvar.c to handle get var function (receiving var from calc to PC). +* ---20/09/11--- +* - Some improvement to the rcvmenu. +* ---22/09/11--- +* - Add the ti83pfr.skn file. This skin is for ti83plus.fr. The creator is Claude Clerc (aka claudiux). He donates the skin under GPL v3+ licence.Thanks to him !!! +* ---11/10/11--- +* - Correct the getvar.c code to work correctly. Add columns to the tree view. +* - Add app receive handling. Set index column invisible. +* ---12/10/11--- +* - Some corrections : receive dialog is transcient. Receive dialog is not destroyed when closed just hided. New refresh button to refresh var/app list. +* - Use a separate thread to receive files. +* ---14/10/11--- +* - Add a feature to list ti82 and ti85 entries. User must prepare manually the link. Can't save a ti82 or ti85 entry (when selected in the rcvdialog). +* - Lot of bug, code is really ugly. No error handling. The prepare_for_link_receive is not working. etc... +* ---11/11/11--- +* - Benjamin add a totally new prefs dialog. Some unused function (print lcd in terminal by example) are deleted. +* ---12/11/11--- +* - Working on ns silent get var. +* ---14/11/11--- +* - New files : emucore.c and emucore.h . +* - Benjamin adds a totally new core task manager in the gui. Begin to convert the send files function to use it. +* - Use this task manager for macro. There's a little priority problem when a macro load a file. Load a file then run a macro at startup works fine. +* ---15/11/11--- +* - Receive non silent vars : Benjamin get it working! +* - Remove unused getvar.c file. +* ---16/11/11--- +* - Allow multiple selection and multiple receive file. Need to fix a segfault (tomorow... xD). +* ---17/11/11--- +* - Repare the file loading inside a macro (it was broken by new task manager). +* - Delete a lot of unused functions/printf. +* - Remove debuginfo.h file. +* ---22/11/11--- +* - Benjamin finished the last things left to do on receive dialog. Nice! +* ---25/11/11--- +* - New command line parser. Now tilem2 uses g_option from the glib instead of getopt. Easy handling of long options. Do not need to take care about correct parsing. A lot of new options are provided but not implemented ! +* - Delete args.c (old _but working well _ cmd line parser using getopt) and TilemCmdlineArgs structure from gui.h. +* - Add the possibility to getvar a file at startup. I had to use a weird solution to do this with task manager. But it's working :) +* ---12/12/11--- +* - Benjamin do a lot of improvements on file chooser (filters, ...). +* ---22/12/11--- +* - Benjamin fix certificate patching. +* ---19/03/12--- +* - Icons are finally commited into the trunk. These pictures are originaly designed by Scott Zeid and modified by me. No .desktop and icons installer for the moment. +* - Scott, thank you a lot for these wonderful pictures! +* ---21/03/12--- +* - Adding documentation (LaTeX). The documentation is not finished yet. Lot of pictures added. +* ---24/04/12--- +* - Benjamin added some correction to install properly the icons. +* ---27/04/12--- +* - Some modifications on the configure script because something was failing on my debian wheezy. It works fine yet (using squeeze and gtk/glib downgraded packages and some minor modifications on configure scripts). +* ---03/05/12--- +* - Reverting changes on the configure script because it was not the fault of configure script. +* ---07/05/12--- +* - Update doc (add "basic task" chapter). +* - Update .desktop files. +* ---08/05/12--- +* - Benjamin added a rule to install in the $HOME directory. +* - Benjamin added MIME type files. +* ---09/05/12--- +* - Benjamin added a piece of documentation about "getting ROM". +* ---11/05/12--- +* - Add an huge explanation for debugger part into the documentation. +* ---13/05/12--- +* - Add some screenshot documentation. +* ---15/05/12--- +* - Benjamin add README, THANKS and COPYING file. +* - Update screenshot doc. +*/ + diff --git a/tool/tilem-src/COPYING b/tool/tilem-src/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/tool/tilem-src/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/tool/tilem-src/INSTALL b/tool/tilem-src/INSTALL new file mode 100644 index 0000000..84008d7 --- /dev/null +++ b/tool/tilem-src/INSTALL @@ -0,0 +1,53 @@ + Installing TilEm + ------------------ + +To compile TilEm, you will need: + + * a standard C compiler + + * the GTK+ library, version 2.6.0 or later + + * the libticalcs2 library (part of the TiLP project), and its + dependencies (libticables2, libtifiles2, and libticonv) + +Some OS distributions package the "development files" separately from +the libraries themselves; you need both. If you are using Debian (or +a related distribution, such as Ubuntu or Mint), install the packages +'build-essential', 'libticalcs-dev', and 'libgtk2.0-dev'. + +If these packages aren't available from your package manager, you'll +need to compile them yourself; see the TiLP website for more +information (http://lpg.ticalc.org/prj_tilp/). + +Mac OS X has not been tested by the TilEm team, but if TiLP works, +TilEm should, too. Please let us know if you do get it to work. + +Once the above dependencies are installed, open a terminal and 'cd' to +the directory containing this INSTALL file. Then run + + ./configure + +which will check whether you have all of the necessary software +installed, and figure out how to compile TilEm for your particular +system. If configure is successful, you can then run + + make + +to compile TilEm. Finally, you can run + + sudo make install + +to install it (or run 'make install' as the superuser.) The program +will be installed in '/usr/local/bin/', and data files will be +installed in '/usr/local/share/tilem2/'. Alternatively, you can run + + make install-home + +to install TilEm in your own home directory ('$HOME/bin/', with data +files stored in '$HOME/.local/share/tilem2/'), which does not require +superuser privileges. (If $HOME/bin didn't exist already, you might +have to run 'export PATH=$HOME/bin:$PATH', or log out and log back in, +before running TilEm.) + +Once TilEm is installed, start it by running 'tilem2', or through your +system's applications menu. diff --git a/tool/tilem-src/KEYS b/tool/tilem-src/KEYS new file mode 100644 index 0000000..d547d84 --- /dev/null +++ b/tool/tilem-src/KEYS @@ -0,0 +1,270 @@ +KEYBOARD BINDINGS + + This list shows the default keyboard controls for TilEm. You can + modify these, if you like, by editing 'keybindings.ini'. (Place the + modified version of keybindings.ini in your TilEm configuration + directory - $HOME/.config/tilem2/ on Unix, or + $PROFILE\Local Settings\Application Data\tilem2 on Windows.) + + In the following list: + S = Shift + C = Control + ↑ = 2nd + α = Alpha + + ² in the TI-82/83 column indicates that the key is only for the TI-82. + ³ indicates that it is only for the TI-83/TI-83 Plus/TI-84 Plus. + + Key TI-73 TI-76.fr TI-81 TI-82/83 TI-85/86 + ────────────────────────────────────────────────────────────────────────────── + F12 ON ON ON ON ON + S+F12 ↑ [OFF] ↑ [OFF] ↑ [OFF] ↑ [OFF] ↑ [OFF] + + Up Up Up Up Up Up + Down Down Down Down Down Down + Left Left Left Left Left Left + Right Right Right Right Right Right + S+Up ↑ Up ↑ Up ↑ Up ↑ Up ↑ Up + S+Down ↑ Down ↑ Down ↑ Down ↑ Down ↑ Down + Home ↑ Left ↑ Left ↑ Left ↑ Left + End ↑ Right ↑ Right ↑ Right ↑ Right + PageUp α Up ³ + PageDown α Down ³ + + F1 Y= f(x)= Y= Y= F1 + F2 WINDOW fenêtre RANGE WINDOW F2 + F3 ZOOM zoom ZOOM ZOOM F3 + F4 TRACE trace TRACE TRACE F4 + F5 GRAPH graphe GRAPH GRAPH F5 + S+F1 ↑ [PLOT] ↑ [gr stat] ↑ Y= ↑ [STAT PLT] ↑ [M1] + S+F2 ↑ [TBLSET] ↑ [déf tab] ↑ RANGE ↑ [TBLSET] ↑ [M2] + S+F3 ↑ [FORMAT] ↑ [format] ↑ ZOOM ↑ [FORMAT] ↑ [M3] + S+F4 ↑ TRACE ↑ [calculs] ↑ TRACE ↑ [CALC] ↑ [M4] + S+F5 ↑ [TABLE] ↑ [table] ↑ GRAPH ↑ [TABLE] ↑ [M5] + PageDown MORE + + Tab 2nd 2nde 2nd 2nd 2nd + ' or Menu ↑ [TEXT] texte Alpha Alpha Alpha + + Insert ↑ [INS] ↑ [insérer] INS ↑ [INS] ↑ [INS] + Delete DEL suppr DEL DEL DEL + Backspace Left, DEL Left, suppr Left, DEL Left, DEL Left, DEL + C+Backspace CLEAR annul CLEAR CLEAR CLEAR + or C+Delete + + Escape CLEAR annul CLEAR CLEAR EXIT + S+Escape ↑ [QUIT] ↑ [quitter] ↑ [QUIT] ↑ [QUIT] ↑ [QUIT] + + F6 math MATH MATH GRAPH + F7 APPS angle MATRX MATRX/APPS STAT + F8 PRGM prgm PRGM PRGM PRGM + F9 var VARS VARS CUSTOM + F10 stats STAT + F11 MODE mode MODE MODE ↑ [MODE] + c CONST + d DRAW + l LIST + m MATH + p prgm PRGM PRGM + C+Tab ↑ [CATALOG] ↑ [catalog] ↑ [CATALOG]³ ↑ [CATALOG] + + _ UNIT + | or ½ a/b + f F◂▸D + a Ab/c◂▸d/e + s SIMP + % % + + | ↑ [ABS] ↑ [ABS] ² + s sin SIN SIN + c cos COS COS + t tan TAN TAN + o log LOG LOG + l ln LN LN + + u ↑ [uₙ] ↑ [u] ³ + v ↑ [vₙ] ↑ [v] ³ + w ↑ [wₙ] ↑ [w] ³ + u ↑ [Uₙ₋₁] ² + v ↑ [Vₙ₋₁] ² + n ↑ [n] ² + + C+1 or \ ↑ [x⁻¹] x⁻¹ x⁻¹ x⁻¹ ↑ [x⁻¹] + C+2 or ² x² x² x² x² x² + ( ( ( ( ( ( + ) ) ( ) ) ) + { ↑ [{] ↑ [{] + } ↑ [}] ↑ [}] + [ ↑ [[] ↑ [[] + ] ↑ []] ↑ []] + ^ ^ ^ ^ ^ ^ + / ÷ ÷ ÷ ÷ ÷ + * × × × × × + - - - - - - + + + + + + + + , , , α [,] , , + + > STO▸ STO▸ STO▸ STO▸ STO▸ + < ↑ [RCL] ↑ [rappel] ↑ STO▸ ↑ [RCL] ↑ [RCL] + + 0 - 9 0 - 9 0 - 9 0 - 9 0 - 9 0 - 9 + . . . . . . + ~ or ± (-) (-) (-) (-) (-) + & or € ↑ [EE] ↑ […×10ⁿ] EE ↑ [EE] EE + + x X x,n X|T X,T,θ,n + $ ↑ [ANS] ↑ [rép] ↑ [ANS] ↑ [ANS] ↑ [ANS] + # ↑ [π] ↑ [π] ↑ [π] ↑ [π] ↑ [π] + e ↑ [e] ↑ [e] ³ + i ↑ [i] ³ + + A - Z α [A]-[Z] α [A]-[Z] α [A]-[Z] + a - z ↑α [a]-[z] + Space α [space] α [space] α [space] + @ α [θ] α [θ] + " α ["] α ["] + ? α [?] α [?] + : α [:] ↑ [:] + = α [=] + + Return ENTER entrer ENTER ENTER ENTER + S+Return ↑ [ENTRY] ↑ [précéd] ↑ [ENTRY] ↑ [ENTRY] ↑ [ENTRY] + ────────────────────────────────────────────────────────────────────────────── + + For the TI-83 Plus and TI-84 Plus, in addition to the keys listed + above for the TI-83, pressing Shift+letter while Caps Lock is enabled + will type a lowercase letter (Alpha, Alpha, letter.) Lowercase mode + will need to be enabled on the calculator for this to work; it's not + enabled by default, but there are many assembly programs that can do + so. + + +KEYPAD CHARTS + + TI-73 TI-76.fr + + ╭──────┬──────┬──────┬──────┬──────╮ ╭──────┬──────┬──────┬──────┬──────╮ + │S+F1 │S+F2 │S+F3 │S+F4 │S+F5 │ │S+F1 │S+F2 │S+F3 │S+F4 │S+F5 │ + │F1 │F2 │F3 │F4 │F5 │ │F1 │F2 │F3 │F4 │F5 │ + ╰──────┴──────┴──────┴────┬─┴──┬───╯ ╰──────┴──────┴──────┴────┬─┴──┬───╯ + ╭──────┬──────┬──────╮ │S+Up│ ╭──────┬──────┬──────╮ │S+Up│ + │ │S+Esc │Ins ├────┤Up ├─────╮ │ │S+Esc │Ins ├────┤Up ├─────╮ + │Tab │F11 │Del │Home├────┤End │ │Tab │F11 │Del │Home├────┤End │ + ├──────┼──────┼──────┤Left├────┤Right│ ├──────┼──────┼──────┤Left├────┤Right│ + │' │ │ ├────┤S+Dn├─────╯ │ │ │ ├────┤S+Dn├─────╯ + │m │d │l │ │Down│ │' │x │F10 │ │Down│ + ├──────┼──────┼──────┼────┴─┬──┴───╮ ├──────┼──────┼──────┼────┴─┬──┴───╮ + │ │& │C+Tab │ │ │ │ │ │ │ │ │ + │C+2 │^ │F8 │F7 │Esc │ │F6 │F7 │F8 │F9 │Esc │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │\ │# │ │ │ │ │ │ │ │# │ + │_ │| │f │a │c │ │\ │s │c │t │^ │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │ │ │ │ │ │ │& │{ │} │e │ + │s │% │( │) │/ │ │C+2 │, │( │) │/ │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │ │ │ │ │ │ │u │v │w │ │ + │x │7 │8 │9 │* │ │o │7 │8 │9 │* │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │ │ │ │ │ │ │ │ │ │ │ + │, │4 │5 │6 │- │ │l │4 │5 │6 │- │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │< │ │ │ │ │ │< │ │ │ │ │ + │> │1 │2 │3 │+ │ │> │1 │2 │3 │+ │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │S+F12 │ │ │$ │S+Ret │ │S+F12 │C+Tab │ │$ │S+Ret │ + │F12 │0 │. │~ │Ret │ │F12 │0 │. │~ │Ret │ + ╰──────┴──────┴──────┴──────┴──────╯ ╰──────┴──────┴──────┴──────┴──────╯ + + + TI-81 TI-82 + + ╭──────┬──────┬──────┬──────┬──────╮ ╭──────┬──────┬──────┬──────┬──────╮ + │S+F1 │S+F2 │S+F3 │S+F4 │S+F5 │ │S+F1 │S+F2 │S+F3 │S+F4 │S+F5 │ + │F1 │F2 │F3 │F4 │F5 │ │F1 │F2 │F3 │F4 │F5 │ + ╰──────┴──────┴──────┴────┬─┴──┬───╯ ╰──────┴──────┴──────┴────┬─┴──┬───╯ + ╭──────┬──────┬──────╮ │S+Up│ ╭──────┬──────┬──────╮ │S+Up│ + │ │ │ ├────┤Up ├─────╮ │ │S+Esc │Ins ├────┤Up ├─────╮ + │Tab │Ins │Del │ ├────┤ │ │Tab │F11 │Del │Home├────┤End │ + ├──────┼──────┼──────┤Left├────┤Right│ ├──────┼──────┼──────┤Left├────┤Right│ + │ │ │ ├────┤S+Dn├─────╯ │ │ │ ├────┤S+Dn├─────╯ + │' │x │F11 │ │Down│ │' │x │F10 │ │Down│ + ├──────┼──────┼──────┼────┴─┬──┴───╮ ├──────┼──────┼──────┼────┴─┬──┴───╮ + │ │ │ │ │S+Esc │ │ │ │ │ │ │ + │F6 │F7 │F8 │F9 │Esc │ │F6 │F7 │F8 │F9 │Esc │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │| │ │ │ │# │ │| │ │ │ │# │ + │\ │s │c │t │^ │ │\ │s │c │t │^ │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │ │ │ │ │ │ │& │{ │} │ │ + │C+2 │& │( │) │/ │ │C+2 │, │( │) │/ │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │ │ │ │ │ │ │u │v │n │[ │ + │o │7 │8 │9 │* │ │o │7 │8 │9 │* │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │ │ │ │ │ │ │ │ │ │] │ + │l │4 │5 │6 │- │ │l │4 │5 │6 │- │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │< │ │ │ │ │ │< │ │ │ │ │ + │> │1 │2 │3 │+ │ │> │1 │2 │3 │+ │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │S+F12 │ │ │$ │S+Ret │ │S+F12 │ │ │$ │S+Ret │ + │F12 │0 │. │~ │Ret │ │F12 │0 │. │~ │Ret │ + ╰──────┴──────┴──────┴──────┴──────╯ ╰──────┴──────┴──────┴──────┴──────╯ + + Not shown: Not shown: + A-Z Alpha, [A]-[Z] A-Z Alpha, [A]-[Z] + @ Alpha, 3 @ Alpha, 3 + " Alpha, + " Alpha, + + Space Alpha, 0 Space Alpha, 0 + , Alpha, . : Alpha, . + ? Alpha, (-) ? Alpha, (-) + + + TI-83 / TI-83 Plus / TI-84 Plus TI-85 / TI-86 + + ╭──────┬──────┬──────┬──────┬──────╮ ╭──────┬──────┬──────┬──────┬──────╮ + │S+F1 │S+F2 │S+F3 │S+F4 │S+F5 │ │S+F1 │S+F2 │S+F3 │S+F4 │S+F5 │ + │F1 │F2 │F3 │F4 │F5 │ │F1 │F2 │F3 │F4 │F5 │ + ╰──────┴──────┴──────┴────┬─┴──┬───╯ ╰──────┴──────┴──────┴────┬─┴──┬───╯ + ╭──────┬──────┬──────╮ │S+Up│ ╭──────┬──────┬──────╮ │S+Up│ + │ │S+Esc │Ins ├────┤Up ├─────╮ │ │S+Esc │F11 ├────┤Up ├─────╮ + │Tab │F11 │Del │Home├────┤End │ │Tab │Esc │PgDn │Home├────┤End │ + ├──────┼──────┼──────┤Left├────┤Right│ ├──────┼──────┼──────┤Left├────┤Right│ + │ │ │ ├────┤S+Dn├─────╯ │ │ │Ins ├────┤S+Dn├─────╯ + │' │x │F10 │ │Down│ │' │ │Del │ │Down│ + ├──────┼──────┼──────┼────┴─┬──┴───╮ ├──────┼──────┼──────┼────┴─┬──┴───╮ + │ │ │ │ │ │ │ │ │ │C+Tab │ │ + │F6 │F7 │F8 │F9 │Esc │ │F6 │F7 │F8 │F9 │C+BkSp│ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │ │ │ │# │ │ │ │ │ │# │ + │\ │s │c │t │^ │ │ │ │ │ │^ │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │& │{ │} │e │ │ │\ │[ │] │ │ + │C+2 │, │( │) │/ │ │ │& │( │) │/ │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │u │v │w │[ │ │ │ │ │ │ │ + │o │7 │8 │9 │* │ │C+2 │7 │8 │9 │* │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │ │ │ │ │] │ │ │ │ │ │ │ + │l │4 │5 │6 │- │ │, │4 │5 │6 │- │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │< │ │ │ │ │ │< │ │ │ │ │ + │> │1 │2 │3 │+ │ │> │1 │2 │3 │+ │ + ├──────┼──────┼──────┼──────┼──────┤ ├──────┼──────┼──────┼──────┼──────┤ + │S+F12 │C+Tab │i │$ │S+Ret │ │S+F12 │ │: │$ │S+Ret │ + │F12 │0 │. │~ │Ret │ │F12 │0 │. │~ │Ret │ + ╰──────┴──────┴──────┴──────┴──────╯ ╰──────┴──────┴──────┴──────┴──────╯ + + Not shown: Not shown: + PageUp Alpha, Up A-Z Alpha, [A]-[Z] + PageDown Alpha, Down a-z 2nd, Alpha, [a]-[z] + A-Z Alpha, [A]-[Z] Space Alpha, (-) + @ Alpha, 3 = Alpha, Sto▸ + " Alpha, + + Space Alpha, 0 + : Alpha, . + ? Alpha, (-) + + TI-83 Plus / TI-84 Plus only: + CapsLock + Shift + a-z = Alpha, Alpha, [a]-[z] diff --git a/tool/tilem-src/Makefile.in b/tool/tilem-src/Makefile.in new file mode 100644 index 0000000..77de808 --- /dev/null +++ b/tool/tilem-src/Makefile.in @@ -0,0 +1,91 @@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +bindir = @bindir@ +datadir = @datadir@ +pkgdatadir = @datadir@/tilem2 +mandir = @mandir@ + +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +@SET_MAKE@ +SHELL = @SHELL@ + +INSTALL = @INSTALL@ + +distname = @PACKAGE_TARNAME@-@PACKAGE_VERSION@ + +distfiles = CHANGELOG COPYING INSTALL KEYS NEWS README THANKS TODO \ + aclocal.m4 config.h.in configure configure.ac install-sh \ + Makefile.in \ + data/Makefile.in \ + data/keybindings.ini \ + data/desktop/*.desktop data/desktop/*.xml \ + data/icons/hicolor/index.theme data/icons/hicolor/*/*/*.png \ + data/icons-svg/*.svg \ + data/skins/README data/skins/*.skn \ + data/symbols/*.sym \ + db/Makefile.in db/*.c db/*.h \ + emu/Makefile.in emu/*.c emu/*.h emu/x*/*.c emu/x*/*.h \ + gui/Makefile.in gui/*.c gui/*.h gui/*.ico gui/*.rc.in \ + installer/win32/Makefile.in \ + installer/win32/installer.nsi.in installer/win32/gtkrc \ + installer/win32/COPYING-ZLIB installer/win32/COPYING-PIXMAN + +all: + cd emu && $(MAKE) + cd db && $(MAKE) + cd gui && $(MAKE) + +clean: + cd emu && $(MAKE) clean + cd db && $(MAKE) clean + cd gui && $(MAKE) clean + cd installer/win32 && $(MAKE) clean + +install: all + cd gui && $(MAKE) install + cd data && $(MAKE) install + +uninstall: + cd gui && $(MAKE) uninstall + cd data && $(MAKE) uninstall + +install-home: all + $(MAKE) install \ + bindir=$(HOME)/bin \ + datarootdir=$${XDG_DATA_HOME:-$(HOME)/.local/share} + +uninstall-home: + $(MAKE) uninstall \ + bindir=$(HOME)/bin \ + datarootdir=$${XDG_DATA_HOME:-$(HOME)/.local/share} + +distclean: clean + rm -f config.status config.log config.h configure.lineno + rm -rf autom4te.cache + rm -f installer/win32/Makefile installer/win32/installer.nsi + rm -f gui/tilem2.rc + rm -f Makefile emu/Makefile db/Makefile gui/Makefile data/Makefile + +dist: + rm -rf $(distname) + mkdir $(distname) + set -e ; files=`cd $(srcdir) ; echo $(distfiles)` ; \ + for f in $$files ; do \ + dir=`echo $(distname)/$$f | sed 's,/[^/]*$$,,'` ; \ + [ -d $$dir ] || $(INSTALL) -d $$dir ; \ + cp -p $(srcdir)/$$f $$dir ; \ + done + tar cv $(distname) | bzip2 -c -9 > $(distname).tar.bz2 + +Makefile: Makefile.in config.status + $(SHELL) ./config.status + +config.status: configure + $(SHELL) ./config.status --recheck + +.PRECIOUS: Makefile config.status +.PHONY: all clean dist distclean install install-home uninstall uninstall-home diff --git a/tool/tilem-src/NEWS b/tool/tilem-src/NEWS new file mode 100644 index 0000000..45ef967 --- /dev/null +++ b/tool/tilem-src/NEWS @@ -0,0 +1,73 @@ + Version History + ----------------- + +2012-06-07 -- version 2.0 + + This is the first official release of the "new" TilEm. Much of the + old TilEm code has been rewritten, and there are many improvements + (and probably some new bugs.) + + Please note, if you have used older versions of TilEm: + + * Your existing ROM files and settings in ~/.TilEm will not be + used (in fact, you can keep TilEm 0.97x installed alongside + TilEm 2.0 if you wish.) TilEm 2.0 no longer uses a "library" of + ROM files; you can store ROM files anywhere you like. + + * TilEm 2.0 uses a new format for calculator state (SAV) files. + State files created by TilEm 0.97x can be loaded by TilEm 2.0, + but if you then save the state, it will be stored in the new + format, which older versions of TilEm will not support. + + New features and bugs fixed since version 0.975 include: + + * All code that was covered by the Z80em license has been removed. + + * Support for the TI-81 (both hardware versions) and TI-76.fr, and + experimental support for the TI-Nspire's TI-84 Plus emulation + mode. + + * Many hardware emulation improvements for all calculator models. + In particular, major improvements have been made concerning Z80 + interrupts, timers, the LCD driver, and the link port. + + * The emulator window uses TiEmu-format skin files. + + * Greatly improved grayscale emulation. + + * Commands for saving still screenshots (in PNG, BMP, JPEG, or GIF + format) and animations (GIF format only.) + + * Keypad macros can be recorded and replayed. + + * Programs and/or ROM files can be loaded from the command line. + + * Link I/O uses libticalcs2, which allows all types of variables, + as well as Flash apps and OSes, to be transferred through the + link port. For the TI-81, PRG files can be transferred to and + from the calculator memory directly. + + * TilEm does not consume 100% of the host CPU when idle. + + * Improved disassembler (macros; distinct "labels" and "romcalls"; + named IY flags.) + + * The debugger offers a "Finish Subroutine" command. In addition, + the "Step Over" command behaves more sensibly. + + * Breakpoints can be set on absolute memory addresses, and on Z80 + opcodes. + + * Many minor improvements. + + Features of 0.975 that are not yet supported in TilEm 2.0 include: + + * External link cables. + + * Custom symbol files in the disassembler. + + * Program counter history tracking. + + Most of the new code is due to Benjamin Moody (floppusmaximus) and + Thibault Duponchelle (contra-sh). See THANKS for a full list of + contributors. diff --git a/tool/tilem-src/README b/tool/tilem-src/README new file mode 100644 index 0000000..bcd613c --- /dev/null +++ b/tool/tilem-src/README @@ -0,0 +1,92 @@ + TilEm + ------- + +TilEm is an emulator and debugger for Texas Instruments Z80-based +graphing calculators. It can emulate any of the following calculator +models: + + TI-73 / TI-73 Explorer + TI-76.fr + TI-81 + TI-82 + TI-82 STATS / TI-82 STATS.fr + TI-83 + TI-83 Plus / TI-83 Plus Silver Edition / TI-83 Plus.fr + TI-84 Plus / TI-84 Plus Silver Edition / TI-84 pocket.fr + TI-85 + TI-86 + +TilEm fully supports all known versions of the above calculators (as +of 2012), and attempts to reproduce the behavior of the original +calculator hardware as faithfully as possible. + +In addition, TilEm can emulate the TI-Nspire's virtual TI-84 Plus +mode. This is currently experimental, and some programs may not work +correctly. + +TilEm runs on the X Window System on GNU/Linux and other Unix-like +platforms, as well as on Microsoft Windows, and any other platform +supported by the GTK+ library. + + + Installation + -------------- +Packages for Microsoft Windows are available from the TilEm project +website (http://lpg.ticalc.org/prj_tilem/). For other platforms, you +will need to compile TilEm from source; please see the file 'INSTALL' +in the source package. + + + Using TilEm + ------------- +TilEm requires a copy of the operating system from the calculator +model(s) you wish to emulate. This file is called a "ROM image", +since the calculator OS was traditionally stored in Read-Only Memory. +ROM images are copyrighted by TI and may not be distributed without +permission. See the TilEm User's Manual for more information about +how to create a ROM image. + +The main TilEm window shows an image of the calculator (if possible - +we are still missing background images for a few models.) Clicking +with the left mouse button presses a key; clicking with the middle +button presses a key and holds it down. Clicking with the right +button, or pressing Shift+F10, opens the menu. + +When you run TilEm for the first time, it will ask you to select a ROM +image to use. The file you select will be used by default the next +time you run TilEm. You can switch to a different ROM image by +right-clicking and selecting "Open Calculator". + +The state of the emulated calculator is not saved by default; to save +the state, right-click and select "Save Calculator". The state is +saved to a ".sav" file, stored in the same directory as the ROM image. + +You can send program and variable files to the emulated calculator, +either by dragging and dropping them from your file manager, or by +right-clicking and selecting "Send File". To retrieve program or +variable files from the calculator, select "Receive File". + +Other features of TilEm include: + + - A debugger for assembly programs + - Capturing screenshots, both normal and animated + - Recording and replaying keystroke macros + +For more information, see the TilEm User's Manual: + http://lpg.ticalc.org/prj_tilem2/doc.html + + + About this program + -------------------- +Many people deserve credit for helping to make this program possible. +See the file 'THANKS'. + +This program is free software, which means that you are allowed to +modify it, and to distribute it (or your modified version) to others. +When you received this program, you should also have been offered a +complete copy of its source code. For more information, see the file +'COPYING'. + +You can contact the authors at . +Please let us know of any problems you encounter, or ideas for how we +could make TilEm better. diff --git a/tool/tilem-src/THANKS b/tool/tilem-src/THANKS new file mode 100644 index 0000000..b76e949 --- /dev/null +++ b/tool/tilem-src/THANKS @@ -0,0 +1,37 @@ + Thanks + -------- +The current maintainers of TilEm are Thibault Duponchelle and Benjamin +Moody, but many other people have played a part in making this program +possible. + +The original TilEm was written in 2001 by Julien Solignac. The +current version is partially based on the original, but large portions +have been rewritten, starting in 2009, by Benjamin Moody and Thibault +Duponchelle. Portions of the hardware emulation code are also due to +Luc Bruant. + +The code for reading skin files is based on code from TiEmu, written +by Julien Blache. The GIF compression code is based on whirlgif, +written by Hans Dinsen-Hansen and Michael A. Mayer. + +Thanks to Claude Clerc for the photo of his TI-83 Plus, and to Danilo +Šegan for the photo of his TI-86, which we have used as skins. Thanks +to Scott Zeid for the design of the program icon. + +TilEm uses the TiLP libraries (libticalcs2, libticables2, libtifiles2, +and libticonv) to send and receive variables. Thanks are due to the +current maintainer of TiLP, Lionel Debroux, for his assistance, as +well as to all of the past maintainers of TiLP, including Romain +Liévin, Kevin Kofler, and Julien Blache. + +Finally, this program would never have been possible without the +efforts of countless programmers and researchers over the years to +discover and document the inner workings of the calculator hardware. +The following people deserve special recognition for their research: +Randy Compton, Tijl Coosemans, Brian Coventry, Dan Eble, Dan +Englender, Dines Justesen, Julien Lasson, Mattias Lindqvist, James +Montelongo, Michael Vincent, Brandon Wilson, and Joerg Woerner. + +(Thibault) In addition of above: +Thanks to Michael Nock and Guillaume Hoffman for testing, feature request and encouragement. +Thanks to Xavier Andreani for his encouragement. diff --git a/tool/tilem-src/TODO b/tool/tilem-src/TODO new file mode 100644 index 0000000..53b4599 --- /dev/null +++ b/tool/tilem-src/TODO @@ -0,0 +1,7 @@ +- Linking between 2 tilem instances +- Sound +- Rewrite macro (parser and tokens). +- Add scripting (lua or something else?) +- Teacher mode (record keys pressed and produce a picture file with keys icons) + + diff --git a/tool/tilem-src/aclocal.m4 b/tool/tilem-src/aclocal.m4 new file mode 100644 index 0000000..af3c83d --- /dev/null +++ b/tool/tilem-src/aclocal.m4 @@ -0,0 +1,173 @@ +# generated automatically by aclocal 1.11.1 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 1 (pkg-config-0.24) +# +# Copyright © 2004 Scott James Remnant . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +# only at the first occurence in configure.ac, so if the first place +# it's called might be skipped (such as if it is within an "if", you +# have to call PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])# PKG_CHECK_MODULES + diff --git a/tool/tilem-src/config.h.in b/tool/tilem-src/config.h.in new file mode 100644 index 0000000..f2aa9aa --- /dev/null +++ b/tool/tilem-src/config.h.in @@ -0,0 +1,91 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if the system has the type `uintptr_t'. */ +#undef HAVE_UINTPTR_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to the equivalent of the C99 'restrict' keyword, or to + nothing if this is not supported. Do not define if restrict is + supported directly. */ +#undef restrict +/* Work around a bug in Sun C++: it does not support _Restrict or + __restrict__, even though the corresponding Sun C compiler ends up with + "#define restrict _Restrict" or "#define restrict __restrict__" in the + previous line. Perhaps some future version of Sun C++ will work with + restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ +#if defined __SUNPRO_CC && !defined __RESTRICT +# define _Restrict +# define __restrict__ +#endif + +/* Define to the type of an unsigned integer type wide enough to hold a + pointer, if such a type exists, and if the system does not define it. */ +#undef uintptr_t diff --git a/tool/tilem-src/configure b/tool/tilem-src/configure new file mode 100755 index 0000000..cc4e5ad --- /dev/null +++ b/tool/tilem-src/configure @@ -0,0 +1,6127 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.68 for TilEm 2.0. +# +# Report bugs to . +# +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software +# Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + # Preserve -v and -x to the replacement shell. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; + esac + exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: tilem-devel@lists.sourceforge.net about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='TilEm' +PACKAGE_TARNAME='tilem' +PACKAGE_VERSION='2.0' +PACKAGE_STRING='TilEm 2.0' +PACKAGE_BUGREPORT='tilem-devel@lists.sourceforge.net' +PACKAGE_URL='http://tilem.sourceforge.net/' + +ac_unique_file="emu/tilem.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +EGREP +GREP +DLLPATH +TICALCS_BINDIR +GTK_BINDIR +LN_S +MAKENSIS +WINDRES +OBJDUMP +STRIP +TICALCS_LIBS +TICALCS_CFLAGS +gui_extra_objects +GUI_LDFLAGS +GTK_LIBS +GTK_CFLAGS +PKG_CONFIG_LIBDIR +PKG_CONFIG_PATH +PKG_CONFIG +UPDATE_MIME_DATABASE +UPDATE_DESKTOP_DATABASE +SET_MAKE +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +RANLIB +AR_FLAGS +AR +OPT_CFLAGS +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_gtk_deprecated +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +OPT_CFLAGS +AR +AR_FLAGS +RANLIB +PKG_CONFIG +PKG_CONFIG_PATH +PKG_CONFIG_LIBDIR +GTK_CFLAGS +GTK_LIBS +TICALCS_CFLAGS +TICALCS_LIBS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used" >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures TilEm 2.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/tilem] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of TilEm 2.0:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-gtk-deprecated + Disable deprecated GTK+ API + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + OPT_CFLAGS Additional C compiler flags used for optimizing critical areas + of the code (default: -O3 if using GCC) + AR Static library archiver + AR_FLAGS Flags to pass to ar to build a static library + RANLIB Program to make a static library linkable + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path + GTK_CFLAGS C compiler flags for GTK, overriding pkg-config + GTK_LIBS linker flags for GTK, overriding pkg-config + TICALCS_CFLAGS + C compiler flags for TICALCS, overriding pkg-config + TICALCS_LIBS + linker flags for TICALCS, overriding pkg-config + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +TilEm home page: . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +TilEm configure 2.0 +generated by GNU Autoconf 2.68 + +Copyright (C) 2010 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by TilEm $as_me 2.0, which was +generated by GNU Autoconf 2.68. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + +# Checks for programs + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +if test "x$GCC" = "xyes" ; then + CFLAGS="$CFLAGS -W -Wall -Wwrite-strings" + if test "x$OPT_CFLAGS" = "x" ; then + OPT_CFLAGS="-O3" + fi +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_AR="ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +else + AR="$ac_cv_prog_AR" +fi + + + +if test "x$AR_FLAGS" = "x" ; then + AR_FLAGS=cru +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + + +# Extract the first word of "update-desktop-database", so it can be a program name with args. +set dummy update-desktop-database; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_UPDATE_DESKTOP_DATABASE+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$UPDATE_DESKTOP_DATABASE"; then + ac_cv_prog_UPDATE_DESKTOP_DATABASE="$UPDATE_DESKTOP_DATABASE" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_UPDATE_DESKTOP_DATABASE="update-desktop-database" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_UPDATE_DESKTOP_DATABASE" && ac_cv_prog_UPDATE_DESKTOP_DATABASE=":" +fi +fi +UPDATE_DESKTOP_DATABASE=$ac_cv_prog_UPDATE_DESKTOP_DATABASE +if test -n "$UPDATE_DESKTOP_DATABASE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $UPDATE_DESKTOP_DATABASE" >&5 +$as_echo "$UPDATE_DESKTOP_DATABASE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +# Extract the first word of "update-mime-database", so it can be a program name with args. +set dummy update-mime-database; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_UPDATE_MIME_DATABASE+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$UPDATE_MIME_DATABASE"; then + ac_cv_prog_UPDATE_MIME_DATABASE="$UPDATE_MIME_DATABASE" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_UPDATE_MIME_DATABASE="update-mime-database" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_UPDATE_MIME_DATABASE" && ac_cv_prog_UPDATE_MIME_DATABASE=":" +fi +fi +UPDATE_MIME_DATABASE=$ac_cv_prog_UPDATE_MIME_DATABASE +if test -n "$UPDATE_MIME_DATABASE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $UPDATE_MIME_DATABASE" >&5 +$as_echo "$UPDATE_MIME_DATABASE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +# Checks for libraries + + + +# GLib and GTK+ + + + + + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK" >&5 +$as_echo_n "checking for GTK... " >&6; } + +if test -n "$GTK_CFLAGS"; then + pkg_cv_GTK_CFLAGS="$GTK_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_GTK_CFLAGS=`$PKG_CONFIG --cflags "gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$GTK_LIBS"; then + pkg_cv_GTK_LIBS="$GTK_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_GTK_LIBS=`$PKG_CONFIG --libs "gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + GTK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0" 2>&1` + else + GTK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$GTK_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0) were not met: + +$GTK_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +Alternatively, you may set the environment variables GTK_CFLAGS +and GTK_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +Alternatively, you may set the environment variables GTK_CFLAGS +and GTK_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. + +To get pkg-config, see . +See \`config.log' for more details" "$LINENO" 5; } +else + GTK_CFLAGS=$pkg_cv_GTK_CFLAGS + GTK_LIBS=$pkg_cv_GTK_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +fi + +# Check whether --enable-gtk-deprecated was given. +if test "${enable_gtk_deprecated+set}" = set; then : + enableval=$enable_gtk_deprecated; enable_gtk_deprecated=$enableval +else + enable_gtk_deprecated=yes +fi + +if test "x$enable_gtk_deprecated" = "xno" ; then + GTK_CFLAGS="$GTK_CFLAGS -DG_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGSEAL_ENABLE" +fi + +# If using the native Windows version of GTK+, be sure to use +# -mms-bitfields for all compilation. Also, use -mwindows for linking +# GUI programs. + +# (If not using pkg-config, you're on your own) + +if test "x$PKG_CONFIG" != "x" ; then + gtk_target=`$PKG_CONFIG --variable=target gtk+-2.0` +fi + +if test "x$gtk_target" = "xwin32" && test "x$GCC" = "xyes" ; then + CFLAGS="$CFLAGS -mms-bitfields" + GUI_LDFLAGS="-mwindows" + LIBS="-lcomdlg32 -lshell32 -lole32 $LIBS" + gui_extra_objects="tilem2rc.o" +else + GUI_LDFLAGS="" + gui_extra_objects="" +fi + + + + + + save_cflags="$CFLAGS" + save_libs="$LIBS" + CFLAGS="$CFLAGS $GTK_CFLAGS" + LIBS="$LIBS $GTK_LIBS" + +ac_fn_c_check_func "$LINENO" "gtk_init" "ac_cv_func_gtk_init" +if test "x$ac_cv_func_gtk_init" = xyes; then : + have_gtk=yes +else + have_gtk=no +fi + + CFLAGS="$save_cflags" + LIBS="$save_libs" + +if test "x$have_gtk" != "xyes" ; then + as_fn_error $? "GTK+ 2.x libraries not found or not usable. +You must install a recent version of GTK+ 2.x, including the +-dev/-devel packages if appropriate." "$LINENO" 5 +fi + +# Libticalcs2 and related libraries + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for TICALCS" >&5 +$as_echo_n "checking for TICALCS... " >&6; } + +if test -n "$TICALCS_CFLAGS"; then + pkg_cv_TICALCS_CFLAGS="$TICALCS_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ticalcs2 ticables2 tifiles2 ticonv\""; } >&5 + ($PKG_CONFIG --exists --print-errors "ticalcs2 ticables2 tifiles2 ticonv") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_TICALCS_CFLAGS=`$PKG_CONFIG --cflags "ticalcs2 ticables2 tifiles2 ticonv" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$TICALCS_LIBS"; then + pkg_cv_TICALCS_LIBS="$TICALCS_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ticalcs2 ticables2 tifiles2 ticonv\""; } >&5 + ($PKG_CONFIG --exists --print-errors "ticalcs2 ticables2 tifiles2 ticonv") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_TICALCS_LIBS=`$PKG_CONFIG --libs "ticalcs2 ticables2 tifiles2 ticonv" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + TICALCS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ticalcs2 ticables2 tifiles2 ticonv" 2>&1` + else + TICALCS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ticalcs2 ticables2 tifiles2 ticonv" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$TICALCS_PKG_ERRORS" >&5 + + have_ticalcs=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_ticalcs=no +else + TICALCS_CFLAGS=$pkg_cv_TICALCS_CFLAGS + TICALCS_LIBS=$pkg_cv_TICALCS_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_ticalcs=maybe +fi + +if test "x$have_ticalcs" = "xmaybe" ; then + + save_cflags="$CFLAGS" + save_libs="$LIBS" + CFLAGS="$CFLAGS $TICALCS_CFLAGS" + LIBS="$LIBS $TICALCS_LIBS" + ac_fn_c_check_func "$LINENO" "ticalcs_library_init" "ac_cv_func_ticalcs_library_init" +if test "x$ac_cv_func_ticalcs_library_init" = xyes; then : + have_ticalcs=yes +else + have_ticalcs=no +fi + + CFLAGS="$save_cflags" + LIBS="$save_libs" + +fi + +if test "x$have_ticalcs" != "xyes" ; then + as_fn_error $? "libticalcs2 not found or not usable. + +$TICALCS_PKG_ERRORS + +You must install libticalcs2, libticables2, libtifiles2, and libticonv +(including the -dev/-devel packages if appropriate.) These libraries +are available from . + +If you have installed the libraries in a non-standard location (or if +you're cross-compiling), you will need to add the location of +ticalcs2.pc to your PKG_CONFIG_PATH environment variable, or set the +TICALCS_CFLAGS and TICALCS_LIBS environment variables by hand." "$LINENO" 5 +fi + +# Tools used for building the Windows installer + +if test "x$gtk_target" = "xwin32" ; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="objdump" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args. +set dummy ${ac_tool_prefix}windres; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_WINDRES+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$WINDRES"; then + ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_WINDRES="${ac_tool_prefix}windres" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +WINDRES=$ac_cv_prog_WINDRES +if test -n "$WINDRES"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WINDRES" >&5 +$as_echo "$WINDRES" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_WINDRES"; then + ac_ct_WINDRES=$WINDRES + # Extract the first word of "windres", so it can be a program name with args. +set dummy windres; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_WINDRES+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_WINDRES"; then + ac_cv_prog_ac_ct_WINDRES="$ac_ct_WINDRES" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_WINDRES="windres" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_WINDRES=$ac_cv_prog_ac_ct_WINDRES +if test -n "$ac_ct_WINDRES"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_WINDRES" >&5 +$as_echo "$ac_ct_WINDRES" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_WINDRES" = x; then + WINDRES="windres" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + WINDRES=$ac_ct_WINDRES + fi +else + WINDRES="$ac_cv_prog_WINDRES" +fi + + # Extract the first word of "makensis", so it can be a program name with args. +set dummy makensis; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MAKENSIS+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MAKENSIS"; then + ac_cv_prog_MAKENSIS="$MAKENSIS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_MAKENSIS="makensis" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MAKENSIS=$ac_cv_prog_MAKENSIS +if test -n "$MAKENSIS"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAKENSIS" >&5 +$as_echo "$MAKENSIS" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find GTK+ runtime libraries" >&5 +$as_echo_n "checking where to find GTK+ runtime libraries... " >&6; } + if test "x$GTK_BINDIR" = "x" ; then + prefix=`$PKG_CONFIG --variable=exec_prefix gtk+-2.0` + test "x$prefix" != "x" && GTK_BINDIR="$prefix/bin" + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GTK_BINDIR" >&5 +$as_echo "$GTK_BINDIR" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking where to find ticalcs2 runtime libraries" >&5 +$as_echo_n "checking where to find ticalcs2 runtime libraries... " >&6; } + if test "x$TICALCS_BINDIR" = "x" ; then + prefix=`$PKG_CONFIG --variable=exec_prefix ticalcs2` + test "x$prefix" != "x" && TICALCS_BINDIR="$prefix/bin" + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TICALCS_BINDIR" >&5 +$as_echo "$TICALCS_BINDIR" >&6; } + + if test "x$DLLPATH" = "x" ; then + DLLPATH='${GTK_BINDIR}'$PATH_SEPARATOR'${TICALCS_BINDIR}' + fi + + + +fi + +# Checks for header files + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + + +# Checks for system and compiler characteristics + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if ${ac_cv_c_bigendian+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h +;; #( + no) + ;; #( + universal) + +$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5 +$as_echo_n "checking for C/C++ restrict keyword... " >&6; } +if ${ac_cv_c_restrict+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_restrict=no + # The order here caters to the fact that C++ does not require restrict. + for ac_kw in __restrict __restrict__ _Restrict restrict; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +typedef int * int_ptr; + int foo (int_ptr $ac_kw ip) { + return ip[0]; + } +int +main () +{ +int s[1]; + int * $ac_kw t = s; + t[0] = 0; + return foo(t) + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_restrict=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_restrict" != no && break + done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5 +$as_echo "$ac_cv_c_restrict" >&6; } + + case $ac_cv_c_restrict in + restrict) ;; + no) $as_echo "#define restrict /**/" >>confdefs.h + ;; + *) cat >>confdefs.h <<_ACEOF +#define restrict $ac_cv_c_restrict +_ACEOF + ;; + esac + + + ac_fn_c_check_type "$LINENO" "uintptr_t" "ac_cv_type_uintptr_t" "$ac_includes_default" +if test "x$ac_cv_type_uintptr_t" = xyes; then : + +$as_echo "#define HAVE_UINTPTR_T 1" >>confdefs.h + +else + for ac_type in 'unsigned int' 'unsigned long int' \ + 'unsigned long long int'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(sizeof (void *) <= sizeof ($ac_type))]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +cat >>confdefs.h <<_ACEOF +#define uintptr_t $ac_type +_ACEOF + + ac_type= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test -z "$ac_type" && break + done +fi + + + +# Output + +ac_config_headers="$ac_config_headers config.h" + +ac_config_files="$ac_config_files Makefile emu/Makefile db/Makefile data/Makefile gui/Makefile gui/tilem2.rc installer/win32/Makefile installer/win32/installer.nsi" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by TilEm $as_me 2.0, which was +generated by GNU Autoconf 2.68. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to . +TilEm home page: ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +TilEm config.status 2.0 +configured by $0, generated by GNU Autoconf 2.68, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2010 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "emu/Makefile") CONFIG_FILES="$CONFIG_FILES emu/Makefile" ;; + "db/Makefile") CONFIG_FILES="$CONFIG_FILES db/Makefile" ;; + "data/Makefile") CONFIG_FILES="$CONFIG_FILES data/Makefile" ;; + "gui/Makefile") CONFIG_FILES="$CONFIG_FILES gui/Makefile" ;; + "gui/tilem2.rc") CONFIG_FILES="$CONFIG_FILES gui/tilem2.rc" ;; + "installer/win32/Makefile") CONFIG_FILES="$CONFIG_FILES installer/win32/Makefile" ;; + "installer/win32/installer.nsi") CONFIG_FILES="$CONFIG_FILES installer/win32/installer.nsi" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/tool/tilem-src/configure.ac b/tool/tilem-src/configure.ac new file mode 100644 index 0000000..88e2306 --- /dev/null +++ b/tool/tilem-src/configure.ac @@ -0,0 +1,172 @@ +AC_PREREQ(2.67) +AC_INIT([TilEm], [2.0], [tilem-devel@lists.sourceforge.net], + [tilem], [http://tilem.sourceforge.net/]) +AC_CONFIG_SRCDIR([emu/tilem.h]) + +# Checks for programs + +AC_PROG_CC +AC_PROG_CPP +AC_ARG_VAR(OPT_CFLAGS, + [Additional C compiler flags used for optimizing critical areas of + the code (default: -O3 if using GCC)]) +if test "x$GCC" = "xyes" ; then + CFLAGS="$CFLAGS -W -Wall -Wwrite-strings" + if test "x$OPT_CFLAGS" = "x" ; then + OPT_CFLAGS="-O3" + fi +fi + +AC_CHECK_TOOL(AR, [ar], [false]) +AC_ARG_VAR(AR, [Static library archiver]) +AC_ARG_VAR(AR_FLAGS, [Flags to pass to ar to build a static library]) +if test "x$AR_FLAGS" = "x" ; then + AR_FLAGS=cru +fi + +AC_PROG_RANLIB +AC_ARG_VAR(RANLIB, [Program to make a static library linkable]) + +AC_PROG_INSTALL +AC_PROG_MAKE_SET + +AC_CHECK_PROG([UPDATE_DESKTOP_DATABASE], + [update-desktop-database], [update-desktop-database], [:]) +AC_CHECK_PROG([UPDATE_MIME_DATABASE], + [update-mime-database], [update-mime-database], [:]) + +# Checks for libraries + +m4_define(with_flags, [ + save_cflags="$CFLAGS" + save_libs="$LIBS" + CFLAGS="$CFLAGS $$1_CFLAGS" + LIBS="$LIBS $$1_LIBS" + $2 + CFLAGS="$save_cflags" + LIBS="$save_libs" +]) + +# GLib and GTK+ + +PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 2.6.0 + glib-2.0 >= 2.12.0 + gthread-2.0) + +AC_ARG_ENABLE([gtk-deprecated], + AS_HELP_STRING([--disable-gtk-deprecated], [Disable deprecated GTK+ API]), + [ enable_gtk_deprecated=$enableval ], [ enable_gtk_deprecated=yes ]) +if test "x$enable_gtk_deprecated" = "xno" ; then + GTK_CFLAGS="$GTK_CFLAGS -DG_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGSEAL_ENABLE" +fi + +# If using the native Windows version of GTK+, be sure to use +# -mms-bitfields for all compilation. Also, use -mwindows for linking +# GUI programs. + +# (If not using pkg-config, you're on your own) + +if test "x$PKG_CONFIG" != "x" ; then + gtk_target=`$PKG_CONFIG --variable=target gtk+-2.0` +fi + +if test "x$gtk_target" = "xwin32" && test "x$GCC" = "xyes" ; then + CFLAGS="$CFLAGS -mms-bitfields" + GUI_LDFLAGS="-mwindows" + LIBS="-lcomdlg32 -lshell32 -lole32 $LIBS" + gui_extra_objects="tilem2rc.o" +else + GUI_LDFLAGS="" + gui_extra_objects="" +fi + +AC_SUBST(GUI_LDFLAGS) +AC_SUBST(gui_extra_objects) + +with_flags(GTK, + [ AC_CHECK_FUNC(gtk_init, [ have_gtk=yes ], [ have_gtk=no ]) ]) +if test "x$have_gtk" != "xyes" ; then + AC_MSG_ERROR([GTK+ 2.x libraries not found or not usable. +You must install a recent version of GTK+ 2.x, including the +-dev/-devel packages if appropriate.]) +fi + +# Libticalcs2 and related libraries + +PKG_CHECK_MODULES(TICALCS, ticalcs2 ticables2 tifiles2 ticonv, + [ have_ticalcs=maybe ], [ have_ticalcs=no ]) + +if test "x$have_ticalcs" = "xmaybe" ; then + with_flags(TICALCS, + [ AC_CHECK_FUNC(ticalcs_library_init, [ have_ticalcs=yes ], [ have_ticalcs=no ]) ]) +fi + +if test "x$have_ticalcs" != "xyes" ; then + AC_MSG_ERROR([libticalcs2 not found or not usable. + +$TICALCS_PKG_ERRORS + +You must install libticalcs2, libticables2, libtifiles2, and libticonv +(including the -dev/-devel packages if appropriate.) These libraries +are available from . + +If you have installed the libraries in a non-standard location (or if +you're cross-compiling), you will need to add the location of +ticalcs2.pc to your PKG_CONFIG_PATH environment variable, or set the +TICALCS_CFLAGS and TICALCS_LIBS environment variables by hand.]) +fi + +# Tools used for building the Windows installer + +if test "x$gtk_target" = "xwin32" ; then + AC_CHECK_TOOL([STRIP], [strip], [:]) + AC_CHECK_TOOL([OBJDUMP], [objdump], [objdump]) + AC_CHECK_TOOL([WINDRES], [windres], [windres]) + AC_CHECK_PROG([MAKENSIS], [makensis], [makensis]) + AC_PROG_LN_S + + AC_MSG_CHECKING([where to find GTK+ runtime libraries]) + if test "x$GTK_BINDIR" = "x" ; then + prefix=`$PKG_CONFIG --variable=exec_prefix gtk+-2.0` + test "x$prefix" != "x" && GTK_BINDIR="$prefix/bin" + fi + AC_MSG_RESULT([$GTK_BINDIR]) + + AC_MSG_CHECKING([where to find ticalcs2 runtime libraries]) + if test "x$TICALCS_BINDIR" = "x" ; then + prefix=`$PKG_CONFIG --variable=exec_prefix ticalcs2` + test "x$prefix" != "x" && TICALCS_BINDIR="$prefix/bin" + fi + AC_MSG_RESULT([$TICALCS_BINDIR]) + + if test "x$DLLPATH" = "x" ; then + DLLPATH='${GTK_BINDIR}'$PATH_SEPARATOR'${TICALCS_BINDIR}' + fi + AC_SUBST(GTK_BINDIR) + AC_SUBST(TICALCS_BINDIR) + AC_SUBST(DLLPATH) +fi + +# Checks for header files + +AC_HEADER_STDC + +# Checks for system and compiler characteristics + +AC_C_BIGENDIAN +AC_C_INLINE +AC_C_RESTRICT +AC_TYPE_UINTPTR_T + +# Output + +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile + emu/Makefile + db/Makefile + data/Makefile + gui/Makefile + gui/tilem2.rc + installer/win32/Makefile + installer/win32/installer.nsi]) +AC_OUTPUT diff --git a/tool/tilem-src/data/Makefile.in b/tool/tilem-src/data/Makefile.in new file mode 100644 index 0000000..c41e175 --- /dev/null +++ b/tool/tilem-src/data/Makefile.in @@ -0,0 +1,114 @@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +bindir = @bindir@ +datadir = @datadir@ +pkgdatadir = @datadir@/tilem2 +mandir = @mandir@ +icondir = @datadir@/icons +applicationsdir = @datadir@/applications +mimedir = @datadir@/mime + +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +@SET_MAKE@ + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +SHELL = @SHELL@ +UPDATE_DESKTOP_DATABASE = @UPDATE_DESKTOP_DATABASE@ +UPDATE_MIME_DATABASE = @UPDATE_MIME_DATABASE@ + +# Custom action and status icons go in these directories. These icons +# are only used by TilEm, so they are installed in DATADIR/tilem2/icons. +pkg_icondirs = hicolor/16x16/actions \ + hicolor/16x16/status \ + hicolor/24x24/actions + +# Application icons go in these directories; these icons will be +# installed in DATADIR/icons so they are visible to other programs +# (e.g. desktop application menus.) +shared_icondirs = hicolor/16x16/apps \ + hicolor/22x22/apps \ + hicolor/24x24/apps \ + hicolor/32x32/apps \ + hicolor/48x48/apps + +all: + @echo 'Nothing to do' + +install: + $(INSTALL) -d -m 755 $(DESTDIR)$(pkgdatadir) + $(INSTALL_DATA) $(srcdir)/keybindings.ini $(DESTDIR)$(pkgdatadir) + $(INSTALL) -d -m 755 $(DESTDIR)$(pkgdatadir)/symbols + set -e ; for i in $(srcdir)/symbols/*.sym ; do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(pkgdatadir)/symbols ; \ + done + $(INSTALL) -d -m 755 $(DESTDIR)$(pkgdatadir)/skins + set -e ; for i in $(srcdir)/skins/*.skn ; do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(pkgdatadir)/skins ; \ + done + set -e ; for i in $(pkg_icondirs) ; do \ + $(INSTALL) -d -m 755 $(DESTDIR)$(pkgdatadir)/icons/$$i ; \ + for j in $(srcdir)/icons/$$i/*.png ; do \ + $(INSTALL_DATA) $$j $(DESTDIR)$(pkgdatadir)/icons/$$i ; \ + done ; \ + done + set -e ; for i in $(shared_icondirs) ; do \ + $(INSTALL) -d -m 755 $(DESTDIR)$(icondir)/$$i ; \ + for j in $(srcdir)/icons/$$i/*.png ; do \ + $(INSTALL_DATA) $$j $(DESTDIR)$(icondir)/$$i ; \ + done ; \ + done + $(INSTALL_DATA) $(srcdir)/icons/hicolor/index.theme $(DESTDIR)$(pkgdatadir)/icons/hicolor + $(INSTALL) -d -m 755 $(DESTDIR)$(applicationsdir) + $(INSTALL_DATA) $(srcdir)/desktop/tilem2.desktop $(DESTDIR)$(applicationsdir) + $(INSTALL) -d -m 755 $(DESTDIR)$(mimedir)/packages + $(INSTALL_DATA) $(srcdir)/desktop/tilem2.xml $(DESTDIR)$(mimedir)/packages + [ -n "$(DESTDIR)" ] || $(UPDATE_DESKTOP_DATABASE) $(applicationsdir) + [ -n "$(DESTDIR)" ] || $(UPDATE_MIME_DATABASE) $(mimedir) + +uninstall: + rm -f $(DESTDIR)$(pkgdatadir)/keybindings.ini + set -e ; for i in $(srcdir)/symbols/*.sym ; do \ + rm -f $(DESTDIR)$(pkgdatadir)/symbols/`basename $$i` ; \ + done + set -e ; for i in $(srcdir)/skins/*.skn ; do \ + rm -f $(DESTDIR)$(pkgdatadir)/skins/`basename $$i` ; \ + done + set -e ; for i in $(pkg_icondirs) ; do \ + for j in $(srcdir)/icons/$$i/*.png ; do \ + rm -f $(DESTDIR)$(pkgdatadir)/icons/$$i/`basename $$j` ; \ + done ; \ + done + set -e ; for i in $(shared_icondirs) ; do \ + for j in $(srcdir)/icons/$$i/*.png ; do \ + rm -f $(DESTDIR)$(icondir)/$$i/`basename $$j` ; \ + done ; \ + done + -for i in $(pkg_icondirs) ; do \ + rmdir $(DESTDIR)$(pkgdatadir)/icons/$$i ; \ + done + -rmdir $(DESTDIR)$(pkgdatadir)/icons/hicolor/16x16 + -rmdir $(DESTDIR)$(pkgdatadir)/icons/hicolor/24x24 + rm -f $(DESTDIR)$(pkgdatadir)/icons/hicolor/index.theme + -rmdir $(DESTDIR)$(pkgdatadir)/icons/hicolor + -rmdir $(DESTDIR)$(pkgdatadir)/icons + -rmdir $(DESTDIR)$(pkgdatadir)/symbols + -rmdir $(DESTDIR)$(pkgdatadir)/skins + -rmdir $(DESTDIR)$(pkgdatadir) + rm -f $(DESTDIR)$(applicationsdir)/tilem2.desktop + rm -f $(DESTDIR)$(mimedir)/packages/tilem2.xml + [ -n "$(DESTDIR)" ] || $(UPDATE_DESKTOP_DATABASE) $(applicationsdir) + [ -n "$(DESTDIR)" ] || $(UPDATE_MIME_DATABASE) $(mimedir) + +Makefile: Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status + +$(top_builddir)/config.status: $(top_srcdir)/configure + cd $(top_builddir) && $(SHELL) ./config.status --recheck + +.PRECIOUS: Makefile $(top_builddir)/config.status +.PHONY: all install uninstall diff --git a/tool/tilem-src/data/desktop/tilem2.desktop b/tool/tilem-src/data/desktop/tilem2.desktop new file mode 100644 index 0000000..8118c97 --- /dev/null +++ b/tool/tilem-src/data/desktop/tilem2.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=TilEm +Comment=Graphing calculator emulator +Exec=tilem2 %F +Icon=tilem +MimeType=application/x-tigroup;application/x-ti73-variables;application/x-ti73-program;application/x-ti73-backup;application/x-ti73-app;application/x-ti73-os;application/x-ti81-program;application/x-ti82-variables;application/x-ti82-program;application/x-ti82-backup;application/x-ti83-variables;application/x-ti83-program;application/x-ti83-backup;application/x-ti83plus-variables;application/x-ti83plus-program;application/x-ti83plus-app;application/x-ti83plus-os;application/x-ti85-variables;application/x-ti85-program;application/x-ti85-backup;application/x-ti86-variables;application/x-ti86-program;application/x-ti86-backup; +StartupNotify=true +Terminal=false +Type=Application +Categories=Education;Science;Math;Emulator; diff --git a/tool/tilem-src/data/desktop/tilem2.xml b/tool/tilem-src/data/desktop/tilem2.xml new file mode 100644 index 0000000..7dae83c --- /dev/null +++ b/tool/tilem-src/data/desktop/tilem2.xml @@ -0,0 +1,322 @@ + + + + + TI calculator group file + + + + + TI-73 variable + + + + + + + + + + + + + + + + + + + + + + TI-73 program + + + + + + + + + + + + TI-73 memory backup + + + + + + + + + + + TI-73 Flash application + + + + + + + + + TI-73 operating system + + + + + + + + + + TI-81 program + + + + + + + + TI-82 variable + + + + + + + + + + + + + + + + + + + + TI-82 program + + + + + + + + + + + + TI-82 memory backup + + + + + + + + + + + + TI-83 variable + + + + + + + + + + + + + + + + + + + + + + TI-83 program + + + + + + + + + + + + TI-83 memory backup + + + + + + + + + + + + TI-83/84 Plus variable + + + + + + + + + + + + + + + + + + + + + + + + + TI-83/84 Plus program + + + + + + + + + + + + TI-83/84 Plus Flash application + + + + + + + + + TI-83/84 Plus operating system + + + + + + + + + + TI-85 variable + + + + + + + + + + + + + + + + + + + + + + + + TI-85 program + + + + + + + + + + + + TI-85 memory backup + + + + + + + + + + + + TI-86 variable + + + + + + + + + + + + + + + + + + + + + + + + TI-86 program + + + + + + + + + + + + TI-86 memory backup + + + + + + + + + + diff --git a/tool/tilem-src/data/icons-svg/breakpoint.svg b/tool/tilem-src/data/icons-svg/breakpoint.svg new file mode 100644 index 0000000..e3c55fb --- /dev/null +++ b/tool/tilem-src/data/icons-svg/breakpoint.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/tool/tilem-src/data/icons-svg/pc-arrow.svg b/tool/tilem-src/data/icons-svg/pc-arrow.svg new file mode 100644 index 0000000..13874e3 --- /dev/null +++ b/tool/tilem-src/data/icons-svg/pc-arrow.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/tool/tilem-src/data/icons-svg/stepicons-16.svg b/tool/tilem-src/data/icons-svg/stepicons-16.svg new file mode 100644 index 0000000..c89f3e4 --- /dev/null +++ b/tool/tilem-src/data/icons-svg/stepicons-16.svg @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tool/tilem-src/data/icons-svg/stepicons-24.svg b/tool/tilem-src/data/icons-svg/stepicons-24.svg new file mode 100644 index 0000000..d4b9f4b --- /dev/null +++ b/tool/tilem-src/data/icons-svg/stepicons-24.svg @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tool/tilem-src/data/icons/hicolor/16x16/actions/tilem-db-finish.png b/tool/tilem-src/data/icons/hicolor/16x16/actions/tilem-db-finish.png new file mode 100644 index 0000000..bd51d31 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/16x16/actions/tilem-db-finish.png differ diff --git a/tool/tilem-src/data/icons/hicolor/16x16/actions/tilem-db-step-over.png b/tool/tilem-src/data/icons/hicolor/16x16/actions/tilem-db-step-over.png new file mode 100644 index 0000000..19f79af Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/16x16/actions/tilem-db-step-over.png differ diff --git a/tool/tilem-src/data/icons/hicolor/16x16/actions/tilem-db-step.png b/tool/tilem-src/data/icons/hicolor/16x16/actions/tilem-db-step.png new file mode 100644 index 0000000..4b8e1d7 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/16x16/actions/tilem-db-step.png differ diff --git a/tool/tilem-src/data/icons/hicolor/16x16/apps/tilem.png b/tool/tilem-src/data/icons/hicolor/16x16/apps/tilem.png new file mode 100644 index 0000000..0023cd9 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/16x16/apps/tilem.png differ diff --git a/tool/tilem-src/data/icons/hicolor/16x16/status/tilem-disasm-break-pc.png b/tool/tilem-src/data/icons/hicolor/16x16/status/tilem-disasm-break-pc.png new file mode 100644 index 0000000..35aaa06 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/16x16/status/tilem-disasm-break-pc.png differ diff --git a/tool/tilem-src/data/icons/hicolor/16x16/status/tilem-disasm-break.png b/tool/tilem-src/data/icons/hicolor/16x16/status/tilem-disasm-break.png new file mode 100644 index 0000000..9e9dc33 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/16x16/status/tilem-disasm-break.png differ diff --git a/tool/tilem-src/data/icons/hicolor/16x16/status/tilem-disasm-pc.png b/tool/tilem-src/data/icons/hicolor/16x16/status/tilem-disasm-pc.png new file mode 100644 index 0000000..e6c5594 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/16x16/status/tilem-disasm-pc.png differ diff --git a/tool/tilem-src/data/icons/hicolor/22x22/apps/tilem.png b/tool/tilem-src/data/icons/hicolor/22x22/apps/tilem.png new file mode 100644 index 0000000..59a0a9b Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/22x22/apps/tilem.png differ diff --git a/tool/tilem-src/data/icons/hicolor/24x24/actions/tilem-db-finish.png b/tool/tilem-src/data/icons/hicolor/24x24/actions/tilem-db-finish.png new file mode 100644 index 0000000..8eeb723 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/24x24/actions/tilem-db-finish.png differ diff --git a/tool/tilem-src/data/icons/hicolor/24x24/actions/tilem-db-step-over.png b/tool/tilem-src/data/icons/hicolor/24x24/actions/tilem-db-step-over.png new file mode 100644 index 0000000..d78ffe4 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/24x24/actions/tilem-db-step-over.png differ diff --git a/tool/tilem-src/data/icons/hicolor/24x24/actions/tilem-db-step.png b/tool/tilem-src/data/icons/hicolor/24x24/actions/tilem-db-step.png new file mode 100644 index 0000000..96b9f9d Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/24x24/actions/tilem-db-step.png differ diff --git a/tool/tilem-src/data/icons/hicolor/24x24/apps/tilem.png b/tool/tilem-src/data/icons/hicolor/24x24/apps/tilem.png new file mode 100644 index 0000000..6e19b2a Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/24x24/apps/tilem.png differ diff --git a/tool/tilem-src/data/icons/hicolor/32x32/apps/tilem.png b/tool/tilem-src/data/icons/hicolor/32x32/apps/tilem.png new file mode 100644 index 0000000..8014816 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/32x32/apps/tilem.png differ diff --git a/tool/tilem-src/data/icons/hicolor/48x48/apps/tilem.png b/tool/tilem-src/data/icons/hicolor/48x48/apps/tilem.png new file mode 100644 index 0000000..852b196 Binary files /dev/null and b/tool/tilem-src/data/icons/hicolor/48x48/apps/tilem.png differ diff --git a/tool/tilem-src/data/icons/hicolor/index.theme b/tool/tilem-src/data/icons/hicolor/index.theme new file mode 100644 index 0000000..cbb495c --- /dev/null +++ b/tool/tilem-src/data/icons/hicolor/index.theme @@ -0,0 +1,44 @@ +[Icon Theme] +Name=hicolor +Comment=Default Theme +Directories=16x16/actions,16x16/apps,16x16/status,22x22/apps,24x24/actions,24x24/apps,32x32/apps,48x48/apps + +[16x16/actions] +Size=16 +Context=Actions +Type=Threshold + +[16x16/apps] +Size=16 +Context=Applications +Type=Threshold + +[16x16/status] +Size=16 +Context=Status +Type=Threshold + +[22x22/apps] +Size=22 +Context=Applications +Type=Threshold + +[24x24/actions] +Size=24 +Context=Actions +Type=Threshold + +[24x24/apps] +Size=24 +Context=Applications +Type=Threshold + +[32x32/apps] +Size=32 +Context=Applications +Type=Threshold + +[48x48/apps] +Size=48 +Context=Applications +Type=Threshold diff --git a/tool/tilem-src/data/keybindings.ini b/tool/tilem-src/data/keybindings.ini new file mode 100644 index 0000000..64f3583 --- /dev/null +++ b/tool/tilem-src/data/keybindings.ini @@ -0,0 +1,555 @@ +[common] +Up = Up +KP_Up = Up +Down = Down +KP_Down = Down +Left = Left +KP_Left = Left +Right = Right +KP_Right = Right +Shift+Up = 2nd, Up +Shift+Down = 2nd, Down +Tab = 2nd +KP_Tab = 2nd +ISO_Left_Tab = 2nd +Delete = Del +KP_Delete = Del +BackSpace = Left, Del +Ctrl+BackSpace = Clear +Ctrl+Delete = Clear +Ctrl+KP_Delete = Clear +0 = 0 +KP_0 = 0 +1 = 1 +KP_1 = 1 +2 = 2 +KP_2 = 2 +3 = 3 +KP_3 = 3 +4 = 4 +KP_4 = 4 +5 = 5 +KP_5 = 5 +6 = 6 +KP_6 = 6 +7 = 7 +KP_7 = 7 +8 = 8 +KP_8 = 8 +9 = 9 +KP_9 = 9 +period = DecPnt +KP_Decimal = DecPnt +asciitilde = Chs +dead_tilde = Chs +plusminus = Chs +Shift+KP_Subtract = Chs +dollar = 2nd, Chs +plus = Add +KP_Add = Add +minus = Sub +KP_Subtract = Sub +asterisk = Mul +KP_Multiply = Mul +slash = Div +KP_Divide = Div +asciicircum = Power +dead_circumflex = Power +twosuperior = Square +Ctrl+2 = Square +parenleft = LParen +parenright = RParen +greater = Store +less = 2nd, Store +F12 = On +Shift+F12 = 2nd, On +Return = Enter +KP_Enter = Enter +ISO_Enter = Enter +Shift+Return = 2nd, Enter +Shift+KP_Enter = 2nd, Enter +Shift+ISO_Enter = 2nd, Enter + +[ti73] +INHERIT = common +F1 = YEqu +F2 = Window +F3 = Zoom +F4 = Trace +F5 = Graph +Shift+F1 = 2nd, YEqu +Shift+F2 = 2nd, Window +Shift+F3 = 2nd, Zoom +Shift+F4 = 2nd, Trace +Shift+F5 = 2nd, Graph +Home = 2nd, Left +End = 2nd, Right +F11 = Mode +Shift+Escape = 2nd, Mode +Insert = 2nd, Del +KP_Insert = 2nd, Del +m = Math +Menu = 2nd, Math +apostrophe = 2nd, Math +d = Draw +l = List +ampersand = 2nd, Power +EuroSign = 2nd, Power +F8 = Prgm +p = Prgm +Ctrl+Tab = 2nd, Prgm +Ctrl+KP_Tab = 2nd, Prgm +Ctrl+ISO_Left_Tab = 2nd, Prgm +F7 = Apps +Escape = Clear +underscore = Unit +bar = FracSlash +brokenbar = FracSlash +onehalf = FracSlash +backslash = 2nd, FracSlash +onesuperior = 2nd, FracSlash +Ctrl+1 = 2nd, FracSlash +f = FracDec +numbersign = 2nd, FracDec +a = MixSimp +c = Const +s = Simp +percent = Percent +x = VarX +comma = Comma + +[ti76] +INHERIT = common +F1 = YEqu +F2 = Window +F3 = Zoom +F4 = Trace +F5 = Graph +Shift+F1 = 2nd, YEqu +Shift+F2 = 2nd, Window +Shift+F3 = 2nd, Zoom +Shift+F4 = 2nd, Trace +Shift+F5 = 2nd, Graph +Home = 2nd, Left +KP_Home = 2nd, Left +End = 2nd, Right +KP_End = 2nd, Right +F11 = Mode +Shift+Escape = 2nd, Mode +Insert = 2nd, Del +KP_Insert = 2nd, Del +Menu = Alpha +apostrophe = Alpha +x = Graphvar +F10 = Stat +F6 = Math +F7 = Matrix +F8 = Prgm +F9 = Vars +Escape = Clear +backslash = Recip +onesuperior = Recip +Ctrl+1 = Recip +s = Sin +c = Cos +t = Tan +numbersign = 2nd, Power +comma = Comma +ampersand = 2nd, Comma +EuroSign = 2nd, Comma +braceleft = 2nd, LParen +braceright = 2nd, RParen +e = 2nd, Div +o = Log +u = 2nd, 7 +v = 2nd, 8 +w = 2nd, 9 +l = Ln +p = Prgm +Ctrl+Tab = 2nd, 0 +Ctrl+KP_Tab = 2nd, 0 +Ctrl+ISO_Left_Tab = 2nd, 0 + +[ti81] +INHERIT = common +F1 = YEqu +F2 = Range +F3 = Zoom +F4 = Trace +F5 = Graph +Shift+F1 = 2nd, YEqu +Shift+F2 = 2nd, Range +Shift+F3 = 2nd, Zoom +Shift+F4 = 2nd, Trace +Shift+F5 = 2nd, Graph +Insert = Ins +KP_Insert = Ins +Menu = Alpha +apostrophe = Alpha +x = Graphvar +F11 = Mode +F6 = Math +F7 = Matrix +F8 = Prgm +F9 = Vars +Escape = Clear +Shift+Escape = 2nd, Clear +backslash = Recip +onesuperior = Recip +Ctrl+1 = Recip +bar = 2nd, Recip +brokenbar = 2nd, Recip +s = Sin +c = Cos +t = Tan +p = Prgm +numbersign = 2nd, Power +ampersand = EE +EuroSign = EE +o = Log +l = Ln +A = Alpha, Math +B = Alpha, Matrix +C = Alpha, Prgm +D = Alpha, Recip +E = Alpha, Sin +F = Alpha, Cos +G = Alpha, Tan +H = Alpha, Power +I = Alpha, Square +J = Alpha, EE +K = Alpha, LParen +L = Alpha, RParen +M = Alpha, Div +N = Alpha, Log +O = Alpha, 7 +P = Alpha, 8 +Q = Alpha, 9 +R = Alpha, Mul +S = Alpha, Ln +T = Alpha, 4 +U = Alpha, 5 +V = Alpha, 6 +W = Alpha, Sub +X = Alpha, Store +Y = Alpha, 1 +Z = Alpha, 2 +at = Alpha, 3 +quotedbl = Alpha, Add +space = Alpha, 0 +comma = Alpha, DecPnt +question = Alpha, Chs + +[ti82] +INHERIT = common +F1 = YEqu +F2 = Window +F3 = Zoom +F4 = Trace +F5 = Graph +Shift+F1 = 2nd, YEqu +Shift+F2 = 2nd, Window +Shift+F3 = 2nd, Zoom +Shift+F4 = 2nd, Trace +Shift+F5 = 2nd, Graph +Home = 2nd, Left +KP_Home = 2nd, Left +End = 2nd, Right +KP_End = 2nd, Right +F11 = Mode +Shift+Escape = 2nd, Mode +Insert = 2nd, Del +KP_Insert = 2nd, Del +Menu = Alpha +apostrophe = Alpha +x = Graphvar +F10 = Stat +F6 = Math +F7 = Matrix +F8 = Prgm +F9 = Vars +Escape = Clear +backslash = Recip +onesuperior = Recip +Ctrl+1 = Recip +bar = 2nd, Recip +brokenbar = 2nd, Recip +s = Sin +c = Cos +t = Tan +numbersign = 2nd, Power +comma = Comma +ampersand = 2nd, Comma +EuroSign = 2nd, Comma +braceleft = 2nd, LParen +braceright = 2nd, RParen +o = Log +u = 2nd, 7 +v = 2nd, 8 +n = 2nd, 9 +bracketleft = 2nd, Mul +l = Ln +p = Prgm +bracketright = 2nd, Sub +A = Alpha, Math +B = Alpha, Matrix +C = Alpha, Prgm +D = Alpha, Recip +E = Alpha, Sin +F = Alpha, Cos +G = Alpha, Tan +H = Alpha, Power +I = Alpha, Square +J = Alpha, Comma +K = Alpha, LParen +L = Alpha, RParen +M = Alpha, Div +N = Alpha, Log +O = Alpha, 7 +P = Alpha, 8 +Q = Alpha, 9 +R = Alpha, Mul +S = Alpha, Ln +T = Alpha, 4 +U = Alpha, 5 +V = Alpha, 6 +W = Alpha, Sub +X = Alpha, Store +Y = Alpha, 1 +Z = Alpha, 2 +at = Alpha, 3 +quotedbl = Alpha, Add +space = Alpha, 0 +colon = Alpha, DecPnt +question = Alpha, Chs + +[ti83] +INHERIT = common +F1 = YEqu +F2 = Window +F3 = Zoom +F4 = Trace +F5 = Graph +Shift+F1 = 2nd, YEqu +Shift+F2 = 2nd, Window +Shift+F3 = 2nd, Zoom +Shift+F4 = 2nd, Trace +Shift+F5 = 2nd, Graph +Home = 2nd, Left +KP_Home = 2nd, Left +End = 2nd, Right +KP_End = 2nd, Right +Page_Up = Alpha, Up +KP_Page_Up = Alpha, Up +Page_Down = Alpha, Down +KP_Page_Down = Alpha, Down +F11 = Mode +Shift+Escape = 2nd, Mode +Insert = 2nd, Del +KP_Insert = 2nd, Del +Menu = Alpha +apostrophe = Alpha +x = Graphvar +F10 = Stat +F6 = Math +F7 = Matrix +F8 = Prgm +F9 = Vars +Escape = Clear +backslash = Recip +onesuperior = Recip +Ctrl+1 = Recip +s = Sin +c = Cos +t = Tan +numbersign = 2nd, Power +comma = Comma +ampersand = 2nd, Comma +EuroSign = 2nd, Comma +braceleft = 2nd, LParen +braceright = 2nd, RParen +e = 2nd, Div +o = Log +u = 2nd, 7 +v = 2nd, 8 +w = 2nd, 9 +p = Prgm +bracketleft = 2nd, Mul +l = Ln +bracketright = 2nd, Sub +Ctrl+Tab = 2nd, 0 +Ctrl+KP_Tab = 2nd, 0 +Ctrl+ISO_Left_Tab = 2nd, 0 +i = 2nd, DecPnt +A = Alpha, Math +B = Alpha, Matrix +C = Alpha, Prgm +D = Alpha, Recip +E = Alpha, Sin +F = Alpha, Cos +G = Alpha, Tan +H = Alpha, Power +I = Alpha, Square +J = Alpha, Comma +K = Alpha, LParen +L = Alpha, RParen +M = Alpha, Div +N = Alpha, Log +O = Alpha, 7 +P = Alpha, 8 +Q = Alpha, 9 +R = Alpha, Mul +S = Alpha, Ln +T = Alpha, 4 +U = Alpha, 5 +V = Alpha, 6 +W = Alpha, Sub +X = Alpha, Store +Y = Alpha, 1 +Z = Alpha, 2 +at = Alpha, 3 +quotedbl = Alpha, Add +space = Alpha, 0 +colon = Alpha, DecPnt +question = Alpha, Chs + +[ti83p] +INHERIT = ti83 +CapsLock+a = Alpha, Alpha, Math +CapsLock+b = Alpha, Alpha, Apps +CapsLock+c = Alpha, Alpha, Prgm +CapsLock+d = Alpha, Alpha, Recip +CapsLock+e = Alpha, Alpha, Sin +CapsLock+f = Alpha, Alpha, Cos +CapsLock+g = Alpha, Alpha, Tan +CapsLock+h = Alpha, Alpha, Power +CapsLock+i = Alpha, Alpha, Square +CapsLock+j = Alpha, Alpha, Comma +CapsLock+k = Alpha, Alpha, LParen +CapsLock+l = Alpha, Alpha, RParen +CapsLock+m = Alpha, Alpha, Div +CapsLock+n = Alpha, Alpha, Log +CapsLock+o = Alpha, Alpha, 7 +CapsLock+p = Alpha, Alpha, 8 +CapsLock+q = Alpha, Alpha, 9 +CapsLock+r = Alpha, Alpha, Mul +CapsLock+s = Alpha, Alpha, Ln +CapsLock+t = Alpha, Alpha, 4 +CapsLock+u = Alpha, Alpha, 5 +CapsLock+v = Alpha, Alpha, 6 +CapsLock+w = Alpha, Alpha, Sub +CapsLock+x = Alpha, Alpha, Store +CapsLock+y = Alpha, Alpha, 1 +CapsLock+z = Alpha, Alpha, 2 + +[ti83pse] +INHERIT = ti83p + +[ti84p] +INHERIT = ti83p + +[ti84pse] +INHERIT = ti83p + +[ti84pns] +INHERIT = ti83p + +[ti85] +INHERIT = common +F1 = F1 +F2 = F2 +F3 = F3 +F4 = F4 +F5 = F5 +Shift+F1 = 2nd, F1 +Shift+F2 = 2nd, F2 +Shift+F3 = 2nd, F3 +Shift+F4 = 2nd, F4 +Shift+F5 = 2nd, F5 +Home = 2nd, Left +KP_Home = 2nd, Left +End = 2nd, Right +KP_End = 2nd, Right +Escape = Exit +Shift+Escape = 2nd, Exit +Page_Down = More +KP_Page_Down = More +F11 = 2nd, More +Menu = Alpha +apostrophe = Alpha +Insert = 2nd, Del +KP_Insert = 2nd, Del +F6 = Graph +F7 = Stat +F8 = Prgm +F9 = Custom +Ctrl+Tab = 2nd, Custom +Ctrl+KP_Tab = 2nd, Custom +Ctrl+ISO_Left_Tab = 2nd, Custom +numbersign = 2nd, Power +ampersand = EE +EuroSign = EE +backslash = 2nd, EE +onesuperior = 2nd, EE +Ctrl+1 = 2nd, EE +bracketleft = 2nd, LParen +bracketright = 2nd, RParen +comma = Comma +colon = 2nd, DecPnt +A = Alpha, Log +B = Alpha, Sin +C = Alpha, Cos +D = Alpha, Tan +E = Alpha, Power +F = Alpha, Ln +G = Alpha, EE +H = Alpha, LParen +I = Alpha, RParen +J = Alpha, Div +K = Alpha, Square +L = Alpha, 7 +M = Alpha, 8 +N = Alpha, 9 +O = Alpha, Mul +P = Alpha, Comma +Q = Alpha, 4 +R = Alpha, 5 +S = Alpha, 6 +T = Alpha, Sub +U = Alpha, 1 +V = Alpha, 2 +W = Alpha, 3 +X = Alpha, Add +Y = Alpha, 0 +Z = Alpha, DecPnt +space = Alpha, Chs +equal = Alpha, Store +a = 2nd, Alpha, Log +b = 2nd, Alpha, Sin +c = 2nd, Alpha, Cos +d = 2nd, Alpha, Tan +e = 2nd, Alpha, Power +f = 2nd, Alpha, Ln +g = 2nd, Alpha, EE +h = 2nd, Alpha, LParen +i = 2nd, Alpha, RParen +j = 2nd, Alpha, Div +k = 2nd, Alpha, Square +l = 2nd, Alpha, 7 +m = 2nd, Alpha, 8 +n = 2nd, Alpha, 9 +o = 2nd, Alpha, Mul +p = 2nd, Alpha, Comma +q = 2nd, Alpha, 4 +r = 2nd, Alpha, 5 +s = 2nd, Alpha, 6 +t = 2nd, Alpha, Sub +u = 2nd, Alpha, 1 +v = 2nd, Alpha, 2 +w = 2nd, Alpha, 3 +x = 2nd, Alpha, Add +y = 2nd, Alpha, 0 +z = 2nd, Alpha, DecPnt + +[ti86] +INHERIT = ti85 diff --git a/tool/tilem-src/data/skins/README b/tool/tilem-src/data/skins/README new file mode 100644 index 0000000..4b4028d --- /dev/null +++ b/tool/tilem-src/data/skins/README @@ -0,0 +1,45 @@ +** About the format : ** + +When I started to work on tilem 2, I firstly decided to use the old tilem format (4 pixs around a lcd). +But that's not a good idea, and the new skin creation is too hard like this ... :( + +Finally, we (with Benjamin) have choosen the Tiemu skin format: +This format consists in a binary file containing skin information (author, name, model, size etc...) and in the second part :the image data. +With this format, it becomes really simple and fun to create a skin. +Tiemu Skinedit works perfectly for tilem 2 (how do you think I 've created these "officials skins :P). +I hope this new format will pleased to you. + +Thank you Julien Blache for his help and his authorization to use Tiemu skin format and skinops.c/skinops.h files. +Thank you Romain Lievin too. + + + +** About the image source : ** + +To find free files to generate skins is not so easy :| +A part was found on wikimedia (god bless wikimedia) +Some other are created and donated by tilem2 users :) +Some other are just my own calc. + +For the source of the pictures : +ti81.skn = GFDL wikimedia.commons +ti82.skn = GFDL wikimedia.commons +TI82stats.skn = GFDL wikimedia.commons +ti83.skn = My TI83 !!! +ti83p.skn = GFDL wikimedia.commons +ti83pfr.skn = GPL v3+ given by Claude Clerc (aka claudiux). It's his calc. Thanks to him !!! +ti84p.skn = My TI84plus !!! +ti84p2.skn = Mine. +ti86.skn = LGPL v2.1. Picture given by Danilo Segan. I've resized with the gimp and I've done the skin (the skin given by Danilo was perfect but too big).Thanks to him !!! +... +... +Your contribution ... +Your contribution ... +Your contribution ... +... +... +... + +Have fun with tilem 2 ;) + +Thibault Duponchelle (aka contra-sh) diff --git a/tool/tilem-src/data/skins/ti76.skn b/tool/tilem-src/data/skins/ti76.skn new file mode 100644 index 0000000..2f5958b Binary files /dev/null and b/tool/tilem-src/data/skins/ti76.skn differ diff --git a/tool/tilem-src/data/skins/ti81.skn b/tool/tilem-src/data/skins/ti81.skn new file mode 100644 index 0000000..6c1ab92 Binary files /dev/null and b/tool/tilem-src/data/skins/ti81.skn differ diff --git a/tool/tilem-src/data/skins/ti82.skn b/tool/tilem-src/data/skins/ti82.skn new file mode 100644 index 0000000..6d3a080 Binary files /dev/null and b/tool/tilem-src/data/skins/ti82.skn differ diff --git a/tool/tilem-src/data/skins/ti82stats.skn b/tool/tilem-src/data/skins/ti82stats.skn new file mode 100644 index 0000000..e89827b Binary files /dev/null and b/tool/tilem-src/data/skins/ti82stats.skn differ diff --git a/tool/tilem-src/data/skins/ti83.skn b/tool/tilem-src/data/skins/ti83.skn new file mode 100644 index 0000000..6cbbe6b Binary files /dev/null and b/tool/tilem-src/data/skins/ti83.skn differ diff --git a/tool/tilem-src/data/skins/ti83p.skn b/tool/tilem-src/data/skins/ti83p.skn new file mode 100644 index 0000000..5c97529 Binary files /dev/null and b/tool/tilem-src/data/skins/ti83p.skn differ diff --git a/tool/tilem-src/data/skins/ti83pfr.skn b/tool/tilem-src/data/skins/ti83pfr.skn new file mode 100644 index 0000000..0866be2 Binary files /dev/null and b/tool/tilem-src/data/skins/ti83pfr.skn differ diff --git a/tool/tilem-src/data/skins/ti84p.skn b/tool/tilem-src/data/skins/ti84p.skn new file mode 100644 index 0000000..c58d544 Binary files /dev/null and b/tool/tilem-src/data/skins/ti84p.skn differ diff --git a/tool/tilem-src/data/skins/ti84p2.skn b/tool/tilem-src/data/skins/ti84p2.skn new file mode 100644 index 0000000..5913805 Binary files /dev/null and b/tool/tilem-src/data/skins/ti84p2.skn differ diff --git a/tool/tilem-src/data/skins/ti86.skn b/tool/tilem-src/data/skins/ti86.skn new file mode 100644 index 0000000..3377aeb Binary files /dev/null and b/tool/tilem-src/data/skins/ti86.skn differ diff --git a/tool/tilem-src/data/symbols/ti82.sym b/tool/tilem-src/data/symbols/ti82.sym new file mode 100644 index 0000000..4c8afd0 --- /dev/null +++ b/tool/tilem-src/data/symbols/ti82.sym @@ -0,0 +1,939 @@ +[macros] +0CD748D ROM_CALL~%j + +[labels] +0008 rOP1TOOP2 +0010 rFINDSYM +0018 rPUSHREALO1 +0020 rMOV9TOOP1 +0028 rFPMULT +0030 rFPADD +0033 _LdHLind +004F _SetTblGraphDraw +0095 _CpHLDE +00A1 _DivHLBy10 +00A3 _DivHLByA + +012B _ApdSetup +0131 _KbdScan +01B8 _KEY_READ +01C7 _STORE_KEY +01D4 _GetCSC +01E8 _CanAlphIns +020D _coorMon +0213 _Mon +0220 _MonForceKey +02B5 _sendKPress +033C _JForceCmdNoChar +033D _JForceCmd +0355 _resetStacks +0368 _sysErrHandler +0374 _newContext +0385 _newContext0 +0462 _PPutAwayPrompt +046D _PPutAway +0473 _PutAway +04B4 _Redisp +04B9 _SizeWind +04CA _ErrorEP +04DC _callMain +04ED _monErrHand + +10C2 _SinCosRad +10C6 _Sin +10CA _Cos +10CE _Tan +14AA _SinHCosH +14AE _TanH +14B2 _CosH +14B6 _SinH +154D _ACosRad +1553 _ATanRad +1556 _ATan2Rad +1558 _ATan2Rad_83 +155E _ASinRad +1563 _ACos +156D _ATan +15CD _ATan2_83 +1570 _ATan2 +1575 _ASin +1787 _ATanH +17D4 _ASinH +17E2 _ACosH +18E7 _HLTimes9 +18F1 _CkOP1Real +18F7 _Angle +18FF _CpOP4OP3 +1907 _Mov9OP2Cp +190C _AbsO1O2Cp +1912 _CpOP1OP2 +1955 _OP3ToOP4 +195E _OP1ToOP4 +1967 _OP2ToOP4 +1970 _OP4ToOP2 +1979 _OP3ToOP2 +1981 _OP1ToOP3 +1989 _OP5ToOP2 +1991 _OP5ToOP6 +1999 _OP5ToOP4 +19A1 _OP1ToOP2 +19A9 _OP6ToOP2 +19B1 _OP6ToOP1 +19B6 _OP4ToOP1 +19BB _OP5ToOP1 +19C0 _OP3ToOP1 +19C5 _OP4ToOP5 +19CD _OP3ToOP5 +19D5 _OP2ToOP5 +19DD _OP2ToOP6 +19E5 _OP1ToOP6 +19ED _OP1ToOP5 +19F5 _OP2ToOP1 +19FB _Mov11B +19FD _Mov10B +19FF _Mov9B +1A01 _Mov8B +1A03 _Mov7B +1A05 _Mov6B +1A07 _Mov5B +1A09 _Mov4B +1A0B _Mov3B +1A0D _Mov2B +1A54 _OP2ToOP3 +1A5C _OP4ToOP3 +1A61 _OP5ToOP3 +1A66 _OP4ToOP6 +1A6E _Mov9ToOP1 +1A73 _Mov9OP1OP2 +1A74 _Mov9ToOP2 +1A79 _MovFrOP1 +1A7E _OP4Set1 +1A83 _OP3Set1 +1A88 _OP2Set8 +1A8F _OP2Set5 +1A96 _OP2Set4 +1A9D _OP2Set3 +1AA5 _OP1Set1 +1AAA _OP1Set4 +1AB1 _OP1Set3 +1AB8 _OP3Set2 +1ABD _OP1Set2 +1AC2 _OP2Set2 +1AC5 _SetNum2 +1AC9 _SetMant1 +1ACD _OP2Set1 +1AD0 _SetNum1 +1AD2 _SetNum +1AD7 _SetNumA +1AD8 _SetMant +1ADC _Zero16D +1ADD _Set16A +1ADE _Set14A +1ADF _Set14D +1AED _OP4Set0 +1AF2 _OP3Set0 +1AF7 _OP2Set0 +1AFC _OP1Set0 +1AFF _SetNum0 +1B07 _ZeroOP1 +1B0C _ZeroOP2 +1B11 _ZeroOP3 +1B14 _ZeroOP +1B1C _ClrLp +1B23 _ShRAcc +1B2B _ShLAcc +1B39 _ShR18 +1B3A _ShR18A +1B42 _ShR16 +1B43 _ShR16A +1B46 _ShR14 +1B76 _ShL16 +1B7A _ShL14 +1C2D _Add16D +1C33 _Add14D +1CA2 _Sub16D +1CA8 _Sub14D +1CEB _OP2ExOP6 +1CF0 _OP5ExOP6 +1CF8 _OP1ExOP5 +1CFD _OP1ExOP6 +1D02 _OP2ExOP4 +1D07 _OP2ExOP5 +1D0F _OP1ExOP3 +1D14 _OP1ExOP4 +1D19 _OP1ExOP2 +1D21 _ExLp +1D2B _CkOP1FP0 +1D30 _CkOP2FP0 +1D39 _PosNo0Int +1D3F _CkPosInt +1D48 _CkInt +1D4F _CkOdd +1DD5 _GetCon1 +1DDA _GetCon +1E48 _ExpToHex +1E52 _OP1ExpToDec +1E9A _CkOP2Pos +1EA0 _CkOP1Pos +1EA6 _ClrOP2S +1EAB _ClrOP1S +1EB2 _FDiv100 +1EB5 _FDiv10 +1EC0 _DecO1Exp +1EC8 _IncO1Exp +1ECB _IncExp +1ED0 _CkValidNum +1EF2 _GetExp +1EFE _HTimesL +1F0F _EOP1NotReal +1F16 _PrgmBangName +1F1B _PrgmPoundName +1F3E _ThetaName +1F42 _RName +1F46 _RegEqName +1F4C _RecurNName +1F57 _XName +1F5B _YName +1F5F _TName +1F61 _RealName +1F6A _SetEStoFPS +1F71 _ChkTempDirt +1F94 _OP1MOP2Exp +1F9E _OP1ExpMDE +1FB2 _ChkErrBreak +1FCC _IsA2ByteTok +1FE3 _GetLastEntry +1FFE _GetLastEntryPtr +2014 _REGRCLRCHNG +2057 _CheckSplitFlag +2083 _ResetWinTop +209C _SetYUp +20A6 _SetXUp +20AB _MemChk +20BD _CmpPrgNamLen1 +20C0 _CmpPrgNamLen +20D1 _FindProgSym +2124 _ChkFindSym +2129 _FindSym +2258 _InsertMem +2262 _InsertMemA +227D _EnoughMem +22AE _CmpMemNeed +22C3 _CreatePVar4 +22E2 _CreatePVar3 +230F _CreateVar3 +2333 _CreateReal +2339 _CreateTRList +233F _CreateRList +235B _CreateTRMat +2361 _CreateRMat +2369 _CreateTStrng +236F _CreateStrng +237D _Create0Equ +2382 _CreateTEqu +2388 _CreateEqu +238C _CreatePict +2393 _CreateGDB +2397 _CreateProg +239B _ChkDel +23A4 _ChkDelA +23DF _AdjParser +2400 _AdjMath +2455 _AdjM7 +2531 _DelMemA +2547 _DelVar +2548 _DelVarIO +258D _DelMem +2590 _DelVar3D +2593 _DelVar3C +25BB _DelVar3DC +2612 _AdjSymPtrs +2695 _DataSizeA +2699 _DataSize +26C1 _PopRealO6 +26C6 _PopRealO5 +26CB _PopRealO4 +26D0 _PopRealO3 +26D5 _PopRealO2 +26DA _PopRealO1 +26DD _PopReal +26E3 _FPopCplx +26E6 _FPopReal +26E9 _FPopFPS +26F1 _DeallocFPS +26F4 _DeallocFPS1 +26FF _AllocFPS +2702 _AllocFPS1 +270B _PushRealO6 +2710 _PushRealO5 +2715 _PushRealO4 +271A _PushRealO3 +271F _PushRealO2 +2724 _PushRealO1 +2727 _PushReal +273B _CpyTo1FPS11 +273E _CpyFPS11 +2745 _CpyTo1FPS5 +2748 _CpyFPS5 +274F _CpyTo1FPS6 +2752 _CpyFPS6 +2759 _CpyTo2FPS4 +275C _CpyFPS4 +2763 _CpyTo6FPS3 +2768 _CpyTo2FPS3 +276D _CpyTo1FPS3 +2770 _CpyFPS3 +277C _CpyTo3FPS2 +2783 _CpyTo5FPST +2788 _CpyTo6FPST +278D _CpyTo4FPST +2792 _CpyTo3FPST +2797 _CpyTo2FPST +279C _CpyTo1FPST +279F _CpyFPST +27A4 _CpyStack +27AB _CpyTo3FPS1 +27B0 _CpyTo2FPS1 +27B5 _CpyTo1FPS1 +27B8 _CpyFPS1 +27BF _CpyTo2FPS2 +27C4 _CpyTo1FPS2 +27C7 _CpyFPS2 +27CE _CpyO3ToFPST +27D3 _CpyO2ToFPST +27D8 _CpyO6ToFPST +27DD _CpyO1ToFPST +27E0 _CpyToFPST +27E5 _CpyToStack +27ED _CpyO5ToFPS1 +27F2 _CpyO1ToFPS1 +27F5 _CpyToFPS1 +27FC _CpyO2ToFPS2 +2801 _CpyO3ToFPS2 +2806 _CpyO6ToFPS2 +280B _CpyO1ToFPS2 +280E _CpyToFPS2 +2815 _CpyO5ToFPS3 +281A _CpyO1ToFPS3 +281D _CpyToFPS3 +2824 _CpyO1ToFPS4 +2827 _CpyToFPS4 +282E _ErrNotEnoughMem +283D _FPSMinus9 +2840 _HLMinus9 +2845 _ErrOverflow +2849 _ErrDivBy0 +284D _ErrSingularMat +2851 _ErrDomain +2855 _ErrIncrement +2859 _ErrSyntax +285D _ErrMode +2861 _ErrDataType +2865 _ErrReserved +2869 _ErrArgument +286D _ErrDimMismatch +2871 _ErrDimension +2875 _ErrUndefined +2879 _ErrMemory +287D _ErrMemoryNE +2881 _ErrInvalid +2885 _ErrIllegalNest +2889 _ErrBound +288D _ErrGraphRange +2891 _ErrZoom +2895 _ErrBreak +2899 _ErrStat +289D _ErrSolver +28A1 _ErrIterations +28A5 _ErrBadGuess +28A9 _ErrTolTooSmall +28AD _ErrStatPlot +28B1 _ErrLink +28B3 _JError +28B6 _JErrorNo +28DC _noErrorEntry +28DD _pushErrorHandler +2903 _popErrorHandler +2913 _StrLength +2926 _StrCopy +292E _StrCat +295B _IsInSet + +2E46 ___bank_call +2E6D ___bank_ret +2E75 ___bank_jump + +2E86 _PutTokString +# 2E8C _DispYPrompt2 ? +2E92 _SetupDispEq +2E98 _BufPeek2 +2EAA _CloseEquField +2EB0 _BufToBtm +2EB6 _CursorLeft +2EBC _SetEmptyEditEqu +# 2ECE DOREFFLAGS02 ? +2EEC _PrevEq +2EF2 _DarkLine +2F1C _CursorDown +2F22 _JPromptCursor +2F34 _GrphPars +2F3A _PlotPars +2F40 _ParseInp +2F46 _InitPFlgs +2F4C _OP2Set60 +2F58 _StoTheta +2F5E _StoR +2F64 _StoY +2F6A _StoT +2F70 _StoX +2F7C _RclAns +2F82 _RclY +2F88 _StMatEl +2F8E _StLstVecEl +2F94 _GetDEPtr +2FA0 _WScrollRight +2FA6 _WScrollLeft +2FAC _WScrollUp +2FB8 _RclToQueue +2FBE _FORSEQINIT +2FC4 _PDspGrph +2FCA _GRDECODA +2FDC _XYCent +2FE2 _ZmInt +2FE8 _CopyRng +2FEE _ZooDefault +2FF4 _ZmTrig +# 2FFA _ZmFit ? +# 3000 _ZmPrev ? +# 3006 _ZmDecml ? +# 300C _ZmUsr ? +# 3012 _SetUZm ? +# 3024 _DrawCmd ? +302A _InvCmd +3030 _TraceOff +304E _FNDDB +3054 _NextEq +305A _SetFuncM +3060 _SetPolM +3066 _SetParM +306C _SetSeqM +307E _TanLnF +# 3084 _ShadeCmd ? +309C _PointCmd +30A2 _PixelCmd +30A8 _ChkTextCurs +30AE _FormToTok +30B4 _GDispToken +30BA _UnLineCmd +30C0 _LineCmd +30CC _VertCmd +30D2 _HorizCmd +30DE _FormERealTok +30F0 _RclSysTok +30F6 _FormEReal +30FC _OP1toEdit +310E _IsAtTop +3114 _ToggleIns +311A _BufReplace +3120 _StoSysTok +3126 _SetupEditEqu +3132 _RecallEd +313E _SetupBuffer +3144 _CreateNumEditBuf +314A _CallCommon +3150 _CommonKeys +3156 _Leftmore +315C _fDel +3162 _fClear +3168 _fInsDisp02 +3180 _CloseEditBufNoR +3186 _ReleaseBuffer +31AA _numError02 +31B0 _SetupEditCmd +31B6 _CursorToOffset +31BC _RstGFlags +31C2 _RclVarToEdit +31C8 _BufInsert +31CE _BufToTop +31DA _IsEditEmpty +31E6 _DisarmScroll +31EC _SetEmptyEditPtr +31F2 _FormDisp +3216 _JCursorRight +321C _JCursorUp +3228 _JCursorFirst +322E _JCursorLast +323A _BufDelete +3258 _RclEntryToEdit +325E _InsDisp +3270 _RclVarToEditPtr +3276 _FormScrollUp +327C _FormMatrix +3294 _FormReal +32D4 _DispTail +3312 _RclX +3318 _SaveParse +331E _GetParse +3330 _CpyTo2ES1 +3336 _CpyTo6ES1 +333C _CpyTo1ES1 +3342 _CpyTo3ES1 +3348 _CpyTo3ES2 +334E _CpyTo2ES2 +3354 _CpyTo1ES2 +335A _CpyTo2ES3 +3360 _CpyTo1ES3 +3366 _CpyTo3ES4 +336C _CpyTo6ES4 +3372 _CpyTo2ES4 +3378 _CpyTo1ES4 +337E _CpyTo2ES5 +3384 _CpyTo1ES5 +338A _CpyTo4EST +3390 _CpyTo2EST +3396 _CpyTo1EST +339C _CpyTo2ES6 +33A2 _CpyTo1ES6 +33A8 _CpyTo2ES7 +33AE _CpyTo1ES7 +33B4 _CpyTo2ES8 +33BA _CpyTo1ES8 +33C0 _CpyTo1ES9 +33C6 _CpyTo2ES9 +33CC _CpyTo2ES10 +33D2 _CpyTo1ES10 +33D8 _CpyES10 +33DE _CpyTo2ES11 +33E4 _CpyTo1ES11 +33EA _CpyTo2ES12 +33F0 _CpyTo1ES12 +33F6 _CpyTo2ES13 +33FC _CpyTo1ES13 +3402 _CpyTo1ES14 +3408 _CpyTo1ES16 +340E _CpyTo1ES17 +3414 _CpyTo1ES18 +341A _CpyTo1ES15 +3420 _CpyTo2ES15 +3426 _CpyO1ToEST +342C _CpyO1ToES1 +3432 _CpyO6ToES1 +3438 _CpyO6ToES4 +343E _CpyO1ToES2 +3444 _CpyO2ToES2 +344A _CpyO1ToES3 +3450 _CpyO1ToES4 +3456 _CpyO1ToES5 +345C _CpyO1ToES6 +3462 _CpyO1ToES7 +3468 _CpyO2ToES1 +346E _CpyO2ToES4 +3474 _CpyO2ToES7 +347A _CpyO1ToES8 +3480 _CpyO1ToES9 +3486 _CpyO1ToES10 +348C _CpyO1ToES11 +3492 _CpyO1ToES12 +3498 _CpyO1ToES13 +349E _CpyO1ToES14 +34A4 _CpyO1ToES15 +34AA _PixelTest +# 34BC _DFMIN2 ? +3504 _RestoreErrNo +3516 _StoN +351C _ParseOff +3522 _RclN +354C _Random +3552 _StoRand +3558 _GetL3TOP1A +355E _GetL1ToOP1 +3564 _GetL1TOP1A +356A _GetLToOP1 +3570 _GetLToOP2 +3576 _GetL2TOP1A +3582 _PutToLA1 +3588 _PutToL +35A0 _ConvOP1 +35AC _GetM1ToOP1A +35B2 _GetMToOP1 +35B8 _PutToM1A +35BE _PutToMA1 +35C4 _PutToMat +35D0 _AdrMEle +35D6 _AdrLEle +35DC _RclVarSym +35E2 _StoOther +35E8 _RedimMat +35EE _IncLstSize +35F4 _InsertList +35FA _DelListEl +3600 _EditProg +3606 _CloseProg +360C _ClrGraphRef +3612 _FixTempCnt +3618 _SaveData +361E _RestoreData +3624 _CleanAll +362A _MoveToNextSym +3630 _DispErrorScreen +3660 _GrRedisp +3690 _FindAlphaUp +3696 _FindAlphaDn +369C _RestoreDisp +36A2 _LoadMenuNum +36A8 _LoadMenuNumL +36AE _RstrSmallText +36B4 _ConvKeyToTok +36BA _GrLabels +36CC _NBCursor +36D2 _IsEditFull +36D8 _IsAtBtm +36DE _ShowCursor +36E4 _BufLeft +36EA _BufPeek +36F0 _BufRight +36F6 _FDispBOL +36FC _FDispBOL1 +3702 _SetWinAbove +3708 _GetPrevTok +370E _StringWidth +371A _DispEOW +3720 _BufPeek1 +372C _BufPeek3 +3732 _ReadDisp2 +3738 _PutMap +373E _PutPS +3744 _WPutPS +374A _PutBuf +3750 _PutBuf1 +3756 _WPutC +375C _WPutS +3762 _WPutSEOL +3768 _WHomeUp +376E _SetNumWindow +377A _NewLine +3780 _MoveDown +3786 _ScrollUp +3792 _ShrinkWindow +3798 _MoveUp +379E _ScrollDown +37A4 _ClrScrnFull +37AA _ClrTxtShd +37B6 _EraseEOL +37C8 _GetCurLoc +37CE _VPutMap +37D4 _VPutS +37DA _VPutSNG +37E0 _SaveCmdShadow +37EC _SaveShadow +37F8 _RstrShadow +37FE _RstrCurRow +3804 _RstrUnderMenu +380A _RstrBotRow +3810 _SaveTR +3816 _RestoreTR +381C _GetTokLen +3822 _GetTokString +3828 _PUTBPATBUF2 +382E _PUTBPATBUF +3834 _putbPAT +383A _putcCheckScroll +3840 _DispEOL +3846 _FDispEOL +384C _TblScreenDn +3852 _TblScreenUp +3858 _TOTOSTRP +385E _SetVarName +386A _DispDone +3870 _FinishOutput +3876 _ClrLCD +387C _DispHL +3894 _CreateTemp +389A _ClrLCDFull +38A0 _LoadNoEEntry +38AC _GrBufCpy +38BE _AbortPrgmode +38CA _ClrScrn +38E2 _StoAns +38E8 _GrReset +38F4 _RandInit +38FA _PutS +3900 _HomeUp +3912 _CoorDisp +3918 _RunIndicOff +391E _CursorOn +3924 _GetKey +392A _BlinkGCur +3930 _RunIndicOn +# 3936 _PullDownChk ? +393C _TokToKey +3942 _GrPutAway +3948 _PopCx +39C6 _SaveOScreen +39CC _CurBlink +39D2 _PutC + +## note: following ROM calls are not correctly handled by +## SNG on ROM 19.006 + +31E0 _CloseEditEqu +3246 _DispHead +324C _BufClear +32BE _Send1BErr +# 32C4 +# 32CA +# 32D0 +# 32D6 +32DC _KeyScnLnk +37B0 _ClrWindow +37C2 _EraseEOW +38B8 _RstrPartial +38C4 _HideCursor +38D6 _LCD_DriverOn +38DC _RstrOScreen +390C _CursorOff + +## note: following ROM calls are not correctly handled by +## SNG on ROM 19.0 + +050C _AppInit +0684 _initialize +07F3 _LCD_BUSY +0801 _Min +080A _Max +0818 _AbsO1PAbsO2 +0820 _Intgr +0836 _Trunc +083A _InvSub +083F _Times2 +0842 _Plus1 +0847 _Minus1 +084A _FPSub +0851 _FPAdd +0905 _DToR +090E _RToD +0917 _Cube +091C _TimesPt5 +0924 _FPSquare +0925 _FPMult + +## note: following ROM calls are not correctly handled by +## any current shells on ROM 19.0 + +0A1B _LJRnd +0A5D _InvOP1S +0A6D _InvOP2S +0A83 _Frac +0AD0 _FPRecip +0AD4 _FPDiv +0BA1 _SqRoot +0C69 _Int +0C6B _Round +0C4F _RndGuard +# 0CDF _Factorial ? +0D27 _LnX +0D2B _LogX +0E63 _LJNoRnd +0F03 _EToX +0F0D _TenX + +8000 kbdScanCode +8001 kbdLGSC +8002 kbdPSC +8003 kbdWUR +8004 kbdDebncCnt +8005 kbdKey +8006 kbdGetKy +8007 keyExtend +8008 contrast +8009 apdSubTimer +800A apdTimer +800B curTime +800C curRow +800D curCol +800E curUnder +800F curY +# 8010 curType ? +8011 curXRow +# 801A tokVarPtr ? +# 801C tokLen ? +801E indicMem +8026 indicCounter +8027 indicBusy +8028 OP1 +8033 OP2 +803E OP3 +8049 OP4 +8054 OP5 +805F OP6 +806A progToEdit +8072 nameBuff +807D iMathPtr1 +807F iMathPtr2 +8081 iMathPtr3 +8083 iMathPtr4 +8085 iMathPtr5 +8087 chkDelPtr1 +8089 chkDelPtr2 +808B insDelPtr +808D upDownPtr +808F textShadow +810F textShadCur +8111 textShadTop +8112 textShadAlph +8113 textShadIns +8114 cxMain +8116 cxPPutAway +8118 cxPutAway +811A cxRedisp +811C cxErrorEP +811E cxSizeWind +8120 cxPage +8121 cxCurApp +8122 cxPrev +8131 monQH +8132 monQT +# 8133 monQueue ? +8143 onSP +8145 onCheckSum +8161 menuActive +8163 menuCurrent +81F0 ioFlag +81F2 sndRecState +81F3 ioErrState +81F4 header +81FD ioData +8209 bakHeader +8215 penCol +8216 penRow +8217 rclQueue +8219 errNo +821A errSP +821C errOffset +8228 saveSScreen +8528 flags +8549 curGStyle +854A curGY +854B curGX +854C curGY2 +854D curGX2 +# 8551 XOffset ? +# 8552 YOffset ? +8553 lcdTallP +8554 pixWideP +8557 lastEntryStk +85D7 numLastEntries +85D8 currLastEntry +# 85E1 ORGXMIN ? +8691 Xmin +86AC Ymin +874E deltaX +8757 deltaY +8760 shortX +8769 shortY +8774 XOutDat +8778 YOutDat +# 877A inputSym ? +877C inputDat +88AE ES +88B8 plotSScreen +8BB8 seed1 +8BC1 seed2 +8BCA parseVar +8BD3 begPC +8BD5 curPC +8BD7 endPC +8BDF cmdShadow +8C5F cmdShadCur +8C65 editTop +8C67 editCursor +8C69 editTail +8C6B editBtm +8C71 editSym +# 8C73 editDat ? +8C8D modePtr +8C8F winTop +8C90 winBtm +8C91 winLeftEdge +8C92 winLeft +8C94 winAbove +8C96 winRow +8C98 winCol +8C9A fmtDigits +8CF2 fmtMatSym +8CF4 fmtMatMem +# 8CF6 EQS ? +8CFF delAdjAmt +8D06 chkDelPtr3 +8D08 chkDelPtr4 +8D0A tempMem +8D0C fpBase +8D0E FPS +8D10 OPBase +8D12 OPS +8D14 pTempCnt +8D16 cleanTmp +8D18 pTemp +8D24 userMem +FE6E symTable + +[flags] +00 kbdFlags +2,00 trigDeg,trigFlags +3,00 kbdSCR,kbdFlags +4,00 kbdKeyPress,kbdFlags +01 editFlags +2,01 editOpen,editFlags +02 plotFlags +1,02 plotLoc,plotFlags +2,02 plotDisp,plotFlags +4,02 grfFuncM,grfModeFlags +5,02 grfPolarM,grfModeFlags +6,02 grfParamM,grfModeFlags +7,02 grfRecurM,grfModeFlags +03 graphFlags +0,03 graphDraw,graphFlags +2,03 graphCursor,graphFlags +04 grfDBFlags +5,04 grfNoAxis,grfDBFlags +5,04 grfLabel,grfDBFlags +05 textFlags +1,05 textEraseBelow,textFlags +2,05 textScrolled,textFlags +3,05 textInverse,textFlags +4,05 textInsMode,textFlags +08 apdFlags +2,08 apdAble,apdFlags +3,08 apdRunning,apdFlags +09 onFlags +3,09 onRunning,onFlags +4,09 onInterrupt,onFlags +0C curFlags +2,0C curAble,curFlags +3,0C curOn,curFlags +4,0C curLock,curFlags +0D appFlags +1,0D appTextSave,appFlags +2,0D appAutoScroll,appFlags +5,0D appCurGraphic,appFlags +6,0D appCurWord,appFlags +11 promptFlags +0,11 promptEdit,promptFlags +12 indicFlags +0,12 indicRun,indicFlags +1,12 indicInUse,indicFlags +2,12 indicOnly,indicFlags +3,12 shift2nd,shiftFlags +4,12 shiftAlpha,shiftFlags +5,12 shiftLwrAlph,shiftFlags +6,12 shiftALock,shiftFlags +7,12 shiftKeepAlph,shiftFlags +13 tblFlags +6,13 reTable,tblFlags +14 sGrFlags +0,14 grfSplit,sGrFlags +1,14 grfSChanged,sGrFlags +2,14 grfSplitOverride,sGrFlags +7,14 textWrite,sGrFlags +15 newIndicFlags +1,15 saIndic,newIndicFlags diff --git a/tool/tilem-src/data/symbols/ti83.sym b/tool/tilem-src/data/symbols/ti83.sym new file mode 100644 index 0000000..70df089 --- /dev/null +++ b/tool/tilem-src/data/symbols/ti83.sym @@ -0,0 +1,1307 @@ +[macros] +# Nothing currently + +[labels] +0008 rOP1TOOP2 +0010 rFINDSYM +0018 rPUSHREALO1 +0020 rMOV9TOOP1 +0028 rFPMULT +0030 rFPADD + +8000 kbdScanCode +8001 kbdLGSC +8002 kbdPSC +8003 kbdWUR +8004 kbdDebncCnt +8005 kbdKey +8006 kbdGetKy +8007 keyExtend +8008 contrast +8009 apdSubTimer +800a apdTimer +800b curTime +800c curRow +800d curCol +800e curOffset +800f curUnder +8010 curY +8012 curXRow +801a lFont_record +8022 sFont_record +802f indicMem +8037 indicCounter +8038 indicBusy +8039 OP1 +8044 OP2 +804f OP3 +805a OP4 +8065 OP5 +8070 OP6 +8080 progToEdit +8088 nameBuff +8094 iMathPtr1 +8096 iMathPtr2 +8098 iMathPtr3 +809a iMathPtr4 +809c iMathPtr5 +809e chkDelPtr1 +80a0 chkDelPtr2 +80a2 insDelPtr +80a4 upDownPtr +80ac asm_data_ptr1 +80ae asm_data_ptr2 +80c8 asm_ind_call +80c9 textShadow +8149 textShadCur +814b textShadTop +814c textShadAlph +814d textShadIns +814e cxMain +8150 cxPPutAway +8152 cxPutAway +8154 cxRedisp +8156 cxErrorEP +8158 cxSizeWind +815a cxPage +815b cxCurApp +815c cxPrev +817d onSP +817f onCheckSum +819d menuActive +819f menuCurrent +822d ioFlag +822f sndRecState +8230 ioErrState +8231 header +823a ioData +8246 bakHeader +8252 penCol +8253 penRow +8254 rclQueue +8256 errNo +8257 errSP +8259 errOffset +8265 saveSScreen +8565 usermem_offset +8567 flags +858f statVars +886c curGStyle +886d curGY +886e curGX +886f curGY2 +8870 curGX2 +8871 freeSaveY +8872 freeSaveX +88F6 XOffset +88F7 YOffset +88F8 lcdTallP +88F9 pixWideP +88FA pixWide_m_1 +88FB pixWide_m_2 +88FE lastEntryStk +897E numLastEntries +897F currLastEntry +8AA5 Xmin +8AB7 Xscl +8AC0 Ymin +8AD2 Yscl +8C3B deltaX +8C44 deltaY +8C4D shortX +8C56 shortY +8C77 XOutDat +8C7B YOutDat +8C7F inputDat +8DEB ES +8e29 plotSScreen +913B parseVar +9144 begPC +9146 curPC +9148 endPC +9157 cmdShadow +91D7 cmdShadCur +91DB cmdCursor +91DD editTop +91DF editCursor +91E1 editTail +91E3 editBtm +91EF editSym +91F1 editDat +928C modePtr +928E winTop +928F winBtm +9290 winLeftEdge +9291 winLeft +9293 winAbove +9295 winRow +9297 winCol +92F1 fmtMatSym +92F3 fmtMatMem +92F5 EQS +92FE delAdjAmt +9305 chkDelPtr3 +9307 chkDelPtr4 +9309 tempMem +930B fpBase +930D FPS +930F OPBase +9311 OPS +9313 pTempCnt +9315 cleanTmp +9317 pTemp +9319 progPtr +9327 userMem +0fe6e symTable + +# System routines (page C) +4000 _ldHLind +4004 _cphlde +4008 _divHLBy10 +400c _divHLbyA +4010 _kbdScan +4014 _getcsc +4018 _coorMon +401b _mon +401e _monForceKey +4021 _sendKPress +4024 _JforceCmdNoChaR +4027 _JforceCmd +402a _sysErrHandler +402e _newContext +4032 _newContext0 +4036 _A2PointHLind +403a _PointHLind +403e _PPutAwayPrompt +4042 _PPutAway +4046 _PutAway +404a _SizeWind +404e _ErrorEP +4052 _callMain +4056 _monErrHand +405a _appInit +405e _appInitIfDec +4062 _Initialize +4066 _lcd_busy +406a _Min +406e _Max +4072 _AbsO1PAbsO2 +4076 _Intgr +407a _Trunc +407e _InvSub +4082 _Times2 +4086 _Plus1 +408a _Minus1 +408e _FPSub +4092 _FPAdd +4096 _DToR +409a _RToD +409e _Cube +40a2 _TimesPt5 +40a6 _FPSquare +40aa _FPMult +40ae _LJRnd +40b2 _InvOP1SC +40b6 _InvOP1S +40ba _InvOP2S +40be _Frac +40c2 _FPRecip +40c6 _FPDiv +40ca _SqRoot +40ce _RndGuard +40d2 _RnFx +40d6 _Int +40da _Round +40de _LnX +40e2 _LogX +40e6 _LJNoRnd +40ea _EToX +40ee _TenX +40f2 _SinCosRad +40f6 _Sin +40fa _Cos +40fe _Tan +4102 _SinHCosH +4106 _TanH +410a _CosH +410e _SinH +4112 _ACosRad +4116 _ATanRad +411a _ATan2Rad +411e _ASinRad +4122 _ACos +4126 _ATan +412a _ASin +412e _ATan2 +4132 _ATanH +4136 _ASinH +413a _ACosH +413e _PToR +4142 _RToP +4146 _HLTimes9 +414a _CkOP1Cplx +414e _CkOP1Real +4152 _Angle +4156 _COP1Set0 +415a _CpOP4OP3 +415e _Mov9OP2Cp +4162 _AbsO1O2Cp +4166 _CpOP1OP2 +416a _OP3ToOP4 +416e _OP1ToOP4 +4172 _OP2ToOP4 +4176 _OP4ToOP2 +417a _OP3ToOP2 +417e _OP1ToOP3 +4182 _OP5ToOP2 +4186 _OP5ToOP6 +418a _OP5ToOP4 +418e _OP1ToOP2 +4192 _OP6ToOP2 +4196 _OP6ToOP1 +419a _OP4ToOP1 +419e _OP5ToOP1 +41a2 _OP3ToOP1 +41a6 _OP6ToOP5 +41aa _OP4ToOP5 +41ae _OP3ToOP5 +41b2 _OP2ToOP5 +41b6 _OP2ToOP6 +41ba _OP1ToOP6 +41be _OP1ToOP5 +41c2 _OP2ToOP1 +41c6 _MovREG +41ca _Mov10B +41ce _Mov9B +41d2 _Mov18 +41d6 _Mov8B +41da _Mov7B +41de _Mov14 +41e2 _Mov6B +41e6 _Mov5B +41ea _Mov4B +41ee _Mov3B +41f2 _Mov2B +41f6 _OP2ToOP3 +41fa _OP4ToOP3 +41fe _OP5ToOP3 +4202 _OP4ToOP6 +4206 _Mov9ToOP1 +420a _Mov9OP1OP2 +420e _Mov9ToOP2 +4212 _MovFrOP1 +4216 _OP4Set1 +421a _OP3Set1 +421e _OP2Set8 +4222 _OP2Set5 +4226 _OP2SetA +422a _OP2Set4 +422e _OP2Set3 +4232 _OP1Set1 +4236 _OP1Set4 +423a _OP1Set3 +423e _OP3Set2 +4242 _OP1Set2 +4246 _OP2Set2 +424a _SetNum2 +424e _SetMant1 +4252 _OP2Set1 +4256 _SetNum1 +425a _SetNum +425e _SetNumA +4262 _SetMant +4266 _Zero16D +426a _Set16A +426e _Set14A +4272 _Set14D +4276 _OP5Set0 +427a _OP4Set0 +427e _OP3Set0 +4282 _OP2Set0 +4286 _OP1Set0 +428a _SetNum0 +428e _ZeroOP1 +4292 _ZeroOP2 +4296 _ZeroOP3 +429a _ZeroOP +429e _ClrLp +42a2 _ShRAcc +42a6 _ShLAcc +42aa _ShR18 +42ae _ShR18A +42b2 _ShR16 +42b6 _ShR14 +42ba _ShL16 +42be _ShL14 +42c2 _SRDO1 +42c6 _SHRDRND +42ca _MANTPA +42ce _ADDPROP +42d2 _ADDPROPLP +42d6 _Add16D +42da _Add14D +42de _Sub16D +42e2 _Sub14D +42e6 _OP2ExOP6 +42ea _OP5ExOP6 +42ee _OP1ExOP5 +42f2 _OP1ExOP6 +42f6 _OP2ExOP4 +42fa _OP2ExOP5 +42fe _OP1ExOP3 +4302 _OP1ExOP4 +4306 _OP1ExOP2 +430a _ExLp +430e _CkOP1C0 +4312 _CkOP1FP0 +4316 _CkOP2FP0 +431a _PosNo0Int +431e _CkPosInt +4322 _CkInt +4326 _CkOdd +432a _CkOP1M +432e _GetCon1 +4332 _GetCon +4336 _PIDIV2 +433a _PIDIV4 +433e _TWOPI +4342 _PICON +4346 _PIDIV4A +434a _PIDIV2A +434e _ExpToHex +4352 _OP1ExpToDec +4356 _CkOP2Pos +435a _CkOP1Pos +435e _ClrOP2S +4362 _ClrOP1S +4366 _FDiv100 +436a _FDiv10 +436e _DecO1Exp +4372 _IncO1Exp +4376 _IncExp +437a _CkValidNum +437e _GetExp +4382 _HTimesL +4386 _EOP1NotReal +438a _ThetaName +438e _RName +4392 _RegEqName +4396 _RecurNName +439a _XName +439e _YName +43a2 _TName +43a6 _RealName +43aa _SETesTOfps +43ae _ChkTempDirt +43b2 _OP1MOP2Exp +43b6 _OP1EXPMDE +43ba _ChkErrBreak +43be _IsA2ByteTok +43c2 _GetLastEntry +43c6 _GetLastEntryPtr +43ca _REGRCLRCHNG +43ce _ResetWinTop +43d2 _SetYUp +43d6 _SetXUp +43da _ISO1NONTLSTorPROG +43de _ISO1NONTEMPLST +43e2 _IS_A_LSTorCLST +43e6 _Chk_HL_999 +43ea _EQU_or_NEWEQU +43ee _GET_PLUS1_SAVE +43f2 _ErrD_OP1NotPos +43f6 _ErrD_OP1Not_R +43fa _ErrD_OP1NotPosInt +43fe _ErrD_OP1_LE_0 +4402 _ErrD_OP1_0 +4406 _Findsym_Get_Size +440a _Sto_StatVar +440e _Rcl_StatVar +4412 _CkOP2Real +4416 _Get_X_Indirect +441a _MemChk +441e _CmpPrgNamLen1 +4422 _CmpPrgNamLen +4426 _FindProgSym +442a _ChkFindSym +442e _FindSym +4432 _InsertMem +4436 _InsertMemA +443a _EnoughMem +443e _CmpMemNeed +4442 _CREATEPVAR4 +4446 _CREATEPVAR3 +444a _CREATEVAR3 +444e _CreateCplx +4452 _CreateReal +4456 _CreateTRList +445a _CreateRList +445e _CreateTCList +4462 _CreateCList +4466 _CreateTRMat +446a _CreateRMat +446e _CreateTStrng +4472 _CreateStrng +4476 _Create0Equ +447a _CreateTEqu +447e _CreateEqu +4482 _CreatePict +4486 _CreateGDB +448a _CreateProg +448e _ChkDel +4492 _ChkDelA +4496 _AdjParser +449a _AdjMath +449e _AdjM7 +44a2 _DelMemA +44a6 _Get_Form_Num +44aa _DelVar +44ae _DelVarIO +44b2 _DelMem +44b6 _DelVar3D +44ba _DelVar3C +44be _DelVar3DC +44c2 _Sym_Prog_Non_T_Lst +44c6 _AdjSymPtrs +44ca _DataSizeA +44ce _DataSize +44d2 _PopMCplxO1 +44d6 _PopMCplx +44da _MovCplx +44de _PopOP5 +44e2 _PopOP3 +44e6 _PopOP1 +44ea _PopRealO6 +44ee _PopRealO5 +44f2 _PopRealO4 +44f6 _PopRealO3 +44fa _PopRealO2 +44fe _PopRealO1 +4502 _PopReal +4506 _FPopCplx +450a _FPopReal +450e _FPopFPS +4512 _DeallocFPS +4516 _DeallocFPS1 +451a _AllocFPS +451e _AllocFPS1 +4522 _PushRealO6 +4526 _PushRealO5 +452a _PushRealO4 +452e _PushRealO3 +4532 _PushRealO2 +4536 _PushRealO1 +453a _PushReal +453e _PushOP5 +4542 _PushOP3 +4546 _PushMCplxO3 +454a _PushOP1 +454e _PushMCplxO1 +4552 _PushMCplx +4556 _ExMCplxO1 +455a _Exch9 +455e _CpyTo1FPS11 +4562 _CpyTo2FPS5 +4566 _CpyTo1FPS5 +456a _CpyTo2FPS6 +456e _CpyTo1FPS6 +4572 _CpyTo2FPS7 +4576 _CpyTo1FPS7 +457a _CpyTo1FPS8 +457e _CpyTo2FPS8 +4582 _CpyTo1FPS10 +4586 _CpyTo1FPS9 +458a _CpyTo2FPS4 +458e _CpyTo6FPS3 +4592 _CpyTo6FPS2 +4596 _CpyTo2FPS3 +459a _CpyCTo1FPS3 +459e _CpyTo1FPS3 +45a2 _CpyFPS3 +45a6 _CpyTo1FPS4 +45aa _CpyTo3FPS2 +45ae _CpyTo5FPST +45b2 _CpyTo6FPST +45b6 _CpyTo4FPST +45ba _CpyTo3FPST +45be _CpyTo2FPST +45c2 _CpyTo1FPST +45c6 _CpyFPST +45ca _CpySTACK +45ce _CpyTo3FPS1 +45d2 _CpyTo2FPS1 +45d6 _CpyTo1FPS1 +45da _CpyFPS1 +45de _CpyTo2FPS2 +45e2 _CpyTo1FPS2 +45e6 _CpyFPS2 +45ea _CpyO3ToFPST +45ee _CpyO2ToFPST +45f2 _CpyO6ToFPST +45f6 _CpyO1ToFPST +45fa _CpyToFPST +45fe _CpyToSTACK +4602 _CpyO3ToFPS1 +4606 _CpyO5ToFPS1 +460a _CpyO2ToFPS1 +460e _CpyO1ToFPS1 +4612 _CpyToFPS1 +4616 _CpyO2ToFPS2 +461a _CpyO3ToFPS2 +461e _CpyO6ToFPS2 +4622 _CpyO1ToFPS2 +4626 _CpyToFPS2 +462a _CpyO5ToFPS3 +462e _CpyO2ToFPS3 +4632 _CpyO1ToFPS3 +4636 _CpyToFPS3 +463a _CpyO1ToFPS6 +463e _CpyO1ToFPS7 +4642 _CpyO1ToFPS5 +4646 _CpyO2ToFPS4 +464a _CpyO1ToFPS4 +464e _ErrNotEnoughMem +4652 _FPSMinus9 +4656 _HLMinus9 +465a _ErrOverflow +465d _ErrDivBy0 +4660 _ErrSingularMat +4663 _ErrDomain +4666 _ErrIncrement +4669 _ErrNon_Real +466c _ErrSyntax +466f _ErrDataType +4672 _ErrArgument +4675 _ErrDimMismatch +4678 _ErrDimension +467b _ErrUndefined +467e _ErrMemory +4681 _ErrInvalid +4684 _ErrBreak +4687 _ErrStat +468a _ErrSignChange +468d _ErrIterations +4690 _ErrBadGuess +4693 _ErrTolTooSmall +4696 _ErrStatPlot +4699 _ErrLinkXmit +469c _JError +469f _JErrorNo +46a2 _noErrorEntry +46a5 _pushErrorHandlER +46a9 _popErrorHandleR +46ad _strCopy +46b1 _strCat +46b5 _isInSet +46b9 _sDone +46bd _serrort +46c1 _sNameEq +46c5 _sUnderScr +46c9 _sFAIL +46cd _sName +46d1 _sOK +46d5 _seqn +46d9 _Sselect +46dd _STransmit +46e1 _SRECURN +46e5 _GEQNAMEA +46e9 _RECNAME +46ed ___bank_call +46f1 ___bank_ret +46f5 ___bank_jump +46f9 ___bank_entry +46fd _ReadDisp2 +4701 _putmap +4705 _putc +4709 _dispHL +470d _puts +4711 _putpsb +4715 _putps +4719 _wputps +471d _putbuf +4721 _putbuf1 +4725 _wputc +4729 _wputs +472d _wputsEOL +4731 _wdispEOL +4735 _whomeUp +4739 _setNumWindow +473d _newline +4741 _moveDown +4745 _scrollUp +4749 _shrinkWindow +474d _moveUp +4751 _scrollDown +4755 _clrLCDFull +4759 _clrLCD +475d _clrScrnFull +4761 _clrScrn +4765 _clrTxtShd +4769 _clrWindow +476d _eraseEOL +4771 _eraseEOW +4775 _homeUp +4779 _getcurloc +477d _vputmap +4781 _vputs +4785 _vputsn +4789 _vputsnG +478d _vputsnT +4791 _runIndicOn +4795 _runIndicOff +4799 _saveCmdShadow +479d _saveShadow +47a1 _rstrShadow +47a5 _rstrpartial +47a9 _rstrCurRow +47ad _rstrUnderMenu +47b1 _rstrbotrow +47b5 _saveTR +47b9 _restoreTR +47bd _GetKeyPress +47c1 _GetTokLen +47c5 _Get_Tok_Strng +47c9 _GetTokString +47cd _PUTBPATBUF2 +47d1 _PUTBPATBUF +47d5 _putbPAT +47d9 _putcCheckScrolL +47dd _dispEOL +47e1 _fdispEOL +47e5 _MAKEROWCMD +47e9 _TOTOSTRP +47ed _SETVARNAME +47f1 _dispDone +47f5 _finishoutput +47f9 _curBlink +47fd _cursorOff +4801 _hideCursor +4805 _cursorOn +4809 _showCursor +480d _KeyToString +4811 _PullDownChk +4815 _MenuCatCommon +4819 _ZIfCatalog +481d _ZIfMatrixMenu +4821 _loadCurCat +4825 _NCifprgmedmode +4829 _LoadMenuNum +482d _LoadMenuNumL +4831 _MenuEdKey +4835 _MenCatRet +4839 _notalphnum +483d _SaveSavedFlags +4841 _SetMenuFlags +4845 _RstrSomeFlags +4849 _RstrSmallText +484d _dispListName +4851 _SeeIfErrorCx +4855 _RstrOScreen +4859 _SaveOscreen +485d _DispLAlphaName +4861 _AbortPrgmode +4865 _Is_FullCntx +4869 _AdrMRow +486d _AdrMEle +4871 _GetMatOP1A +4875 _GetM1ToOP1 +4879 _GetM1TOP1A +487d _GetMToOP1 +4881 _PutToM1A +4885 _PutToMA1 +4889 _PutToMat +488d _Mat_El_Div +4891 _CMatFun +4895 _RowEch_Poly +4899 _RowEchelon +489d _AdrLEle +48a1 _GetL1ToOP1 +48a5 _GetL1TOP1A +48a9 _GetLToOP1 +48ad _GetL1ToOP2 +48b1 _GetL1TOP2A +48b5 _GetL2TOP1A +48b9 _PutToLA1 +48bd _PutToL +48c1 _MaxMinLst +48c5 _LLow +48c9 _LHigh +48cd _LSum +48d1 _CumSum +48d5 _ToFrac +48d9 _SeqSet +48dd _SeqSolve +48e1 _Cmp_Num_Init +48e5 _BinOpExec +48e9 _EXMEAN1 +48ed _SET2MVLPTRS +48f1 _SETMAT1 +48f5 _CreateTList +48f9 _UnOpExec +48fd _ThreeExec +4901 _RestoreErrNo +4904 _FourExec +4908 _FiveExec +490c _CpyTo2ES1 +4910 _CpyTo6ES1 +4914 _CpyTo1ES1 +4918 _CpyTo3ES1 +491c _CpyTo3ES2 +4920 _CpyTo2ES2 +4924 _CpyTo1ES2 +4928 _CpyTo2ES3 +492c _CpyTo1ES3 +4930 _CpyTo3ES4 +4934 _CpyTo6ES3 +4938 _CpyTo2ES4 +493c _CpyTo1ES4 +4940 _CpyTo2ES5 +4944 _CpyTo1ES5 +4948 _CpyTo4EST +494c _CpyTo2EST +4950 _CpyTo1EST +4954 _CpyTo2ES6 +4958 _CpyTo1ES6 +495c _CpyTo2ES7 +4960 _CpyTo1ES7 +4964 _CpyTo2ES8 +4968 _CpyTo1ES8 +496c _CpyTo1ES9 +4970 _CpyTo2ES9 +4974 _CpyTo2ES10 +4978 _CpyTo1ES10 +497c _CpyTo2ES11 +4980 _CpyTo1ES11 +4984 _CpyTo2ES12 +4988 _CpyTo1ES12 +498c _CpyTo2ES13 +4990 _CpyTo1ES13 +4994 _CpyTo1ES14 +4998 _CpyTo1ES16 +499c _CpyTo1ES17 +49a0 _CpyTo1ES18 +49a4 _CpyTo1ES15 +49a8 _CpyTo2ES15 +49ac _CpyO1ToEST +49b0 _CpyO1ToES1 +49b4 _CpyO6ToES1 +49b8 _CpyO6ToES3 +49bc _CpyO1ToES2 +49c0 _CpyO2ToES2 +49c4 _CpyO1ToES3 +49c8 _CpyO1ToES4 +49cc _CpyO1ToES5 +49d0 _CpyO1ToES6 +49d4 _CpyO1ToES7 +49d8 _CpyO2ToES4 +49dc _CpyO2ToES5 +49e0 _CpyO2ToES6 +49e4 _CpyO2ToES7 +49e8 _CpyO2ToES8 +49ec _CpyO2ToES9 +49f0 _CpyO1ToES8 +49f4 _CpyO1ToES9 +49f8 _CpyO1ToES10 +49fc _CpyO1ToES11 +4a00 _CpyO1ToES12 +4a04 _CpyO1ToES13 +4a08 _CpyO1ToES14 +4a0c _CpyO1ToES15 +4a10 _EVALF3A +4a14 COMPLEX_EXEC +4a18 _GetK +4a1c _setTitlE +4a20 _dispVarVal +4a24 _RecallEd +4a28 _setupBuffer +4a2c _createNumEditBUF +4a30 _CallCommon +4a34 _CommonKeys +4a37 _Leftmore +4a3a _fDel +4a3e _fClear +4a42 _finsDisp +4a45 _FinsDisp02 +4a49 _setIndicator +4a4d _CloseEditBufNo +4a51 _releaseBuffer +4a55 _varnameToOP1hl +4a59 _nameToOP1 +4a5d _numPPutAway +4a61 _numRedisp +4a65 _numError02 +4a68 _Load_SFont +4a6c _SFont_Len +4a70 _InitNumVec +4a74 _SetXXOP1 +4a78 _SetXXOP2 +4a7c _SetXXXXOP2 +4a80 _UCLineS +4a84 _CLine +4a88 _CLineS +4a8c _XRooty +4a90 _YToX +4a94 _ZmStats +4a98 _POINT_STAT_HLP +4a9c _DRAWSPLOT +4aa0 _INITNEWTRACEP +4aa4 _SPLOTCOORD +4aa8 _SPLOTRIGHT +4aac _SPLOTLEFT +4ab0 _CMPBOXINFO +4ab4 _NEXTPLOT +4ab8 _PREVPLOT +4abc _CLRPREVPLOT +4ac0 _PUT_INDEX_LST +4ac4 _GET_INDEX_LST +4ac8 _HEAP_SORT +4acc _STOGDB2 +4ad0 _RCLGDB2 +4ad4 _CircCmd +4ad8 _GrphCirc +4adc _Mov18B +4ae0 _DarkLine +4ae4 _ILine +4ae8 _IPoint +4aec _XYRndBoth +4af0 _XYRnd +4af4 _CheckTOP +4af8 _CheckXY +4afc _DarkPnt +4b00 _CPointS +4b04 _WtoV +4b08 _VtoWHLDE +4b0c _XItoF +4b10 _YFtoI +4b14 _XFtoI +4b18 _TraceOff +4b1c _GrRedisp +4b20 _GDispToken +4b24 _GRDECODA +4b28 _LABCOOR +4b2c _COORDISP +4b30 _TMPEQUNOSRC +4b34 _GrLabels +4b38 _YPixSet +4b3c _XPixSet +4b40 _CopyRng +4b44 _VALCUR +4b48 _GrPutAway +4b4c _RstGFlags +4b50 _GRReset +4b54 _XYCent +4b58 _ZoomXYCmd +4b5c _CPTDELY +4b60 _CPTDELX +4b64 _SetFuncM +4b68 _SetSeqM +4b6c _SetPolM +4b70 _SetParM +4b74 _ZmInt +4b78 _ZmDecml +4b7c _ZmPrev +4b80 _ZmUsr +4b84 _SetUZm +4b88 _ZmFit +4b8c _ZmSquare +4b90 _ZmTrig +4b94 _SetXMinMax +4b98 _ZooDefault +4b9c _GrBufCpy +4ba0 _DrawSplitLine +4ba4 _RestoreDisp +4ba8 _FNDDB +4bac _ALLEQ +4bb0 _fndallseleq +4bb4 _NEXTEQ +4bb8 _PREVEQ +4bbc _BlinkGCur +4bc0 _NBCURSOR +4bc4 _StatMark +4bc8 _ChkTextCurs +4bcc _Regraph +4bd0 _DOREFFLAGS02 +4bd4 _InitNSeq +4bd8 _YRES +4bdc _PLOTPTXY2 +4be0 _Ceiling +4be4 _PutXY +4be7 _Putequno +4beb _PDspGrph +4bef _HorizCmd +4bf3 _VertCmd +4bf7 _LineCmd +4bfb _UnLineCmd +4bff _PointCmd +4c03 _PixelTest +4c07 _PixelCmd +4c0b _TanLnF +4c0f _DrawCmd_Init +4c13 _DrawCmd +4c17 _ShadeCmd +4c1b _InvCmd +4c1f _StatShade +4c23 _dspmattable +4c27 _dsplsts +4c2b _closeEditBuf +4c2f _parseEditBuf +4c33 _putsm +4c37 _DspCurTbl +4c3b _DspGrTbl +4c3f _ZeroTemplate +4c43 _settblrefs +4c47 _dispTblBot +4c4b _DispTblTop +4c4f _dispTblbody +4c53 _VPutBlank +4c57 _TblTrace +4c5b _dispListNameY +4c5f _CurNameLength +4c63 _NameToBuf +4c67 _jpromptcursor +4c6a _bufLeft +4c6e _bufRight +4c72 _bufInsert +4c76 _bufQueueChar +4c7a _bufReplace +4c7e _bufDelete +4c82 _bufPeek +4c86 _bufPeek1 +4c8a _bufPeek2 +4c8e _bufPeek3 +4c92 _BufToBtm +4c96 _setupEditEqu +4c9a _bufToTop +4c9e _isEditFull +4ca2 _isEditEmpty +4ca6 _isAtTop +4caa _isAtBtm +4cae _bufClear +4cb2 _JcursorFirst +4cb5 _JcursorLast +4cb8 _cursorLeft +4cbc _JcursorRight +4cbf _JcursorUp +4cc2 _cursorDown +4cc6 _cursorToOffset +4cca _insDisp +4cce _FDISPBOL1 +4cd2 _FDISPBOL +4cd6 _dispEOW +4cda _dispHead +4cde _dispTail +4ce2 _PutTokString +4ce6 _setupEditCmd +4cea _setEmptyEditEqU +4cee _setEmptyEditPtR +4cf2 _closeEditEqu +4cf6 _toggleIns +4cfa _GetPrevTok +4cfe _getkey +4d02 _canIndic +4d06 _LCD_DriverOn +4d0a _DFMIN2 +4d0e _formDisp +4d12 _formMatrix +4d16 _wscrollLeft +4d1a _wscrollUp +4d1e _wscrollDown +4d22 _wscrollRight +4d26 _formEReal +4d2a _formERealTOK +4d2e _formDCplx +4d32 _formReal +4d36 _formScrollUp +4d3a _setwinabove +4d3e _disarmScroll +4d42 _OP1toEdit +4d46 _MinToEdit +4d4a _rclVarToEdit +4d4e _rclVarToEditPtR +4d52 _RclEntryToEdit +4d56 _rclToQueue +4d5a _FormToTok +4d5e _Disp_Interval +4d62 _DisplstName +4d66 _dispSLstNameHL +4d6a _EditEqu +4d6e _closeEquField +4d72 _AutoSelect +4d76 _DISPYEOS +4d7a _dispNumEOS +4d7e _setupdispeq +4d82 _dispForward +4d86 _DispYPrompt2 +4d8a _stringwidth +4d8e _dispErrorScreeN +4d92 _PopCx +4d96 _loadnoeentry +4d9a _SaveScreen +4d9e _RetScreen +4da2 _RetScreenErr +4da6 _SplitChange +4daa _SolveRedisp +4dae _SolveDisp +4db2 _itemName +4db6 _SetNorm_Vals +4dba _SetVert_Vals +4dbe _ConvKeyToTok +4dc2 _KeyToTokNew2B +4dc6 _KeyToTok2Byte +4dca _TokToKey +4dce _Load_LFont +4dd2 _Send1BErr +4dd6 _GetVarCmd +4dda _SendVarCmd +4dde _PrintScreen +4de2 _KeyScnLnk +4de6 _IOResetAll +4dea _DelRes +4dee _ConvLCtoLR +4df2 _RedimMat +4df6 _IncLstSize +4dfa _insertlist +4dfe _dellistel +4e02 _EditProg +4e06 _CloseProg +4e0a _ClrGraphRef +4e0e _FixTempCnt +4e12 _SaveData +4e16 _RestoreData +4e1a _FindAlphaup +4e1e _FindAlphadn +4e22 _CmpSyms +4e26 _CreateTemp +4e2a _CleanAll +4e2e _MoveToNextSym +4e32 _ConvLRtoLC +4e36 _TblScreenDn +4e3a _TblScreenUp +4e3e _ScreenUp +4e42 _ScreenUpDown +4e46 _ZifRclHandler +4e4a _zifrclkapp +4e4e _rclkeyRtn +4e52 _RclKey +4e55 _RclRegEq_Call +4e59 _RclRegEq +4e5c _initNamePrompt +4e60 _NamePrompt2 +4e64 _CatalogChk +4e68 _clrTR +4e6c _Quad +4e70 _GraphQuad +4e74 _BC2NonReal +4e78 _ErrNonReal +4e7c _Write_Text +4e80 _FORSEQINIT +4e84 _GrphPars +4e88 _PlotPars +4e8c _PARSEinp +4e90 _PARSEOFF +4e94 _PARSESCAN +4e98 _GetParse +4e9c _SaveParse +4ea0 _InitPFlgs +4ea4 _CkEndLinErr +4ea8 _OP2Set60 +4eac _GetStatPtr +4eb0 _Cmp_StatPtr +4eb4 _VarSysAdr +4eb8 _StoSysTok +4ebc _StoAns +4ec0 _StoTheta +4ec4 _StoR +4ec8 _StoY +4ecc _StoN +4ed0 _StoT +4ed4 _StoX +4ed8 _StoOther +4edc _RCLANS +4ee0 _RclY +4ee4 _RclN +4ee8 _RclX +4eec _RclVarSym +4ef0 _RclSysTok +4ef4 _StMatEl +4ef8 _StLstVecEl +4efc _ConvOP1 +4f00 _isaletter +4f04 _Find_Parse_ForMULA +4f08 _Parse_Formula +4f0c _StrngEnt1 +4f15 _GetNumLet +4f1e _Init_Prog_List +4f27 _PRGRDLP +4f30 _VarEnt +4f39 _RclStat +4f42 _ParseLoop +4f4b _ParseOnC +4f52 _ParseOn +4f5a _ParseCmd +4f61 _StoType +4f6a _CreatePair +4f73 _PushNum +4f7c _IncCurPCErrEnd +4f85 _ErrEnd +4f8e _CommaErrF +4f97 _CommaErr +4fa0 _StEqArg2 +4fa9 _StEqArg +4fb2 _InpArg +4fbb _StEqArg3 +4fc4 _NxtFetch +4fcd _CkFetchVar +4fd6 _FetchVarA +4fdf PARSER_EXEC +4fe3 _FetchVar +4fec _CkEndLin +4ff5 _CkEndExp +4ffe _CkParsEnd +5007 _StoTypeArg +5010 _ConvDim +5019 _ConvDim00 +5022 _AheadEqual +502b _ParsAheadS +5034 _ParsAhead +503d _AnsName +5046 _StoCmpReals +504f _GetDEPtr +5058 _Push2BOper +5061 _Pop2BOper +506a _PushOper +5073 _PopOper +507c _Find_E_Undef +5085 _StTmpEq +508e _FindEOL +5097 _BrkInc +50a0 _IncFetch +50a9 _CurFetch +50b2 PGMIO_EXEC +50b6 _Random +50ba _StoRand +50be _RandInit +50c2 _resetStacks +50c6 _Factorial +50ca _YOnOff +50ce _EqSelUnsel +50d2 _ITSOLVER +50d6 _GRITSOLVER +50da _ITSOLVERB +50de _ITSOLVERNB +50e2 _ExTest_INT +50e6 _Dist_Fun +50ea _LogGamma +50ee _OneVar +50f2 _OneVarS_0 +50f6 _ORDSTAT +50fa _INITSTATANS2 +50fe _ANOVA_SPEC +5102 _EXEC_ASSEMBLY +5106 _outputExpr +510a _CentCursor +5113 _Text +511c _FINISHSPEC +5125 _TRCYFUNC +512e _Rcl_Seq_X +5137 _RclSeq2 +5140 _GRPPutAway +5149 _CkValDelX +5152 _CkValDelta +515b _GrBufClr +5164 _GrBufCpy_V +516d _FndSelEq +5176 _ClrGraphXY +517f _Next_Y_Style +5188 GRAPH_EXEC +518c _PLOTPT +5195 _NEWINDEP +519e _Axes +51a7 _setPenX +51b0 _setPenY +51b9 _setPenT +51c2 _Tan_Equ_Disp +51cb _putAns +51d4 _DispOP1A +51dd _MathTanLn +51e6 _EndDraw +51ef IO_EXEC +52e5 EXECUTE_Z80 + +[flags] +00 kbdFlags +2,00 trigDeg,trigFlags +3,00 kbdSCR,kbdFlags +4,00 kbdKeyPress,kbdFlags +01 editFlags +2,01 editOpen,editFlags +4,01 monAbandon,monFlags +02 plotFlags +1,02 plotLoc,plotFlags +2,02 plotDisp,plotFlags +4,02 grfFuncM,grfModeFlags +5,02 grfPolarM,grfModeFlags +6,02 grfParamM,grfModeFlags +7,02 grfRecurM,grfModeFlags +03 graphFlags +0,03 graphDraw,graphFlags +2,03 graphCursor,graphFlags +04 grfDBFlags +0,04 grfDot,grfDBFlags +1,04 grfSimul,grfDBFlags +2,04 grfGrid,grfDBFlags +3,04 grfPolar,grfDBFlags +4,04 grfNoCoord,grfDBFlags +5,04 grfNoAxis,grfDBFlags +6,04 grfLabel,grfDBFlags +05 textFlags +1,05 textEraseBelow,textFlags +2,05 textScrolled,textFlags +3,05 textInverse,textFlags +08 apdFlags +2,08 apdAble,apdFlags +3,08 apdRunning,apdFlags +09 onFlags +3,09 onRunnings,onFlags +4,09 onInterrupt,onFlags +6,09 statsValid,statFlags +10 fmtFlags +0,0A fmtExponent,fmtFlags +1,0A fmtEng,fmtFlags +5,0A fmtReal,fmtFlags +6,0A fmtRect,fmtFlags +7,0A fmtPolar,fmtFlags +0c curFlags +2,0C curAble,curFlags +3,0C curOn,curFlags +4,0C curLock,curFlags +0d appFlags +1,0D appTextSave,appFlags +2,0D appAutoScroll,appFlags +11 plotFlag2 +3,11 expr_param,plotFlag2 +4,11 expr_writing,plotFlag2 +12 indicFlags +0,12 indicRun,indicFlags +1,12 indicInUse,indicFlags +2,12 indicOnly,indicFlags +3,12 shift2nd,shiftFlags +4,12 shiftAlpha,shiftFlags +6,12 shiftALock,shiftFlags +13 tblFlags +4,13 AutoFill,tblFlags +5,13 AutoCalc,tblFlags +14 sGrFlags +0,14 grfSplit,sGrFlags +1,14 VertSplit,sGrFlags +2,14 grfSChanged,sGrFlags +3,14 grfSplitOverride,sGrFlags +4,14 write_on_graph,sGrFlags +6,14 cmp_mod_box,sGrFlags +7,14 textWrite,sGrFlags +15 newIndicFlags +1,15 saIndic,newIndicFlags +0,1D gkKeyRepeating,gkFlag +21 asm_flag1 +22 asm_flag1 +23 asm_flag1 diff --git a/tool/tilem-src/data/symbols/ti83p.sym b/tool/tilem-src/data/symbols/ti83p.sym new file mode 100644 index 0000000..086215a --- /dev/null +++ b/tool/tilem-src/data/symbols/ti83p.sym @@ -0,0 +1,2327 @@ +[macros] +0EF B_CALL~%c +0CD5000 B_JUMP~%c +0EDED DB~$ED,$ED,%b,%b + +[labels] +0008 rOP1TOOP2 +000B LCD_BUSY_QUICK +0010 rFINDSYM +0018 rPUSHREALO1 +0020 rMOV9TOOP1 +0028 rBR_CALL +0030 rFPADD +0050 BRT_JUMP0 +0059 APP_PUSH_ERRORH +005C APP_POP_ERRORH + +[romcalls] +4000 JErrorNo +4003 FontHook +4006 LocalizeHook +4009 LdHLind +400c CpHLDE +400f DivHLBy10 +4012 DivHLByA +4015 KbdScan +4018 GetCSC +401b coorMon +401e Mon +4021 monForceKey +4024 SendKPress +4027 JForceCmdNoChar +402a JForceCmd +402d sysErrHandler +4030 newContext +4033 newContext0 +4036 PPutAwayPrompt +4039 PPutAway +403c PutAway +403f SizeWind +4042 ErrorEP +4045 callMain +4048 monErrHand +404b AppInit +404e initialize +4051 LCD_BUSY +4054 Min +4057 Max +405a AbsO1PAbsO2 +405d Intgr +4060 Trunc +4063 InvSub +4066 Times2 +4069 Plus1 +406c Minus1 +406f FPSub +4072 FPAdd +4075 DToR +4078 RToD +407b Cube +407e TimesPt5 +4081 FPSquare +4084 FPMult +4087 LJRnd +408a InvOP1SC +408d InvOP1S +4090 InvOP2S +4093 Frac +4096 FPRecip +4099 FPDiv +409c SqRoot +409f RndGuard +40a2 RnFx +40a5 Int +40a8 Round +40ab LnX +40ae LogX +40b1 LJNoRnd +40b4 EToX +40b7 TenX +40ba SinCosRad +40bd Sin +40c0 Cos +40c3 Tan +40c6 SinHCosH +40c9 TanH +40cc CosH +40cf SinH +40d2 ACosRad +40d5 ATanRad +40d8 ATan2Rad +40db ASinRad +40de ACos +40e1 ATan +40e4 ASin +40e7 ATan2 +40ea ATanH +40ed ASinH +40f0 ACosH +40f3 PToR +40f6 RToP +40f9 HLTimes9 +40fc CkOP1Cplx +40ff CkOP1Real +4102 Angle +4105 COP1Set0 +4108 CpOP4OP3 +410b Mov9OP2Cp +410e AbsO1O2Cp +4111 CpOP1OP2 +4114 OP3ToOP4 +4117 OP1ToOP4 +411a OP2ToOP4 +411d OP4ToOP2 +4120 OP3ToOP2 +4123 OP1ToOP3 +4126 OP5ToOP2 +4129 OP5ToOP6 +412c OP5ToOP4 +412f OP1ToOP2 +4132 OP6ToOP2 +4135 OP6ToOP1 +4138 OP4ToOP1 +413b OP5ToOP1 +413e OP3ToOP1 +4141 OP6ToOP5 +4144 OP4ToOP5 +4147 OP3ToOP5 +414a OP2ToOP5 +414d OP2ToOP6 +4150 OP1ToOP6 +4153 OP1ToOP5 +4156 OP2ToOP1 +4159 Mov11B +415c Mov10B +415f Mov9B +4162 Mov18 +4165 Mov8B +4168 Mov7B +416b Mov14 +416e OP2ToOP3 +4171 OP4ToOP3 +4174 OP5ToOP3 +4177 OP4ToOP6 +417a Mov9ToOP1 +417d Mov9OP1OP2 +4180 Mov9ToOP2 +4183 MovFrOP1 +4186 OP4Set1 +4189 OP3Set1 +418c OP2Set8 +418f OP2Set5 +4192 OP2SetA +4195 OP2Set4 +4198 OP2Set3 +419b OP1Set1 +419e OP1Set4 +41a1 OP1Set3 +41a4 OP3Set2 +41a7 OP1Set2 +41aa OP2Set2 +41ad OP2Set1 +41b0 Zero16D +41b3 OP5Set0 +41b6 OP4Set0 +41b9 OP3Set0 +41bc OP2Set0 +41bf OP1Set0 +41c2 SetNum0 +41c5 ZeroOP1 +41c8 ZeroOP2 +41cb ZeroOP3 +41ce ZeroOP +41d1 ClrLp +41d4 ShRAcc +41d7 ShLAcc +41da ShR18 +41dd ShR18A +41e0 ShR16 +41e3 ShR14 +41e6 ShL16 +41e9 ShL14 +41ec SRDO1 +41ef ShRDRnd +41f2 MantPA +41f5 ADDPROP +41f8 ADDPROPLP +41fb Add16D +41fe Add14D +4201 Sub16D +4204 Sub14D +4207 OP2ExOP6 +420a OP5ExOP6 +420d OP1ExOP5 +4210 OP1ExOP6 +4213 OP2ExOP4 +4216 OP2ExOP5 +4219 OP1ExOP3 +421c OP1ExOP4 +421f OP1ExOP2 +4222 ExLp +4225 CkOP1C0 +4228 CkOP1FP0 +422b CkOP2FP0 +422e PosNo0Int +4231 CkPosInt +4234 CkInt +4237 CkOdd +423a CkOP1M +423d GetConOP1 +4240 GetConOP2 +4243 PIDIV2 +4246 PIDIV4 +4249 TWOPI +424c PICON +424f ExpToHex +4252 OP1ExpToDec +4255 CkOP2Pos +4258 CkOP1Pos +425b ClrOP2S +425e ClrOP1S +4261 FDiv100 +4264 FDiv10 +4267 DecO1Exp +426a IncO1Exp +426d IncExp +4270 CkValidNum +4273 GetExp +4276 HTimesL +4279 EOP1NotReal +427c ThetaName +427f RName +4282 RegEqName +4285 RecurNName +4288 XName +428b YName +428e TName +4291 RealName +4294 SetEStoFPS +4297 ChkTempDirt +429a OP1MOP2Exp +429d OP1ExpMDE +42a0 ChkErrBreak +42a3 IsA2ByteTok +42a6 GetLastEntry +42a9 GetLastEntryPtr +42ac RegrClrChng +42af ResetWinTop +42b2 SetYUp +42b5 SetXUp +42b8 IsO1NonTLstOrProg +42bb IsO1NonTempLst +42be Is_A_LstOrCLst +42c1 Chk_HL_999 +42c4 Equ_or_NewEqu +42c7 ErrD_OP1NotPos +42ca ErrD_OP1Not_R +42cd ErrD_OP1NotPosInt +42d0 ErrD_OP1_LE_0 +42d3 ErrD_OP1_0 +42d6 FindSym_Get_Size +42d9 Sto_StatVar +42dc Rcl_StatVar +42df CkOP2Real +42e2 Get_X_Indirect +42e5 MemChk +42e8 CmpPrgNamLen1 +42eb CmpPrgNamLen +42ee FindProgSym +42f1 ChkFindSym +42f4 FindSym +42f7 InsertMem +42fa InsertMemA +42fd EnoughMem +4300 CmpMemNeed +4303 CreatePVar4 +4306 CreatePVar3 +4309 CreateVar3 +430c CreateCplx +430f CreateReal +4312 CreateTRList +4315 CreateRList +4318 CreateTCList +431b CreateCList +431e CreateTRMat +4321 CreateRMat +4324 CreateTStrng +4327 CreateStrng +432a Create0Equ +432d CreateTEqu +4330 CreateEqu +4333 CreatePict +4336 CreateGDB +4339 CreateProg +433c ChkDel +433f ChkDelA +4342 AdjParser +4345 AdjMath +4348 AdjM7 +434b DelMemA +434e Get_Form_Num +4351 DelVar +4354 DelVarIO +4357 DelMem +435a DelVar3D +435d DelVar3C +4360 DelVar3DC +4363 Sym_Prog_Non_T_Lst +4366 AdjSymPtrs +4369 DataSizeA +436c DataSize +436f PopMCplxO1 +4372 PopMCplx +4375 MovCplx +4378 PopOP5 +437b PopOP3 +437e PopOP1 +4381 PopRealO6 +4384 PopRealO5 +4387 PopRealO4 +438a PopRealO3 +438d PopRealO2 +4390 PopRealO1 +4393 PopReal +4396 FPopCplx +4399 FPopReal +439c FPopFPS +439f DeallocFPS +43a2 DeallocFPS1 +43a5 AllocFPS +43a8 AllocFPS1 +43ab PushRealO6 +43ae PushRealO5 +43b1 PushRealO4 +43b4 PushRealO3 +43b7 PushRealO2 +43ba PushRealO1 +43bd PushReal +43c0 PushOP5 +43c3 PushOP3 +43c6 PushMCplxO3 +43c9 PushOP1 +43cc PushMCplxOP1 +43cf PushMCplx +43d2 ExMCplxO1 +43d5 Exch9 +43d8 CpyTo1FPS11 +43db CpyTo2FPS5 +43de CpyTo1FPS5 +43e1 CpyTo2FPS6 +43e4 CpyTo1FPS6 +43e7 CpyTo2FPS7 +43ea CpyTo1FPS7 +43ed CpyTo1FPS8 +43f0 CpyTo2FPS8 +43f3 CpyTo1FPS10 +43f6 CpyTo1FPS9 +43f9 CpyTo2FPS4 +43fc CpyTo6FPS3 +43ff CpyTo6FPS2 +4402 CpyTo2FPS3 +4405 CpyCTo1FPS3 +4408 CpyTo1FPS3 +440b CpyFPS3 +440e CpyTo1FPS4 +4411 CpyTo3FPS2 +4414 CpyTo5FPST +4417 CpyTo6FPST +441a CpyTo4FPST +441d CpyTo3FPST +4420 CpyTo2FPST +4423 CpyTo1FPST +4426 CpyFPST +4429 CpyStack +442c CpyTo3FPS1 +442f CpyTo2FPS1 +4432 CpyTo1FPS1 +4435 CpyFPS1 +4438 CpyTo2FPS2 +443b CpyTo1FPS2 +443e CpyFPS2 +4441 CpyO3ToFPST +4444 CpyO2ToFPST +4447 CpyO6ToFPST +444a CpyO1ToFPST +444d CpyToFPST +4450 CpyToStack +4453 CpyO3ToFPS1 +4456 CpyO5ToFPS1 +4459 CpyO2ToFPS1 +445c CpyO1ToFPS1 +445f CpyToFPS1 +4462 CpyO2ToFPS2 +4465 CpyO3ToFPS2 +4468 CpyO6ToFPS2 +446b CpyO1ToFPS2 +446e CpyToFPS2 +4471 CpyO5ToFPS3 +4474 CpyO2ToFPS3 +4477 CpyO1ToFPS3 +447a CpyToFPS3 +447d CpyO1ToFPS6 +4480 CpyO1ToFPS7 +4483 CpyO1ToFPS5 +4486 CpyO2ToFPS4 +4489 CpyO1ToFPS4 +448c ErrNotEnoughMem +448f FPSMinus9 +4492 HLMinus9 +4495 ErrOverflow +4498 ErrDivBy0 +449b ErrSingularMat +449e ErrDomain +44a1 ErrIncrement +44a4 ErrNon_Real +44a7 ErrSyntax +44aa ErrDataType +44ad ErrArgument +44b0 ErrDimMismatch +44b3 ErrDimension +44b6 ErrUndefined +44b9 ErrMemory +44bc ErrInvalid +44bf ErrBreak +44c2 ErrStat +44c5 ErrSignChange +44c8 ErrIterations +44cb ErrBadGuess +44ce ErrTolTooSmall +44d1 ErrStatPlot +44d4 ErrLinkXmit +44d7 JError +44da noErrorEntry +44dd pushErrorHandler +44e0 popErrorHandler +44e3 StrCopy +44e6 StrCat +44e9 IsInSet +44ec GEQNAMEA +44ef RECNAME +44f2 __bank_call +44f5 __bank_ret +44f8 __bank_jump +44fb __bank_entry +44fe ReadDisp2 +4501 PutMap +4504 PutC +4507 DispHL +450a PutS +450d PutPSB +4510 PutPS +4513 WPutPS +4516 PutBuf +4519 PutBuf1 +451c WPutC +451f WPutS +4522 WPutSEOL +4525 WDispEOL +4528 WHomeUp +452b SetNumWindow +452e NewLine +4531 MoveDown +4534 ScrollUp +4537 ShrinkWindow +453a MoveUp +453d ScrollDown +4540 ClrLCDFull +4543 ClrLCD +4546 ClrScrnFull +4549 ClrScrn +454c ClrTxtShd +454f ClrWindow +4552 EraseEOL +4555 EraseEOW +4558 HomeUp +455b GetCurLoc +455e VPutMap +4561 VPutS +4564 VPutSN +4567 VPutSNG +456a VPutSNT +456d RunIndicOn +4570 RunIndicOff +4573 SaveCmdShadow +4576 SaveShadow +4579 RstrShadow +457c RstrPartial +457f RstrCurRow +4582 RstrUnderMenu +4585 RstrBotRow +4588 SaveTR +458b RestoreTR +458e GetKeyPress +4591 GetTokLen +4594 Get_Tok_Strng +4597 GetTokString +459a PUTBPATBUF2 +459d PUTBPATBUF +45a0 PUTBPAT +45a3 putcCheckScroll +45a6 DispEOL +45a9 fdispEOL +45ac MakeRowCmd +45af TOTOSTRP +45b2 SetVarName +45b5 DispDone +45b8 FinishOutput +45bb CurBlink +45be CursorOff +45c1 HideCursor +45c4 CursorOn +45c7 ShowCursor +45ca KeyToString +45cd PullDownChk +45d0 MenuCatCommon +45d3 LoadCurCat +45d6 NCifprgmedmode +45d9 LoadMenuNum +45dc LoadMenuNumL + 45df +45e2 MenCatRet +45e5 NotAlphNum +45e8 SaveSavedFlags +45eb SetMenuFlags +45ee RstrSomeFlags +45f1 RstrSmallText +45f4 DispListName +45f7 RstrOScreen +45fa SaveOScreen +45fd DispLAlphaName +4600 AbortPrgmode +4603 Is_FullCntx +4606 AdrMRow +4609 AdrMEle +460c GetMatOP1A +460f GetM1ToOP1 +4612 GetM1TOP1A +4615 GetMToOP1 +4618 PutToM1A +461b PutToMa1 +461e PutToMat +4621 Mat_El_Div +4624 CMATFUN +4627 RowEch_Poly +462a RowEchelon +462d AdrLEle +4630 GetL1ToOP1 +4633 GetL1TOP1A +4636 GetLToOP1 +4639 GetL1TOOP2 +463c GetL1TOP2A +463f GetL2TOP1A +4642 PutToLA1 +4645 PutToL +4648 MaxMinLst +464b LLow +464e LHigh +4651 LSum +4654 CumSum +4657 ToFrac +465a SeqSet +465d SeqSolve +4660 Cmp_Num_Init +4663 BinOPExec +4666 ExMean1 +4669 Set2MVLPtrs +466c SetMat1 +466f CreateTList +4672 UnOPExec +4675 ThreeExec +4678 RestoreErrNo +467b FourExec +467e FiveExec +4681 CpyTo2ES1 +4684 CpyTo6ES1 +4687 CpyTo1ES1 +468a CpyTo3ES1 +468d CpyTo3ES2 +4690 CpyTo2ES2 +4693 CpyTo1ES2 +4696 CpyTo2ES3 +4699 CpyTo1ES3 +469c CpyTo3ES4 +469f CpyTo6ES3 +46a2 CpyTo2ES4 +46a5 CpyTo1ES4 +46a8 CpyTo2ES5 +46ab CpyTo1ES5 +46ae CpyTo4EST +46b1 CpyTo2EST +46b4 CpyTo1EST +46b7 CpyTo2ES6 +46ba CpyTo1ES6 +46bd CpyTo2ES7 +46c0 CpyTo1ES7 +46c3 CpyTo2ES8 +46c6 CpyTo1ES8 +46c9 CpyTo1ES9 +46cc CpyTo2ES9 +46cf CpyTo2ES10 +46d2 CpyTo1ES10 +46d5 CpyTo2ES11 +46d8 CpyTo1ES11 +46db CpyTo2ES12 +46de CpyTo1ES12 +46e1 CpyTo2ES13 +46e4 CpyTo1ES13 +46e7 CpyTo1ES14 +46ea CpyTo1ES16 +46ed CpyTo1ES17 +46f0 CpyTo1ES18 +46f3 CpyTo1ES15 +46f6 CpyTo2ES15 +46f9 CpyO1ToEST +46fc CpyO1ToES1 +46ff CpyO6ToES1 +4702 CpyO6ToES3 +4705 CpyO1ToES2 +4708 CpyO2ToES2 +470b CpyO1ToES3 +470e CpyO1ToES4 +4711 CpyO1ToES5 +4714 CpyO1ToES6 +4717 CpyO1ToES7 +471a CpyO2ToES4 +471d CpyO2ToES5 +4720 CpyO2ToES6 +4723 CpyO2ToES7 +4726 CpyO2ToES8 +4729 CpyO2ToES9 +472c CpyO1ToES8 +472f CpyO1ToES9 +4732 CpyO1ToES10 +4735 CpyO1ToES11 +4738 CpyO1ToES12 +473b CpyO1ToES13 +473e CpyO1ToES14 +4741 EvalF3A +4744 GetK +4747 SetTitle +474a DispVarVal +474d RecallEd +4750 SetupBuffer +4753 CreateNumEditBuf +4756 CallCommon +4759 CommonKeys +475c Leftmore +475f fDel +4762 fClear +4765 fInsDisp +4768 fInsDisp02 +476b SetIndicator +476e CloseEditBufNoR +4771 ReleaseBuffer +4774 VarNameToOP1HL +4777 NameToOP1 +477a numPPutAway +477d numRedisp +4780 numError02 +4783 Load_SFont +4786 SFont_Len +4789 InitNumVec +478c SetXXOP1 +478f SetXXOP2 +4792 SetXXXXOP2 +4795 UCLineS +4798 CLine +479b CLineS +479e XRootY +47a1 YToX +47a4 ZmStats +47a7 Point_Stat_Hlp +47aa DrawSPlot +47ad InitNewTraceP +47b0 SPlotCoord +47b3 SPlotRight +47b6 SPlotLeft +47b9 CmpBoxInfo +47bc NextPlot +47bf PrevPlot +47c2 ClrPrevPlot +47c5 Put_Index_Lst +47c8 Get_Index_Lst +47cb Heap_Sort +47ce StoGDB2 +47d1 RclGDB2 +47d4 CircCmd +47d7 GrphCirc +47da Mov18B +47dd DarkLine +47e0 ILine +47e3 IPoint +47e6 XYRndBoth +47e9 XYRnd +47ec CheckTop +47ef CheckXY +47f2 DarkPnt +47f5 CPointS +47f8 WToV +47fb VtoWHLDE +47fe Xitof +4801 YftoI +4804 XftoI +4807 TraceOff +480a GrRedisp +480d GDispToken +4810 GRDECODA +4813 LabCoor +4816 CoorDisp +4819 TmpEquNoSrc +481c GrLabels +481f YPixSet +4822 XPixSet +4825 CopyRng +4828 ValCur +482b GrPutAway +482e RstGFlags +4831 GrReset +4834 XYCent +4837 ZoomXYCmd +483a CptDelY +483d CptDelX +4840 SetFuncM +4843 SetSeqM +4846 SetPolM +4849 SetParM +484c ZmInt +484f ZmDecml +4852 ZmPrev +4855 ZmUsr +4858 SetUZm +485b ZmFit +485e ZmSquare +4861 ZmTrig +4864 SetXMinMax +4867 ZooDefault +486a GrBufCpy +486d DrawSplitLine +4870 RestoreDisp +4873 FNDDB +4876 AllEq +4879 FndAllSelEq +487c NextEq +487f PrevEq +4882 BlinkGCur +4885 NBCursor +4888 StatMark +488b ChkTextCurs +488e Regraph +4891 DoRefFlags02 +4894 YRes +4897 PlotPtXY2 +489a Ceiling +489d PutXY +48a0 PutEquNo +48a3 PDspGrph +48a6 HorizCmd +48a9 VertCmd +48ac LineCmd +48af UnLineCmd +48b2 PointCmd +48b5 PixelTest +48b8 PixelCmd +48bb TanLnF +48be DrawCmd_Init +48c1 DrawCmd +48c4 ShadeCmd +48c7 InvCmd +48ca StatShade +48cd DspMatTable +48d0 DspLsts +48d3 CloseEditBuf +48d6 ParseEditBuf +48d9 PutSM +48dc DspCurTbl +48df DspGrTbl +48e2 ZeroTemplate +48e5 SetTblRefs +48e8 DispTblBot +48eb DispTblTop +48ee DispTblBody +48f1 VPutBlank +48f4 TblTrace +48f7 DispListNameY +48fa CurNameLength +48fd NameToBuf +4900 JPromptCursor +4903 BufLeft +4906 BufRight +4909 BufInsert +490c BufQueueChar +490f BufReplace +4912 BufDelete +4915 BufPeek +4918 BufPeek1 +491b BufPeek2 +491e BufPeek3 +4921 BufToBtm +4924 SetupEditEqu +4927 BufToTop +492a IsEditFull +492d IsEditEmpty +4930 IsAtTop +4933 IsAtBtm +4936 BufClear +4939 JCursorFirst +493c JCursorLast +493f CursorLeft +4942 JCursorRight +4945 JCursorUp +4948 CursorDown +494b CursorToOffset +494e InsDisp +4951 FDispBOL1 +4954 FDispBOL +4957 DispEOW +495a DispHead +495d DispTail +4960 PutTokString +4963 SetupEditCmd +4966 SetEmptyEditEqu +4969 SetEmptyEditPtr +496c CloseEditEqu +496f GetPrevTok +4972 GetKey +4975 CanIndic +4978 LCD_DRIVERON +497b DFMIN2 +497e FormDisp +4981 FormMatrix +4984 WScrollLeft +4987 WScrollUp +498a WScrollDown +498d WScrollRight +4990 FormEReal +4993 FormERealTok +4996 FormDCplx +4999 FormReal +499c FormScrollUp +499f SetWinAbove +49a2 DisarmScroll +49a5 OP1toEdit +49a8 MinToEdit +49ab RclVarToEdit +49ae RclVarToEditPtr +49b1 RclEntryToEdit +49b4 RclToQueue +49b7 FormToTok +49ba Disp_Interval +49bd DispLstName +49c0 DispSLstNameHL +49c3 EditEqu +49c6 CloseEquField +49c9 AutoSelect +49cc DispYEOS +49cf DispNumEOS +49d2 SetupDispEq +49d5 DispForward +49d8 DispYPrompt2 +49db StringWidth +49de DispErrorScreen +49e1 PopCx +49e4 LoadNoEEntry +49e7 SaveScreen +49ea RetScreen +49ed RetScreenErr +49f0 CheckSplitFlag +49f3 SolveRedisp +49f6 SolveDisp +49f9 ItemName +49fc SetNorm_Vals +49ff SetVert_Vals +4a02 ConvKeyToTok +4a05 KeyToTokNew2B +4a08 KeyToTok2Byte +4a0b TokToKey +4a0e Send1BErr +4a11 GetVarCmd +4a14 SendVarCmd +4a17 PrintScreen +4a1a KeyScnLnk +4a1d IOResetAll +4a20 DelRes +4a23 ConvLcToLr +4a26 RedimMat +4a29 IncLstSize +4a2c InsertList +4a2f DelListEl +4a32 EditProg +4a35 CloseProg +4a38 ClrGraphRef +4a3b FixTempCnt +4a3e SaveData +4a41 RestoreData +4a44 FindAlphaUp +4a47 FindAlphaDn +4a4a CmpSyms +4a4d CreateTemp +4a50 CleanAll +4a53 MoveToNextSym +4a56 ConvLrToLc +4a59 TblScreenDn +4a5c TblScreenUp +4a5f ScreenUp +4a62 ScreenUpDown +4a65 ZIfRclHandler +4a68 ZIfRclKApp +4a6b RclKey +4a6e RclKey2 +4a71 RclRegEq +4a74 RclRegEq2 +4a77 InitNamePrompt +4a7a NamePrompt2 +4a7d CatalogChk +4a80 ClrTR +4a83 Quad +4a86 GraphQuad +4a89 BC2NonReal +4a8c ErrNonReal +4a8f Write_Text +4a92 ForSeqInit +4a95 GrphPars +4a98 PlotPars +4a9b ParseInp +4a9e ParseOff +4aa1 ParseScan +4aa4 GetParse +4aa7 SaveParse +4aaa InitPFlgs +4aad CkEndLinErr +4ab0 OP2Set60 +4ab3 GetStatPtr +4ab6 Cmp_StatPtr +4ab9 VarSysAdr +4abc StoSysTok +4abf StoAns +4ac2 StoTheta +4ac5 StoR +4ac8 StoY +4acb StoN +4ace StoT +4ad1 StoX +4ad4 StoOther +4ad7 RclAns +4ada RclY +4add RclN +4ae0 RclX +4ae3 RclVarSym +4ae6 RclSysTok +4ae9 StMatEl +4aec StLstVecEl +4aef ConvOP1 +4af2 Find_Parse_Formula +4af5 Parse_Formula +4af8 StrngEnt1 +4afb PrgRdLp +4afe VarEnt +4b01 ParseOnC +4b04 ParseOn +4b07 ParseCmd +4b0a StoType +4b0d CreatePair +4b10 PushNum +4b13 IncCurPCErrEnd +4b16 ErrEnd +4b19 CommaErrF +4b1c CommaErr +4b1f StEqArg2 +4b22 StEqArg +4b25 InpArg +4b28 StEqArg3 +4b2b NxtFetch +4b2e CkFetchVar +4b31 FetchVarA +4b34 FetchVar +4b37 CkEndLin +4b3a CkEndExp +4b3d CkParsEnd +4b40 StoTypeArg +4b43 ConvDim +4b46 ConvDim00 +4b49 AheadEqual +4b4c ParsAheadS +4b4f ParsAhead +4b52 AnsName +4b55 StoCmpReals +4b58 GetDEPtr +4b5b Push2BOper +4b5e Pop2BOper +4b61 PushOper +4b64 PopOper +4b67 Find_E_UndefOrArchived +4b6a StTmpEq +4b6d FindEOL +4b70 BrkInc +4b73 IncFetch +4b76 CurFetch +4b79 Random +4b7c StoRand +4b7f RandInit +4b82 ResetStacks +4b85 Factorial +4b88 YOnOff +4b8b EqSelUnsel +4b8e ITSOLVER +4b91 GRITSOLVER +4b94 ITSOLVERB +4b97 ITSOLVERNB +4b9a ExTest_INT +4b9d Dist_Fun +4ba0 LogGamma +4ba3 OneVar +4ba6 OneVarS_0 +4ba9 OrdStat +4bac InitStatAns2 +4baf ANOVA_Spec +4bb2 OutputExpr +4bb5 CentCursor +4bb8 Text +4bbb FinishSpec +4bbe TRCYFUNC +4bc1 Rcl_Seq_X +4bc4 RclSeq2 +4bc7 GrPPutAway +4bca CkValDelX +4bcd CkValDelta +4bd0 GrBufClr +4bd3 GrBufCpy_V +4bd6 FndSelEq +4bd9 ClrGraphXY +4bdc Next_Y_Style +4bdf PlotPt +4be2 NewIndep +4be5 Axes +4be8 SetPenX +4beb SetPenY +4bee SetPenT +4bf1 Tan_Equ_Disp +4bf4 PutAns +4bf7 DispOP1A +4bfa MathTanLn +4bfd EndDraw +4c00 SetTblGraphDraw +4c03 StartDialog +4c06 DialogInit +4c09 GetDialogNumOP1 +4c0c SetDialogNumOP1 +4c0f GetDialogNumHL + 4c12 +4c15 SetDialogKeyOverride +4c18 ResDialogKeyOverride +4c1b ForceDialogKeypress +4c1e DialogStartGetKey +4c21 StartDialog_Override +4c24 CallDialogCallback +4c27 SetDialogCallback +4c2a ResDialogCallback +4c2d CopyDialogNum +4c30 MemClear +4c33 MemSet +4c36 ReloadAppEntryVecs +4c39 PointOn +4c3c ExecuteNewPrgm +4c3f StrLength +4c42 UserPutMap +4c45 GetCurrentPageSub +4c48 FindAppUp +4c4b FindAppDn +4c4e FindApp +4c51 ExecuteApp +4c54 MonReset + 4c57 + 4c5a + 4c5d +4c60 IBounds +4c63 IOffset +4c66 DrawCirc2 +4c69 CanAlphIns +4c6c Redisp +4c6f GetBaseVer +4c72 SetFP0 +4c75 AppGetCbl +4c78 AppGetCalc +4c7b SaveDisp +4c7e SetIgnoreKey +4c81 SetSendThisKeyBack +4c84 DisableApd +4c87 EnableApd + 4c8a + 4c8d +4c90 forcecmd +4c93 ApdSetup +4c96 Get_NumKey +4c99 AppSetup +4c9c HandleLinkActivity + 4c9f +4ca2 ReleaseSedit +4ca5 initsmalleditline +4ca8 startsmalledit + 4cab +4cae SGetTokString +4cb1 LoadPattern +4cb4 SStringLength + 4cb7 + 4cba +4cbd DoNothing + 4cc0 + 4cc3 + 4cc6 + 4cc9 + 4ccc +4ccf SmallEditEraseEOL + 4cd2 + 4cd5 + 4cd8 +4cdb initsmalleditBox + 4cde +4ce1 EmptyHook + 4ce4 + 4ce7 + 4cea +4ced ClearRow + 4cf0 +4cf3 SetupSmallEditCursor + 4cf6 + 4cf9 + 4cfc + 4cff + 4d02 + 4d05 + 4d08 + 4d0b + 4d0e + 4d11 + 4d14 + 4d17 + 4d1a + 4d1d + 4d20 + 4d23 +4d26 AppScreenUpDown +4d29 AppScreenUpDown1 + 4d2c +4d2f initsmalleditlinevar +4d32 initsmalleditlineop1 +4d35 initsmalleditboxvar +4d38 initsmalleditboxop1 + 4d3b + 4d3e +4d41 ErrCustom1 +4d44 ErrCustom2 +4d47 AppStartMouse + 4d4a + 4d4d + 4d50 +4d53 AppEraseMouse + 4d56 +4d59 ATimes12 +4d5c ClearRect +4d5f InvertRect +4d62 FillRect +4d65 AppUpdateMouse + 4d68 + 4d6b +4d6e initcellbox +4d71 drawcell + 4d74 +4d77 invertcell +4d7a setcelloverride +4d7d DrawRectBorder +4d80 ClearCell +4d83 covercell +4d86 EraseRectBorder +4d89 FillRectPattern +4d8c DrawRectBorderClear + 4d8f + 4d92 +4d95 VerticalLine +4d98 IBoundsFull +4d9b DisplayImage + 4d9e + 4da1 + 4da4 + 4da7 + 4daa + 4dad + 4db0 + 4db3 + 4db6 + 4db9 + 4dbc + 4dbf + 4dc2 + 4dc5 +4dc8 CPoint +4dcb DeleteApp + 4dce +4dd1 setmodecellflag +4dd4 resetmodecellflag +4dd7 ismodecellset +4dda getmodecellflag + 4ddd +4de0 CellBoxManager +4de3 startnewcell + 4de6 +4de9 CellCursorHandle + 4dec + 4def +4df2 ClearCurCell +4df5 drawcurcell +4df8 invertcurcell +4dfb covercurcell +4dfe BlinkCell +4e01 BlinkCellNoLookUp +4e04 BlinkCurCell +4e07 BlinkCellToOn +4e0a BlinkCellToOnNoLookUp +4e0d BlinkCurCellToOn +4e10 BlinkCellToOff +4e13 BlinkCellToOffNoLookUp +4e16 BlinkCurCellToOff +4e19 getcurmodecellflag + 4e1c +4e1f startsmalleditreturn + 4e22 + 4e25 +4e28 CellkHandle +4e2b errchkalphabox + 4e2e + 4e31 + 4e34 + 4e37 +4e3a eraseallcells +4e3d iscurmodecellset + 4e40 +4e43 initalphabox + 4e46 + 4e49 +4e4c drawblnkcell +4e4f ClearBlnkCell +4e52 invertblnkcell + 4e55 + 4e58 + 4e5b + 4e5e + 4e61 + 4e64 +4e67 HorizontalLine +4e6a CreateAppVar +4e6d CreateProtProg +4e70 CreateVar +4e73 AsmComp +4e76 GetAsmSize +4e79 SquishPrgm +4e7c ExecutePrgm +4e7f ChkFindSymAsm +4e82 ParsePrgmName +4e85 CSub +4e88 CAdd +4e8b CSquare +4e8e CMult +4e91 CRecip +4e94 CDiv +4e97 CAbs +4e9a CSqrAbs +4e9d CSqRoot +4ea0 CLN +4ea3 CLog +4ea6 CTenX +4ea9 CEtoX +4eac CXrootY + 4eaf +4eb2 CYtoX +4eb5 Conj +4eb8 CMltByReal +4ebb CDivByReal +4ebe CTrunc +4ec1 CFrac +4ec4 CIntgr +4ec7 SendHeaderPacket +4eca CancelTransmission +4ecd SendScreenContents +4ed0 SendRAMVarData +4ed3 SendRAMCmd +4ed6 SendPacket +4ed9 ReceiveAck +4edc Send4BytePacket +4edf SendDataByte +4ee2 Send4Bytes +4ee5 SendAByte +4ee8 SendCByte +4eeb GetSmallPacket +4eee GetDataPacket +4ef1 SendAck +4ef4 Get4Bytes +4ef7 Get3Bytes +4efa Rec1stByte +4efd Rec1stByteNC +4f00 ContinueGetByte +4f03 RecAByteIO +4f06 ReceiveVar +4f09 ReceiveVarData2 +4f0c ReceiveVarData +4f0f SrchVLstUp +4f12 SrchVLstDn +4f15 SendVariable +4f18 Get4BytesCursor +4f1b Get4BytesNC + 4f1e +4f21 SendDirectoryContents +4f24 SendReadyPacket + 4f27 + 4f2a + 4f2d +4f30 SendApplication +4f33 SendOSHeader +4f36 SendOSPage +4f39 SendOS +4f3c FlashWriteDisable +4f3f SendCmd +4f42 SendOSSignature +4f45 Disp +4f48 SendGetKeyPress +4f4b RejectCommand +4f4e CheckLinkLines +4f51 GetHookByte +4f54 LoadBIndPaged +4f57 CursorHook +4f5a LibraryHook +4f5d RawKeyHook +4f60 SetCursorHook +4f63 SetLibraryHook +4f66 SetRawKeyHook +4f69 ClrCursorHook +4f6c ClrLibraryHook +4f6f ClrRawKeyHook +4f72 ResetHookBytes +4f75 AdjustAllHooks +4f78 GetKeyHook +4f7b SetGetKeyHook +4f7e ClrGetKeyHook +4f81 LinkActivityHook +4f84 SetLinkActivityHook +4f87 ClrLinkActivityHook + 4f8a +4f8d SetCatalog2Hook +4f90 ClrCatalog2Hook +4f93 SetLocalizeHook +4f96 ClrLocalizeHook +4f99 SetTokenHook +4f9c ClrTokenHook + 4f9f + 4fa2 + 4fa5 +4fa8 Bit_VertSplit +4fab SetHomeScreenHook +4fae ClrHomeScreenHook +4fb1 SetWindowHook +4fb4 ClrWindowHook +4fb7 SetGraphHook +4fba ClrGraphHook + 4fbd + 4fc0 + 4fc3 +4fc6 DelVarArc +4fc9 DelVarNoArc +4fcc SetAllPlots +4fcf SetYEquHook +4fd2 ClrYEquHook +4fd5 ForceYEqu +4fd8 Arc_Unarc +4fdb ArchiveVar +4fde UnarchiveVar +4fe1 ResDialogKeyOverride +4fe4 SetFontHook +4fe7 ClrFontHook +4fea SetRegraphHook +4fed ClrRegraphHook +4ff0 RegraphHook +4ff3 SetTraceHook +4ff6 ClrTraceHook +4ff9 TraceHook + 4ffc + 4fff +5002 JForceGraphNoKey +5005 JForceGraphKey +5008 PowerOff +500b GetKeyRetOff +500e FindGroupSym +5011 FillBasePageTable +5014 ArcChk +5017 FlashToRam +501a LoadDEIndPaged +501d LoadCIndPaged +5020 SetupPagedPtr +5023 PagedGet +5026 SetParserHook +5029 ClrParserHook +502c SetAppChangeHook +502f ClrAppChangeHook +5032 SetDrawingHook +5035 ClrDrawingHook +5038 IPoint_NoHook +503b ILine_NoHook +503e CLineS_NoHook +5041 DeleteTempPrograms +5044 SetCatalog1Hook +5047 ClrCatalog1Hook +504a SetHelpHook +504d ClrHelpHook + 5050 + 5053 + 5056 +5059 Catalog2Hook +505c Catalog1Hook + 505f + 5062 +5065 DispMenuTitle + 5068 +506b SetCxRedispHook +506e ClrCxRedispHook +5071 BufCpy +5074 BufClr + 5077 + 507a + 507d +5080 DisplayVarInfo +5083 SetMenuHook +5086 ClrMenuHook +5089 GetBCOffsetIX + 508c +508f ForceFullScreen +5092 GetVariableData +5095 FindSwapSector +5098 CopyFlashPage +509b FindAppNumPages +509e HLMinus5 +50a1 SendArcPacket +50a4 ForceGraphKeypress + 50a7 +50aa FormBase + 50ad +50b0 IsFragmented +50b3 Chk_Batt_Low +50b6 Chk_Batt_Low2 + +## OS 1.10 +50b9 Arc_Unarc2 + +## OS 1.13 +50bc GetAppBasePage +50bf SetExSpeed + 50c2 +50c5 GroupAllVars +50c8 UngroupVar +50cb WriteToFlash +50ce SetSilentLinkHook +50d1 ClrSilentLinkHook +50d4 TwoVarSet + 50d7 + 50da +50dd GetSysInfo +50e0 NZIf83Plus +50e3 LinkStatus + +## OS 1.15 + 50e6 +50e9 DBusKeyScn + +## OS 2.21 +50ec RunAppLib +50ef FindSpecialAppHeader + 50f2 + 50f5 + 50f8 +50fb GetVarCmdUSB + +## OS 2.30 + 50fe + 5101 + 5104 + 5107 +510a GetVarVersion + 510d + 5110 + 5113 + 5116 + 5119 + 511c + 511f +5122 InvertTextInsMode + 5125 +5128 ResetDefaults + 512b +512e DispHeader +5131 ForceGroup + 5134 + 5137 + 513a + 513d + 5140 +5143 GetRelSeconds +5146 DisableClock +5149 EnableClock +514c GetDayOfWeek +514f GetDate +5152 FormDate +5155 GetDateFmt +5158 FormDateString +515b GetTime +515e FormTime +5161 GetTimeFmt +5164 FormTimeString +5167 GetClockStatus +516a SetDateMkList +516d SetDateFmt +5170 SetTimeMkList +5173 SetTimeFmt +5176 GetAbsSeconds +5179 AbsSecondsToTimeList + 517c +517f ClrWindowAndFlags +5182 SetMachineID +5185 ResetLists + 5188 + 518b + 518e +5191 ExecLib + 5194 + 5197 + 519a +519d OpenLib +51a0 WaitEnterKey + 51a3 + 51a6 + 51a9 + 51ac + 51af + 51b2 + 51b5 +51b8 IsOP1Resid + 51bb + 51be + 51c1 + 51c4 +51c7 DispAboutScreen +51ca ChkHelpHookVer +51cd Disp32 + 51d0 + 51d3 + 51d6 + 51d9 +51dc DrawTableEditor + 51df + 51e2 + 51e5 + 51e8 + 51eb + 51ee +51f1 MatrixName + 51f4 + 51f7 + 51fa + 51fd + 5200 + 5203 + 5206 + 5209 + 520c + 520f + 5212 + 5215 + 5218 + 521b + 521e +5221 Chk_Batt_Level + 5224 + 5227 + 522a + 522d + 5230 +5233 GoToLastRow +5236 RectBorder + 5239 + 523c + 523f +5242 LoadA5 + 5245 +5248 NamedListToOP1 + 524b + 524e + 5251 + 5254 + 5257 + 525a + 525d + 5260 + 5263 + 5266 +5269 CheckUSBAutoLaunchHeader + 526c + 526f + 5272 + 5275 + 5278 + 527b + 527e + 5281 +5284 SetVertGraphActive +5287 ClearVertGraphActive +528a SetUSBHook +528d ClrUSBHook +5290 InitUSBDevice +5293 KillUSBPeripheral + 5296 + 5299 +529c GraphLine + 529f + 52a2 + 52a5 + 52a8 + 52ab + 52ae +52b1 ZifTableEditor + 52b4 + 52b7 + 52ba +52bd FindAppName + 52c0 + 52c3 + 52c6 +52c9 BufCpyCustom + 52cc + +## OS 2.40 + 52cf + 52d2 + 52d5 + 52d8 + 52db + 52de + 52e1 +52e4 DelVarSym +52e7 FindAppUpNoCase +52ea FindAppDnNoCase +52ed DeleteInvalidApps +52f0 DeleteApp_Link + 52f3 +52f6 SetAppRestrictions +52f9 RemoveAppRestrictions +52fc CheckAppRestrictions +52ff DispAppRestrictions +5302 SetupHome + 5305 + 5308 + 530b + 530e + 5311 +5314 PolarEquToOP1 + 5317 + 531a +531d GetRestrictionsOptions +5320 DispResetComplete + 5323 +5326 FindAppCustom +5329 ClearGraphStyles + +## Boot +8018 MD5Final +801B RSAValidate +801E BigNumCompare +8021 WriteAByteUnsafe +8024 EraseFlash +8027 FindFirstCertificateField +802A ZeroToCertificate +802D GetCertificateEnd +8030 FindGroupedField +8033 DoNothing0 +8036 DoNothing1 +8039 DoNothing2 +803C DoNothing3 +803F DoNothing4 +8042 ATimesE +8045 ATimesDE +8048 DivHLByE +804B DivHLByDE +8051 LoadAIndPaged +8054 FlashToRAM2 +8057 GetCertificateStart +805A GetFieldSize +805D FindSubField +8060 EraseCertificateSector +8063 CheckHeaderKey +806C Load_BootLFontV2 +806F Load_BootLFontV +8072 OSReceive +8075 FindOSHeaderSubField +8078 FindNextCertificateField +807B RecAByteBoot +807E GetCalcSerial +8084 EraseFlashPage +8087 WriteFlashUnsafe +808A DispBootVer +808D MD5Init +8090 MD5Update +8093 MarkOSInvalid +8096 FindAppKey +8099 MarkOSValid +809C CheckOSValid +809F SetupAppPubKey +80A2 RabinValidate +80A5 TransformHash +80A8 IsAppFreeware +80AB FindAppHeaderSubField +80AE RecalcValidationBytes +80B1 Div32ByDE +80B4 FindSimpleGroupedField +80B7 GetBootVer +80BA GetHWVer +80BD XorA +80C0 RSAValidateBigB +80C3 ProdNrPart1 +80C6 WriteAByteSafe +80C9 WriteFlashSafe +80CC SetupDateStampPubKey +80CF SetAppLimit +80D2 BatteryError + +## 84+ Boot +80E4 USBBootMainLoop +80E7 DisplaySysMessage +80EA NewLine2 +80ED DisplaySysErrorAndTurnOff +80F0 CheckBattery +80F3 CheckBattery46 +80F6 OSReceiveUSB +80F9 OSPacketSetup +80FC ForceReboot +80FF SetupOSPubKey +8102 CheckHeaderKeyHL + +[labels] +8000 ramStart +8000 appData +8100 ramCode +822F ramCodeEnd +8230 baseAppBrTab +8251 bootTemp +8269 MD5Length +8292 MD5Hash +82A3 appSearchPage +82A5 tempSwapArea +83A5 MD5Buffer +838D appID +83ED ramReturnData +83EE arcInfo +8406 savedArcInfo +8432 appInfo +843C appBank_jump +843E appPage +843F kbdScanCode +8440 kbdLGSC +8441 kbdPSC +8442 kbdWUR +8443 kbdDebncCnt +8444 kbdKey +8445 kbdGetKy +8446 keyExtend +8447 contrast +8448 apdSubTimer +8449 apdTimer +844A curTime +844B curRow +844C curCol +844D curOffset +844E curUnder +844F curY +8450 curType +8451 curXRow +8452 prevDData +845A lFont_record +8462 sFont_record +846A tokVarPtr +846C tokLen +846E indicMem +8476 indicCounter +8477 indicBusy +8478 OP1 +8483 OP2 +848E OP3 +8499 OP4 +84A4 OP5 +84AF OP6 +84BF progToEdit +84C7 nameBuff +84D2 equ_edit_save +84D3 iMathPtr1 +84D5 iMathPtr2 +84D7 iMathPtr3 +84D9 iMathPtr4 +84DB iMathPtr5 +84DD chkDelPtr1 +84DF chkDelPtr2 +84E1 insDelPtr +84E3 upDownPtr +84E5 fOutDat +84EB asm_data_ptr1 +84ED asm_data_ptr2 +84EF asm_sym_ptr1 +84F1 asm_sym_ptr2 +84F3 asm_ram +8507 asm_ind_call +8508 textShadow +8588 textShadCur +858A textShadTop +858B textShadAlph +858C textShadIns +858D cxMain +858F cxPPutAway +8591 cxPutAway +8593 cxRedisp +8595 cxErrorEP +8597 cxSizeWind +8599 cxPage +859A cxCurApp +859B cxPrev +85AA monQH +85AB monQT +85AC monQueue +85BC onSP +85BE onCheckSum +85C0 promptRow +85C1 promptCol +85C2 promptIns +85C3 promptShift +85C4 promptRet +85C6 promptValid +85C8 promptTop +85CA promptCursor +85CC promptTail +85CE promptBtm +85D0 varType +85D1 varCurrent +85D9 varClass +85DA catCurrent +85DC menuActive +85DD menuAppDepth +85DE menuCurrent +85E8 progCurrent +85FE userMenuSA +865F ioPrompt +8660 RectFillPHeight +8660 dImageWidth +8661 RectFillPWidth +8662 RectFillPattern +8670 ioFlag +8672 sndRecState +8673 ioErrState +8674 header +867D ioData +8689 ioNewData +868B bakHeader +86B7 calc_id +86D7 penCol +86D8 penRow +86D9 rclQueue +86DB rclQueueEnd +86DD errNo +86DE errSP +86E0 errOffset +86EC saveSScreen +89EC usermem_offset +89EE bstCounter +89F0 flags +8A3A statVars +8C17 anovaf_vars +8C4D infVars +8D17 curGStyle +8D18 curGY +8D19 curGX +8D1A curGY2 +8D1B curGX2 +8D1C freeSaveY +8D1D freeSaveX +8DA1 XOffset +8DA2 YOffset +8DA3 lcdTallP +8DA4 pixWideP +8DA5 pixWide_m_1 +8DA6 pixWide_m_2 +8DA7 lastEntryPtr +8DA9 lastEntryStk +8E29 numLastEntries +8E2A currLastEntry +8E67 curInc +8E6A ORGXMIN +8EB4 uThetMin +8EBD uThetMax +8EC6 uThetStep +8ECF uTmin +8ED8 uTmax +8EE1 uTStep +8E7E uXmin +8E87 uXmax +8E90 uXscl +8E99 uYmin +8EA2 uYmax +8EAB uYscl +8EEA uPlotStart +8EF3 unMax +8EFC uu0 +8F05 uv0 +8F0E unMin +8F17 uu02 +8F20 uv02 +8F29 uw0 +8F32 uPlotStep +8F3B uXres +8F44 uw02 +8F50 Xmin +8F59 Xmax +8F62 Xscl +8F6B Ymin +8F74 Ymax +8F7D Yscl +8FB3 Tstep +8FBC PlotStart +8FC5 nMax +8FCE u0 +8FD7 v0 +8FE0 nMin +8FE9 u02 +8FF2 v02 +8FFB w0 +9004 PlotStep +900D XresO +9016 w02 +901F un1 +9028 un2 +9031 vn1 +903A vn2 +9043 wn1 +904C wn2 +9055 fin_N +905E fin_I +9067 fin_PV +9070 fin_PMT +9079 fin_FV +9082 fin_PY +908B fin_CY +9094 cal_N +909D cal_I +90A6 cal_PV +90AF cal_PMT +90B8 cal_FV +90C1 cal_PY +90D3 smallEditRAM +9151 Xres_int +913F XFact +9148 YFact +9152 deltaX +915B deltaY +9164 shortX +916D shortY +9176 lower +917F upper +8F86 ThetaMin +8F8F ThetaMax +8F98 ThetaStep +8FA1 TminPar +8FAA TmaxPar +918C XOutSym +918E XOutDat +9190 YOutSym +9192 YOutDat +9194 inputSym +9196 inputDat +9198 prevData +92C9 P1Type +92CA SavX1List +92CF SavY1List +92D4 SavF1List +92D9 P1FrqOnOff +92DA P2Type +92DB SavX2List +92E0 SavY2List +92E5 SavF2List +92EA P2FrqOnOff +92EB P3Type +92EC SavX3List +92F1 SavY3List +92F6 SavF3List +92FB P3FrqOnOff +92B3 TblMin +92BC TblStep +9302 ES +9340 plotSScreen +9640 seed1 +9649 seed2 +9652 parseVar +965B begPC +965D curPC +965F endPC +9776 GY1 +9777 GY2 +9778 GY3 +9779 GY4 +977A GY5 +977B GY6 +977C GY7 +977D GY8 +977E GY9 +977F GY0 +9780 GX1 +9781 GX2 +9782 GX3 +9783 GX4 +9784 GX5 +9785 GX6 +9786 GR1 +9787 GR2 +9788 GR3 +9789 GR4 +978A GR5 +978B GR6 +978C GU +978D GV +978E GW +966E cmdShadow +96EE cmdShadCur +96F0 cmdShadAlph +96F1 cmdShadIns +96F2 cmdCursor +96F4 editTop +96F6 editCursor +96F8 editTail +96FA editBtm +9706 editSym +9708 editDat +97A3 modePtr +97A5 winTop +97A6 winBtm +97A7 winLeftEdge +97A8 winLeft +97AA winAbove +97AC winRow +97AE winCol +97B0 fmtDigits +97B1 fmtString +97F2 fmtConv +9804 fmtLeft +9806 fmtIndex +9808 fmtMatSym +980A fmtMatMem +980C EQS +9815 delAdjAmt +9818 tSymPtr1 +981A tSymPtr2 +981C chkDelPtr3 +981E chkDelPtr4 +9820 tempMem +9822 fpBase +9824 FPS +9826 OPBase +9828 OPS +982A pTempCnt +982C cleanTmp +982E pTemp +9830 progPtr +9832 newDataPtr +9834 pagedCount +9835 pagedPN +9836 pagedGetPtr +9838 pagedPutPtr +983A pagedBuf +984D appErr1 +985A appErr2 +9867 flashByte1 +9868 flashByte2 +9869 freeArcBlock +986B arcPage +986C arcPtr +9870 appRawKeyHandle +9872 appBackUpScreen +9B72 customHeight +9B73 localLanguage +9B78 linkActivityHookPtr +9B7C cursorHookPtr +9B80 libraryHookPtr +9B84 rawKeyHookPtr +9B88 getKeyHookPtr +9B8C homescreenHookPtr +9B90 windowHookPtr +9B94 graphHookPtr +9B98 yEquHookPtr +9B9C fontHookPtr +9BA0 regraphHookPtr +9BA4 drawingHookPtr +9BA8 traceHookPtr +9BAC parserHookPtr +9BB0 appChangeHookPtr +9BB4 catalog1HookPtr +9BB8 helpHookPtr +9BBC cxRedispHookPtr +9BC0 menuHookPtr +9BC4 catalog2HookPtr +9BC8 tokenHookPtr +9BCC localizeHookPtr +9BD0 silentLinkHookPtr +9BD4 usbHookPtr +9C06 baseAppBrTab2 +9CB0 DBKeyScanCode +9CB1 DBKeyRptCtr +9D65 localTokStr +9D76 keyForStr +9D77 keyToStrRam +9D88 sedMonSp +9D8A bpSave +9D95 userMem +0FE66 symTable + +[flags] +00 kbdFlags +0,00 inDelete,ioDelFlag +2,00 trigDeg,trigFlags +3,00 kbdSCR,kbdFlags +4,00 kbdKeyPress,kbdFlags +5,00 donePrgm,doneFlags +01 editFlags +2,01 editOpen,editFlags +4,01 monAbandon,monFlags +02 plotFlags +0,02 plotTrace,plotFlags +1,02 plotLoc,plotFlags +2,02 plotDisp,plotFlags +4,02 grfFuncM,grfModeFlags +5,02 grfPolarM,grfModeFlags +6,02 grfParamM,grfModeFlags +7,02 grfRecurM,grfModeFlags +03 graphFlags +0,03 graphDraw,graphFlags +1,03 graphProg,graphFlags +04 grfDBFlags +0,04 grfDot,grfDBFlags +1,04 grfSimul,grfDBFlags +2,04 grfGrid,grfDBFlags +3,04 grfPolar,grfDBFlags +4,04 grfNoCoord,grfDBFlags +5,04 grfNoAxis,grfDBFlags +6,04 grfLabel,grfDBFlags +05 textFlags +1,05 textEraseBelow,textFlags +2,05 textScrolled,textFlags +3,05 textInverse,textFlags +4,05 textInsMode,textFlags +07 ParsFlag2 +0,07 numOP1,ParsFlag2 +08 apdFlags +0,08 preClrForMode,newDispF +2,08 apdAble,apdFlags +3,08 apdRunning,apdFlags +09 onFlags +0,09 appWantAlphaUpDn,alphaUpDnFlag +3,09 onRunning,onFlags +4,09 onInterrupt,onFlags +6,09 statsValid,statFlags +7,09 statANSDISP,statFlags +0A fmtFlags +0,0A fmtExponent,fmtFlags +1,0A fmtEng,fmtFlags +2,0A fmtHex,fmtFlags +3,0A fmtOct,fmtFlags +4,0A fmtBin,fmtFlags +5,0A fmtReal,fmtFlags +6,0A fmtRect,fmtFlags +7,0A fmtPolar,fmtFlags +0B fmtOverride +0,0B fmtExponent,fmtOverride +1,0B fmtEng,fmtOverride +2,0B fmtHex,fmtOverride +3,0B fmtOct,fmtOverride +4,0B fmtBin,fmtOverride +5,0B fmtReal,fmtOverride +6,0B fmtRect,fmtOverride +7,0B fmtPolar,fmtOverride +0C curFlags +0,0C fmtEdit,fmtEditFlags +2,0C curAble,curFlags +3,0C curOn,curFlags +4,0C curLock,curFlags +5,0C cmdVirgin,cmdFlags +0D appFlags +0,0D appWantIntrpt,appFlags +1,0D appTextSave,appFlags +2,0D appAutoScroll,appFlags +3,0D appMenus,appFlags +4,0D appLockMenus,appFlags +5,0D appCurGraphic,appFlags +6,0D appCurWord,appFlags +7,0D appExit,appFlags +0E rclFlags +7,0E rclQueueActive +0F seqFlags +0,0F webMode,seqFlags +1,0F webVert,seqFlags +2,0F sequv,seqFlags +3,0F seqvw,seqFlags +4,0F sequw,seqFlags +11 promptFlags +0,11 promptEdit,promptFlags +3,11 expr_param,plotFlag2 +4,11 expr_writing,plotFlag2 +12 shiftFlags +0,12 indicRun,indicFlags +1,12 indicInUse,indicFlags +2,12 indicOnly,indicFlags +3,12 shift2nd,shiftFlags +4,12 shiftAlpha,shiftFlags +5,12 shiftLwrAlph,shiftFlags +6,12 shiftALock,shiftFlags +7,12 shiftKeepAlph,shiftFlags +13 tblFlags +4,13 autoFill,tblFlags +5,13 autoCalc,tblFlags +6,13 reTable,tblFlags +14 sGrFlags +0,14 grfSplit,sGrFlags +1,14 vertSplit,sGrFlags +2,14 grfSChanged,sGrFlags +3,14 grfSplitOverride,sGrFlags +4,14 write_on_graph,sGrFlags +5,14 g_style_active,sGrFlags +6,14 cmp_mod_box,sGrFlags +7,14 textWrite,sGrFlags +15 newIndicFlags +0,15 extraIndic,newIndicFlags +1,15 saIndic,newIndicFlags +16 newFlags2 +5,16 noRestores,newFlags2 +17 smartFlags +0,17 smartGraph,smartFlags +1,17 smartGraph_inv,smartFlags +1A more_Flags +2,1A No_Del_Stat,more_Flags +0,1D gkKeyRepeating,gkFlag +21 asm_Flag1 +22 asm_Flag2 +23 asm_Flag3 +24 appLwrCaseFlag +1,24 comFailed,getSendFlg +3,24 lwrCaseActive,appLwrCaseFlag +26 groupFlags +1,26 inGroup,groupFlags +28 apiFlag +0,28 appAllowContext,apiFlag +4,28 appRunning,apiFlag +7,28 appRetOffKey,apiFlag +29 apiFlg2 +2A apiFlg3 +2B apiFlg4 +2,2B fullScrnDraw,apiFlg4 +2C apiFlg5 +0,2C appWantDiagonalKeys,apiFlg5 +2E xapFlag0 +2F xapFlag1 +30 xapFlag2 +31 xapFlag3 +32 fontFlags +2,32 fracDrawLFont,fontFlags +3,32 fracTallLFont,fontFlags +6,32 drawLFont,fontFlags +7,32 customFont,fontFlags +33 hookFlags0 +0,33 alt_On,scriptFlag +1,33 alt_Off,scriptFlag +2,33 useRclQueueEnd,rclFlag2 +3,33 ignoreBPLink,backGroundLink +4,33 linkActivityHookActive,linkActivityHookFlag +34 hookFlags1 +0,34 getKeyHookActive,getKeyHookFlag +1,34 libraryHookActive,libraryHookFlag +4,34 homescreenHookActive,homescreenHookFlag +5,34 rawKeyHookActive,rawKeyHookFlag +6,34 catalog2HookActive,catalog2HookFlag +7,34 cursorHookActive,cursorHookFlag +35 hookFlags2 +0,35 tokenHookActive,tokenHookFlag +1,35 localizeHookActive,localizeHookFlag +2,35 windowHookActive,windowHookFlag +3,35 graphHookActive,graphHookFlag +4,35 yEquHookActive,yEquHookFlag +5,35 fontHookActive,fontHookFlag +6,35 regraphHookActive,regraphHookFlag +7,35 drawingHookActive,drawingHookFlag +36 hookFlags3 +0,36 traceHookActive,traceHookFlag +1,36 parserHookActive,parserHookFlag +2,36 appChangeHookActive,appChangeHookFlag +3,36 catalog1HookActive,catalog1HookFlag +4,36 helpHookActive,helpHookFlag +5,36 cxRedispHookActive,cxRedispHookFlag +6,36 menuHookActive,menuHookFlag +7,36 silentLinkHookActive,silentLinkHookFlag +37 hookAutoFlags1 +38 hookAutoFlags2 +39 hookAutoFlags3 +3A hookFlags4 +0,3A usbHookActive,usbHookFlag +3C plotFlag3 +0,3C bufferOnly,plotFlag3 +4,3C useFastCirc,plotFlag3 +3D DBKeyFlags +0,3D leftShiftPressed,DBKeyFlags +1,3D rightShiftPressed,DBKeyFlags +2,3D diamondPressed,DBKeyFlags +3,3D squarePressed,DBKeyFlags +4,3D repeatMost,DBKeyFlags +5,3D haveDBKey,DBKeyFlags +6,3D keyDefaultsF,DBKeyFlags +7,3D HWLinkErrF,DBKeyFlags +3E openLibFlag +4,3E openLibActive,openLibFlag +3F clockFlags +0,3F clockNotMonthFirst,clockFlags +1,3F clockYearFirst,clockFlags +2,3F clock24Hour,clockFlags +4,3F clockFmtASCII,clockFlags diff --git a/tool/tilem-src/data/symbols/ti85.sym b/tool/tilem-src/data/symbols/ti85.sym new file mode 100644 index 0000000..4cab7ed --- /dev/null +++ b/tool/tilem-src/data/symbols/ti85.sym @@ -0,0 +1,254 @@ +[macros] +0CD098C ROM_CALL~%C +0CD0C8C CALL_~Z,prgm+%j +0CD0F8C CALL_~prgm+%j +0CD128C CALL_~NZ,prgm+%j +0CD188C CALL_~C,prgm+%j +0CD1E8C CALL_~NC,prgm+%j +0CD248C JUMP_~Z,prgm+%j +0CD278C JUMP_~prgm+%j +0CD2A8C JUMP_~NZ,prgm+%j +0CD308C JUMP_~C,prgm+%j +0CD368C JUMP_~NC,prgm+%j +0CDC88C RCALL_~%j + +[romcalls] +00 TX_CHARPUT +01 D_LT_STR +02 M_CHARPUT +03 D_ZM_STR +04 D_LM_STR +05 GET_T_CUR +06 SCROLL_UP +07 TR_CHARPUT +08 CLEARLCD +09 D_HL_DECI +0A CLEARTEXT +0B D_ZT_STR +0C BUSY_OFF +0D BUSY_ON +80 FIND_PIXEL + +[labels] +0008 rOP1TOOP2 +0010 rFINDSYM +0018 rPUSHOP1 +0020 rMOV10TOOP1 +0028 rFPMULT +0030 rFPADD + +0033 LD_HL_MHL +008E CP_HL_DE +009A UNPACK_HL +01A2 READ_KEYPAD +01B1 STORE_KEY +01BE GET_KEY +0115 UPDATE_APD +0168 READ_KEY + +8000 kbdScanCode +8001 kbdLGSC +8002 kbdPSC +8003 kbdWUR +8004 kbdDebncCnt +8005 kbdKey +8006 kbdGetKy +8007 contrast +8008 apdSubTimer +8009 apdTimer +800A apdWarmUp +800B curTime +800C curRow +800D curCol +800E curUnder +800F undelBufLen +8010 undelBuf +# 8074 tokVarPtr? +# 8076 tokLen? +8078 indicMem +8080 indicCounter +8081 indicBusy +8082 OP1 +808D OP2 +8098 OP3 +80A3 OP4 +80AE OP5 +80B9 OP6 +80C6 iMathPtr1 +80C8 iMathPtr2 +80CA iMathPtr3 +80CC iMathPtr4 +80CE iMathPtr5 +# 80D0 chkDelPtr1? +# 80D2 chkDelPtr2? +# 80D4 insDelPtr? +# 80D6 upDownPtr? +80DF textShadow +8187 textShadCur +# 8189 textShadTop? +# 818A textShadAlph? +# 818B textShadIns? +818C textAccent +818D cxMain +# 818F cxPPutAway? +# 8191 cxPutAway? +# 8193 cxRedisp? +# 8195 cxErrorEP? +8197 cxSizeWind +8199 cxPage +# 819A cxCurApp? +819B cxPrev +# 81AA monQH? +# 81AB monQT? +# 81AC monQueue? +81BC onSP +81BE onCheckSum +81DD menuActive +8333 penCol +8334 penRow +# 8335 rclQueue? +8337 errNo +8338 errSP +# 833A errOffset? +8346 flags +8364 stCounter +# 85E3 XOutDat? +# 85E7 YOutDat? +# 85EB inputDat? +8629 ES +8641 plotSScreen +8A41 seed1 +8A4B seed2 +# 8A56 parseVar? +8A5F begPC +8A61 curPC +8A63 endPC +8A6B cmdShadow +# 8B27 editDat? +8B2D modePtr +8B2F winTop +8B30 winBtm +8B31 winLeftEdge +8B32 winLeft +8B34 winAbove +8B36 winRow +# 8B38 winCol? +8B3A fmtDigits +# 8B96 fmtMatMem? +# 8B98 EQS? +8BD2 delAdjAmt +8BDD tempMem +8BDF fpBase +8BE1 FPS +8BE3 OPBase +8BE5 OPS +# 8BE7 pTempCnt? +# 8BE9 cleanTmp? +8BEB pTemp +8BF7 userMem +0FA6F symTable +0FC00 videoRAM + +## ZShell +8C3C PROGRAM_ADDR +8C3E ROM_VERS +8C3F ZSHELL_VER +8C40 ZS_BITS + +## Usgard +8E8B ORGSP +8EA2 USGSHELL +8EAB VATName +8EB4 DEST_ADDR +8EB4 PAGE1ADDR +8EB6 PAGE2ADDR +8C08 PROG_BYTE +8C41 TX_CHARPUT +8C44 D_LT_STR +8C47 M_CHARPUT +8C4A D_ZM_STR +8C4D D_LM_STR +# 8C50 SCROLL_UP +# 8C53 TR_CHARPUT +# 8C56 CLEARLCD +# 8C59 D_HL_DECI +# 8C5C CLEARTEXT +# 8C5F D_ZT_STR +# 8C62 BUSY_OFF +# 8C65 BUSY_ON +# 8C68 RANDOM +8C6B FIND_PIXEL +8C77 FREEMEM +8C7A VAR_LENGTH +8C7D ASCIIZ_LEN +8C80 NEG_BC +8C83 MUL_HL +8C8C COPY_STRING +8C9B USGARD_INT_INSTALL +8C9E USGARD_INT_REMOVE +8CA1 USGARD_INT_CLEAN +8C95 APPEND +8C98 UNAPPEND +8CCB CHECK_APPEND +8CA4 VAR_NEW +8CA7 VAR_DELETE +8CAA VAR_EXEC +8CAD VAR_GET +8CB0 VAR_RESIZE +8CCE RELOC +8CD1 DERELOC +8CD7 RELOC_TAB +8CB3 SEARCH_VAT +8CB6 OTH_SHUTDOWN +8CB9 DM_HL_DECI +8CBC OTH_PAUSE +8CBF OTH_CLEAR +8CC2 OTH_EXIT +8CC5 OTH_ARROW +8CD4 OTH_FILL + +## Rigel +# 8C50 GET_T_CUR +# 8C53 SCROLL_UP +# 8C56 TR_CHARPUT +# 8C59 CLEARLCD +# 8C5C D_HL_DECI +# 8C5F CLEARTEXT +# 8C62 D_ZT_STR +# 8C65 BUSY_OFF +# 8C68 BUSY_ON +8C6E RIGEL_INT_INSTALL +8C71 RIGEL_INT_REMOVE +8C06 VAR_SEARCH + +[flags] +00 kbdFlags +2,00 trigDeg,trigFlags +3,00 kbdSCR,kbdFlags +4,00 kbdKeyPress,kbdFlags +05 textFlags +2,05 textScrolled,textFlags +3,05 textInverse,textFlags +4,05 textInsMode,textFlags +08 apdFlags +2,08 apdAble,apdFlags +3,08 apdRunning,apdFlags +09 onFlags +3,09 onRunning,onFlags +4,09 onInterrupt,onFlags +0C curFlags +2,0C curAble,curFlags +3,0C curOn,curFlags +4,0C curLock,curFlags +0D appFlags +1,0D appTextSave,appFlags +5,0D appCurGraphic,appFlags +6,0D appCurWord,appFlags +12 indicFlags +0,12 indicRun,indicFlags +2,12 indicOnly,indicFlags +3,12 shift2nd,shiftFlags +4,12 shiftAlpha,shiftFlags +5,12 shiftLwrAlph,shiftFlags +6,12 shiftALock,shiftFlags +7,12 shiftKeepAlph,shiftFlags diff --git a/tool/tilem-src/data/symbols/ti86.sym b/tool/tilem-src/data/symbols/ti86.sym new file mode 100644 index 0000000..5fb2cf3 --- /dev/null +++ b/tool/tilem-src/data/symbols/ti86.sym @@ -0,0 +1,1112 @@ +[labels] +0008 rOP1TOOP2 +0010 rFINDSYM +0018 rPUSHOP1 +0020 rMOV10TOOP1 +0028 rFPMULT +0030 rFPADD + +4010 _ldhlind +4028 _chkON +402C _bitgrffuncm +4030 _bitgrfpolarm +4034 _bitgrfparamm +4038 _bitgrfrecurm +403C _cphlde +4040 _put_colon +4044 _divHLby10 +4048 _divHLbyA +404C _divAHLby10 +4058 _timeout +4060 _resetAPD +4064 _scan_code +4068 _get_key +409C _jforcecmdnochar +40B5 _pPutAwayPrompt +40BD _call_cxPPutAway +40C1 _call_cxPutAway +40C5 _call_cxSizeWind +40C9 _call_cxErrorEP +40CD _call_cxMain +40D1 _cxNull +40D2 _p_cxNull +40DD _err_handler +40E1 _set_cx_100 +40E5 _set_cx_50 +40E9 _set_cx_dec +40ED _set_context +4101 _off +4109 _reset +4119 _removTok +412D _errAxes +4130 _errFldOrder +4133 _errStatPlot +4136 _errOverflow +4139 _errDivBy0 +413c _errSingularMat +413f _errDomain +4142 _errIncrement +4145 _errSyntax +4148 _errNumberBase +414B _errMode +414e _errDataType +4151 _errArgument +4154 _errDimMismatch +4157 _errDimension +415A _errUndefined +4169 _errReserved +416c _errInvalid +416f _errIllegalNest +4172 _errBound +4175 _errGraphWindow +4178 _errZoom +417b _errBreak +417e _errStat +4181 _errConversion +4184 _errSolver +4187 _errIterations +418a _errBadGuess +418d _errDifEqSetup +4190 _errPoly +4193 _errTolNotMet +4196 _errLink +4199 _errorA +419C _error +41A1 _instError +41A4 _removError +41B7 _ld_de_fp0 +41BB _ld_de_fp1 +41BF _mulHL10 +41C3 _ckop1cplx +41C7 _ckcplx +41CB _ckop1real +41FB _cpop1op2 +4203 _op3toop4 +4207 _op1toop4 +420B _op2toop4 +420F _movtoop4 +4213 _op4toop2 +4217 _op4toop3 +421B _op3toop2 +421F _op1toop3 +4223 _movfrop1 +4227 _op5toop2 +422B _op5toop6 +422F _op5toop4 +4233 _op1toop2 +4237 _movtoop2 +423B _op6toop2 +423F _op6toop1 +4243 _op4toop1 +4247 _op5toop1 +424B _op3toop1 +424F _op4toop5 +4253 _op3toop5 +4257 _op2toop5 +425B _movtoop5 +425F _op2toop6 +4263 _op1toop6 +4267 _movtoop6 +426B _op1toop5 +426F _op2toop1 +4273 _movtoop1 +4277 _mov11b +427B _mov10b +427F _mov9b +4283 _mov9b_ +4287 _mov8b +428B _mov7b +428F _mov7b_ +4293 _mov6b +4297 _mov5b +429B _mov4b +429F _mov3b +42A3 _mov2b +42A7 _op4toop2m +42CB _op2toop3 +42CF _movtoop3 +42D3 _op4toop6 +42D7 _mov10toop1 +42DB _mov10toop1op2 +42DF _mov10toop2 +42E3 _movfroop1 +42E7 _op4set1 +42EB _op3set1 +42EF _op2set8 +42F7 _op2set5 +42FB _op2set4 +4303 _op2set3 +430F _op1set1 +4313 _op1set4 +4317 _op1set3 +431B _op3set2 +431F _op1set2 +4323 _op2set2 +432F _op2set1 +4343 _ld_hl_8z +4347 _ld_hl_8a +434B _ld_hlplus1_7a +434F _ld_hl_7a +4353 _op4set0 +4357 _op3set0 +435B _op2set0 +435F _op1set0 +4363 _ld_hl_fp0 +4367 _zeroop1m +436B _zeroop1 +436F _zeroop2 +4373 _zeroop3 +4377 _ld_hl_11z +437B _ld_hl_bz +4383 _shracc +438B _shlacc +446F _ex_op2_op6 +4473 _ex_op5_op6 +4477 _ex_op1_op5 +447B _ex_op1_op6 +447F _ex_op2_op4 +4483 _ex_op2_op5 +4487 _ex_op1_op3 +448B _ex_op1_op4 +448F _ex_op1_op2 +449B _ckop1fp0 +44A3 _ckop2fp0 +44B3 _ckop1int +44B7 _ckint +44BB _ckop1odd +44BF _ckodd +450B _ckop2pos +450F _ckop1pos +4513 _absop2 +4527 _inco1exp +4547 _HtimesL +458F _findsym_error +45E3 _invsub +45EB _PLUS1 +45EF _inc_ptr_ade +45F3 _ex_ahl_bde +460B _get_size_word +4613 _setXXop1 +4617 _setXXop2 +461B _setXXXXop2 +462F _load_ram_ahl +4633 _conv_ahl +4637 _inc_ptr_ahl +463B _dec_ptr_ahl +463F _inc_ptr_bde +4643 _dec_ptr_bde +4647 _set_abs_src_addr +464B _get_free_mem +464F _set_mm_num_bytes +4657 _round_OP1 +46AF _check_asm +46B7 _jump_table +46BB _memchk +46BF _dec_ptr_ade +46C3 _getb_ahl +46C7 _cp_ahl_bde +46CB _findsym +46D3 _copy_fwd +46D7 _del_temp_vars +46EB _createreal +46EF _createrconst +46F3 _createcconst +46F7 _createcplx +46FB _creatervect_temp +46FF _creatervect +4703 _createcvect_temp +4707 _createcvect +470B _createrlist_temp +470F _createrlist +4713 _createclist_temp +4717 _createclist +471B _creatermat_temp +471F _creatermat +4723 _createcmat_temp +4727 _createcmat +472B _createstrng_temp +472F _createstrng +4733 _createequ_temp +4737 _createequ +473B _createpict +474F _createprog +475B _copy_bkwd +475F _delvar +476F _update_VAT_ptrs +477B _get_size +477F _get_var_size +4783 _push_bc_OPS +4787 _check_STACK_mem +478B _pop_bc_OPS +478F _push_a_OPS +4793 _pop_a_OPS +479F _popop1 +47A3 _poprealo6 +47A7 _poprealo5 +47AB _poprealo4 +47AF _poprealo3 +47A3 _poprealo2 +47A7 _poprealo1 +47CB _sub_FPS_20 +47CF _sub_FPS_10 +47D3 _sub_FPS_bc +47DB _deallocfps1 +47E3 _ram_page_1 +47E7 _load_ram_OPS +47EB _load_ram_ES +47EF _load_ram_FPS +47F3 _ram_page_7 +4813 _pushrealo1 +4893 _cpyto2fpst +4897 _cpyto1fpst +48AF _cpyto2fps1 +48C3 _cpyto2fps2 +48D7 _cpyo2tofpst +48DB _cpyo6tofpst +48DF _cpyo1tofpst +48E3 _cpydetofpst +48E7 _cpydetohlt +48EB _cpydetohlc +48EF _cpyo5tofps2 +48F3 _cpyo2tofpsto1tofps1 +48F7 _cpyo1tofps1 +48FB _cpydetofps1 +48FF _cpydetohl1 +4903 _cpyo2tofps2 +4907 _cpyo3tofps2 +490B _cpyo6tofps2 +490F _cpyo1tofps2 +4913 _cpydetofps2 +4917 _cpydetohl2 +491B _cpyo5tofps3 +491F _cpyo2tofps2o1tofps3 +4923 _cpyo1tofps3 +4927 _cpydetofps3 +492B _cpydetohl3 +492F _cpyo1tofps4 +4933 _cpydetofps4 +4937 _cpydetohl4 +493B _cpyo1tofps6 +493F _cpyo1tofps7 +4943 _cpyo1tofps8 +494F _ask_self_test +4953 _self_test +4957 _strlen +495B _strcpy +495F _strcat +4963 _strcmp +496B _find_bit +498C _cursorOff +4994 _cursorOn +49A0 _reset_MATH +49B0 _disp_GRAPH +49DC _flushallmenus +49E8 _disp_menu +4A0A _exec_pg1 +4A27 _putmap +4A2B _putc +4A33 _dispAHL +4A37 _puts +4A3B _putps +4A5F _newline +4A7E _clrLCD +4A82 _clrScrn +4A86 _clrWindow +4A8A _clrLine +4A95 _homeup +4AA1 _vputmap +4AA5 _vputs +4AA9 _vputsn +4AAD _runindicon +4AB1 _runindicoff +4AB5 _clrText +4B1B _exec_pg2 +4B1F _binopexec1 +4B93 _tofrac +4B9F _gfudydx +4C2F _INTOP1 +4C3F _ahl_plus_2_pg3 +4C47 _exec_basic +4C9F _stoAns +4CB3 _stoY +4CBB _stoX +4CBF _stoOther +4CDF _rclY +4CE3 _rclX +4CE7 _rclVarSym +4D13 _get_token +4D1B _get_varname +4D3F _disp +4D43 _pause +4D6F _PDspGrph +4D73 _horizCmd +4D77 _vertCmd +4DAF _unpack_hex +4E39 _grbufcpy +4E51 _ILine +4E59 _IPoint +4E71 _geqnamea +4FA8 _set_app_title +514B _FindAlphaUp +514F _FindAlphaDn +515B _dispOP1 +515F _dispDone +5191 _formReal +51E9 _CLine +5209 _get_abs_src_addr +521D _get_word_ahl +5221 _set_word_ahl +5235 _abs_mov10toop1 +5239 _abs_mov10toop1_noset +523D _abs_mov10b_set_d +5249 _abs_mov10b +5241 _abs_movfrop1_set_d +5245 _abs_movfrop1 +5285 _set_abs_dest_addr +52B5 _RcPicGrph +52ED _mm_ldir +52F1 _mm_lddr +5369 _get_statvar +5371 _getky +5398 _low_battery +5464 _mov10op2add +5468 _INTGR +5470 _MINUS1 +5474 _FPSUB +5478 _FPADD +5484 _TIMESPT5 +5488 _FPSQUARE +548C _FPMULT +5490 _invop1op2 +5494 _invop1s +5498 _invop2s +549C _FRAC +54A4 _FPRECIP +54A8 _FPDIV +54AC _SQROOT +54B0 _SQROOTP +54BC _RNDGUARD +54C0 _ROUND +54C4 _LNX +54C8 _LNXP +54CC _LOGXP +54D0 _LOGX +54D4 _ETOX +54D8 _TENX +54E0 _SIN +54E4 _COS +54E8 _TAN +54F0 _TANH +54F4 _COSH +54F8 _SINH +5508 _ACOS +550C _ACOSP +5510 _ATAN +5514 _ASIN +551C _ATANH +5524 _ASINH +5528 _ACOSH +5538 _YTOX +5544 _randint +5567 _writeb_inc_ahl +5577 _convop1 +557B _set_mode +55a3 _asmComp +55AA _getkey +55DA _random +5643 _vputspace +569D _get_char +56A1 _get_vchar +56EA _call_user_on +56ED _call_user_off +56F0 _call_sqrtexpr +56F3 _call_sqrtparse +56F6 _call_sqrtexec +56F9 _call_sqrtform +56FC _call_sqrtcmdtok +56FF _call_sqrthome +5702 _call_sqrtkey +5705 _call_sqrtgrf +5718 _exec_pg4 +5714 _exec_pg3 +571C _linkExec +5730 _exec_assembly +5732 _errNoSignChng +575C _instTok + +0C000 _kbdScanCode +0C001 _kbdLGSC +0C002 _kbdPSC +0C003 _kbdWUR +0C004 _kbdDebncCnt +0C005 _kbdkey +0C006 _kbdGetKy +0C007 _keyextend +0C008 _contrast +0C009 _APDSubTimer +0C00A _APDTimer +0C00B _APDWarmUp +0C00C _viet +0C00E _curTime +0C00F _curRow +0C010 _curCol +0C011 _curUnder +0C012 _undelBufLen +0C013 _undelBuf +0C077 _P_tokVarPtr +0C07A _toklen +0C07C _TOK_B3 +0C07D _DETOK_H3 +0C07E _MEMPRE_H3 +0C07F _indicMem +0C087 _indicCounter +0C088 _indicBusy +0C089 _OP1 +0C08A _OP1EXPM +0C08B _OP1EXPL +0C08C _OP1M +0C093 _OP1EXT +0C094 _LOGKP +0C094 _OP2 +0C095 _OP2EXPM +0C096 _OP2EXPL +0C097 _OP2M +0C09E _OP2EXT +0C09F _OP3 +0C09F _LOGKM +0C0A0 _OP3EXPM +0C0A1 _OP3EXPL +0C0A2 _OP3M +0C0A9 _OP3EXT +0C0AA _CORDFLG1 +0C0AA _OP4 +0C0AB _OP4EXPM +0C0AC _OP4EXPL +0C0AD _OP4M +0C0B4 _OP4EXT +0C0B5 _EK +0C0B5 _CORDFLG +0C0B5 _OP5 +0C0B6 _OP5EXPM +0C0B6 _SF +0C0B6 _EL +0C0B7 _OP5EXPL +0C0B7 _EM +0C0B8 _OP5M +0C0B8 _EMM1 +0C0B9 _EITS +0C0BA _ENM2 +0C0BB _ENA +0C0BC _EEN +0C0BF _OP5EXT +0C0C0 _EN +0C0C0 _OP6 +0C0C1 _EJ +0C0C1 _OP6EXPM +0C0C2 _OP6EXPL +0C0C2 _EEI +0C0C3 _OP6M +0C0C5 _ELOW +0C0C6 _EIGH +0C0CA _OP6EXT +0C0CC _OP7 +0C0D7 _CPLXTRG +0C0D7 _IOFLAG +0C0D8 _P_IMATHPTR1 +0C0DB _P_IMATHPTR2 +0C0DE _P_IMATHPTR3 +0C0E1 _P_IMATHPTR4 +0C0E4 _P_IMATHPTR5 +0C0E7 _CHKDELPTR1 +0C0E7 _P_CHKDELPTR1 +0C0EA _P_CHKDELPTR2 +0C0ED _P_INSDELPTR +0C0F0 _P_UPDOWNPTR +0C0F3 _STDRNGSGN +0C0F4 _POLRNGSGN +0C0F5 _PARRNGSGN +0C0F6 _DIFRNDSGN +0C0F7 _USRRNGSGN +0C0F8 _STATSGN +0C0F9 _textShadow +0C1A1 _textShadCur +0C1A3 _textShadTop +0C1A4 _textShadAlph +0C1A5 _textShadIns +0C1A6 _textAccent +0C1A7 _cxMain +0C1A9 _cxPPutAway +0C1AB _cxPutAway +0C1AD _cxRedisp +0C1AF _cxErrorEP +0C1B1 _cxSizeWind +0C1B3 _cxPage +0C1B4 _CXCURAPP +0C1B5 _cxPrev +0C1C4 _monQH +0C1C5 _monQT +0C1C6 _monQueue +0C1D6 _onSP +0C1D8 _onCheckSum +0C1DA _promptRow +0C1DB _promptCol +0C1DC _promptIns +0C1DD _promptShift +0C1DE _promptRet +0C1E0 _promptValid +0C1E2 _P_promptTop +0C1E5 _P_promptCursor +0C1E8 _P_promptTail +0C1EB _P_promptBtm +0C1EE _varType +0C1EF _varCurrent +0C1F8 _varFAFlags +0C1FA _varClass +0C1FB _catCurrent +0C1FD _menuActive +0C1FE _menu2Hilite +0C1FF _menuSingle +0C201 _menuAppStack +0C20D _menuAppPtr +0C20F _menuAppDepth +0C210 _menuSysStack +0C21C _menuSysPtr +0C21E _menuSysDepth +0C21F _menuPrvStack +0C22B _menuPrvPtr +0C22D _menuPrvDepth +0C22E _m2i +0C242 _menuDyn1 +0C26A _menuDyn5 +0C274 _userMenu1 +0C275 _userMenuTitle +0C27C _userMenu2 +0C284 _userMenu3 +0C28C _userMenu4 +0C294 _userMenu5 +0C29C _userMenuSA +0C31C _XSTATSAV +0C324 _ioPrompt +0C326 _YSTATSAV +0C330 _FSTATSAV +0C33A _IOSNDTYP +0C33B _SNDRECSTATE +0C33C _IOERRSTATE +0C33D _HEADER +0C346 _IODATA +0C352 _BAKHEADER +0C35B _TBLRNGSGN +0C35C _calc_id +0C37C _penCol +0C37D _penRow +0C37E _P_RCLQUEUE +0C381 _ERRNO +0C382 _ERRSP +0C384 _errOffset +0C386 _ram_to_use +0C390 _offerr_sav_bc +0C392 _ABS_SRC_ADDR +0C395 _ABS_DEST_ADDR +0C398 _MM_NUM_BYTES +0C39B _mm_tmp1 +0C39D _mm_tmp2 +0C39F _mm_tmp3 +0C3A1 _mm_tmp4 +0C3A3 _mm_tmp5 +0C3A5 _ram_cache +0C3E5 _Flags +0C40A _ram_to_use1 +0C414 _statReg +0C415 _STATVARS +0C555 _STCounter +0C555 _curgstyle +0C556 _curGY +0C557 _curGX +0C558 _curGY2 +0C559 _curGX2 +0C55A _curgstyle_save +0C55B _curgstylesave +0C55C _plotflagsave +0C55D _XMINPTR +0C55F _XMAXPTR +0C561 _XSCLPTR +0C563 _YMINPTR +0C565 _YMAXPTR +0C567 _YSCLPTR +0C569 _DIF1STCURINC +0C56B _TRACEPLOT +0C56C _BOXPLOTINFO +0C56D _SCURINC +0C56F _CURINC +0C571 _YPIXEL +0C572 _ORGXMIN +0C57C _PANSHIFT +0C586 _USRRNGSIZE +0C588 _UTHETMIN +0C58D _STSP +0C58D _STRAMStart +0C592 _UTHETMAX +0C59C _UTHETSTEP +0C5A6 _UTPLOT +0C5B0 _UTMIN +0C5BA _UTMAX +0C5C4 _UTSTEP +0C5CE _UXMIN +0C5D8 _UXMAX +0C5E2 _UXSCL +0C5EC _UYMIN +0C5F6 _UYMAX +0C600 _UYSCL +0C60A _UXRES +0C614 _XRES_INT +0C615 _HDERIV +0C61F _TOL +0C629 _XFACT +0C633 _YFACT +0C63D _DELTAX +0C647 _DELTAY +0C651 _SHORTX +0C65B _SHORTY +0C665 _FUNRNGSIZE +0C667 _FLAGSF +0C668 _XMINF +0C672 _XMAXF +0C67C _XSCLF +0C686 _YMINF +0C690 _YMAXF +0C69A _YSCLF +0C6A4 _LOWER +0C6AE _UPPER +0C6B8 _XRES +0C6C2 _POLRNGSIZE +0C6C4 _FLAGSPOL +0C6C5 _THETAMIN +0C6CF _THETAMAX +0C6D9 _THETASTEP +0C6E3 _XMINPOL +0C6ED _XMAXPOL +0C6F7 _XSCLPOL +0C701 _YMINPOL +0C70B _YMAXPOL +0C715 _YSCLPOL +0C71F _PARRNGSIZE +0C721 _FLAGSPAR +0C722 _TMINPAR +0C72C _TMAXPAR +0C736 _TSTEPPAR +0C740 _XMINPAR +0C74A _XMAXPAR +0C754 _XSCLPAR +0C75E _YMINPAR +0C768 _YMAXPAR +0C772 _YSCLPAR +0C77C _DIFRNGSIZE +0C77E _FLAGSDIF +0C77F _TOLERDIF +0C789 _TPLOTDIF +0C793 _TMINDIF +0C79D _TMAXDIF +0C7A7 _TSTEPDIF +0C7B1 _XMINDIF +0C7BB _XMAXDIF +0C7C5 _XSCLDIF +0C7CF _YMINDIF +0C7D9 _YMAXDIF +0C7E3 _YSCLDIF +0C7ED _XAXISDIF +0C7EE _YAXISDIF +0C7EF _SLOPEF_EQU +0C7F0 _DIRF_X +0C7F1 _DIRF_Y +0C7F2 _DIRF_TIME +0C7FC _FRES +0C806 _INTS +0C810 _DNEQ +0C811 _P_XOUTSYM +0C814 _P_XOUTDAT +0C817 _P_YOUTSYM +0C81A _P_YOUTDAT +0C81D _P_INPUTSYM +0C820 _P_INPUTDAT +0C823 _P_FOUTDAT +0C826 _PREVDATA +0C862 _PREVDATA_EXT +0C86C _P1TYPE +0C86D _SavX1List +0C876 _SavY1List +0C87F _SavF1List +0C888 _P1FRQONOFF +0C889 _P2TYPE +0C88A _SavX2List +0C893 _SavY2List +0C89C _SavF2List +0C8A5 _P2FRQONOFF +0C8A6 _P3TYPE +0C8A7 _SavX3List +0C8B0 _SavY3List +0C8B9 _SavF3List +0C8C2 _P3FRQONOFF +0C8C3 _oldtype +0C8C4 _oldxlist +0C8CD _oldylist +0C8D6 _oldflist +0C8D6 _uppery +0C8DF _oldonoff +0C8E0 _tblpsrow +0C8E1 _tblscroll +0C8E3 _INPUTDAT_PG0 +0C8ED _TblLine +0C8F7 _OldTblMin +0C901 _TBLRNGSIZE +0C903 _TblMin +0C90D _TblStep +0C917 _TABLESGN +0C918 _TableYPtr +0C919 _curTblcol +0C91A _curTblrow +0C91B _dspTblcol +0C91C _dspTblrow +0C91D _higTblcol +0C91E _higTblrow +0C920 _TABLEXDATA +0C920 _TBLMATRIX +0C95C _TABLEYDATA +0C9D4 _TABLETEMPLATE +0C9D5 _SavedEqTok +0C9D7 _SavedEqNum1 +0C9D8 _SavedEqTok1 +0C9DA _SaveAppFlags +0C9DB _SaveCurFlags +0C9DC _SaveCurGstyle +0C9DD _SaveGraphFlags +0C9DE _evalflevel +0C9DF _TmpMatCols +0C9DF _ES +0C9E0 _TmpMatRows +0C9E1 _P_DERIVPTR +0C9E4 _DTMPThresh +0C9E6 _ELCPLXLCNT +0C9E8 _DERIVLEVEL +0C9E9 _P_DIFFEQPTR +0C9EB _P_DSOLVPTR +0C9EE _SOLVAR +0C9F7 _P_QUADPTR +0C9FA _plotSScreen +0CDFA _SEED1 +0CE04 _SEED2 +0CE0E _PARSEVAR +0CE18 _P_BEGPC +0CE1B _P_CURPC +0CE1E _P_ENDPC +0CE21 _ELCNT +0CE23 _COLCNT +0CE24 _ROWCNT +0CE25 _LCOUNT +0CE27 _EOS_ASAP_2ND +0CE28 _EXEC_CONV_SAVE +0CE2A _LASTENTRYPTR +0CE2C _LASTENTRYSTK +0CEAC _numlastentries +0CEAD _currlastentry +0CEAE _FREESAVEY +0CEAF _FREESAVEX +0CEB0 _STRACESAVE_TYPE +0CEB1 _STRACESAVE +0CEB3 _TRACESAVE +0CEB5 _DIF_T_SAVE +0CEBF _A_B_SAVE +0CEC0 _A_B_TYPE +0CEC1 _GS_DELX +0CEC2 _GS_D1_YINC +0CEC3 _GS_D2_YINC +0CEC4 _GS_DELY +0CEC5 _GS_MAX_Y_PIX +0CEC6 _CURRENT_STYLE +0CEC7 _CL_X1 +0CEC8 _CL_X2 +0CEC9 _CL_Y_DAT +0CECB _PREV_POINT +0CECD _RESSAVE +0CECE _DREQU_X +0CECF _DREQU_XINIT +0CED9 _DREQU_Y +0CEDA _DREQU_YINIT +0CEE4 _DREQU_XLIST +0CEE7 _DREQU_YLIST +0CEEA _DREQU_tLIST +0CEED _DREQU_COUNT +0CEEF _GY1 +0CF21 _GX1 +0CF53 _GR1 +0CF85 _GQ1 +0CF8A _EQU_EDIT_SAVE +0CF8B _FORMULA_BITMAP +0CFAB _MENUCMD_M2I +0CFAB _cmdShadow +0CFC9 _MENUCMD_ITEMS +0D041 _MENUCMD_NUMROWS +0D042 _MENUCMD_CURROW +0D053 _cmdShadCur +0D055 _cmdShadAlph +0D056 _cmdShadIns +0D057 _cmdCursor +0D059 _P_editTop +0D05C _P_EDITCURSOR +0D05F _P_editTail +0D062 _P_editBtm +0D065 _curmatcol +0D066 _curmatrow +0D067 _curlstrow +0D069 _numedTbl +0D069 _curlistel +0D06A _curlstrowh +0D06B _higmatcol +0D06C _higmatrow +0D06D _higlstrow +0D06F _maxdsprow +0D070 _ForCurMat +0D070 _higlstrowh +0D072 _ForDspCol +0D074 _forerrornum +0D075 _P_editSym +0D078 _P_editDat +0D07B _DspMatCol +0D07C _DspMatRow +0D07D _TmpMatCol +0D07E _TmpMatRow +0D07F _numoflist +0D080 _num1stlist +0D081 _NumCurList +0D082 _STATED_CUT_COL +0D083 _listnamebuffer +0D12E _LastName +0D137 _modeRoot +0D139 _modeCount +0D13A _modeItem +0D13B _modePtr +0D13D _winTop +0D13E _winBtm +0D13F _winLeftEdge +0D140 _winLeft +0D142 _winAbove +0D144 _winRow +0D146 _winCol +0D148 _fmtDigits +0D149 _fmtString +0D18A _fmtConv +0D19E _fmtLeft +0D1A0 _fmtIndex +0D1A2 _P_fmtMatSym +0D1A5 _P_fmtMatMem +0D1A8 _EQS +0D1AA _LSTINDEX +0D1AC _LSTSIZE +0D1AE _EQUINDEX +0D1B0 _order +0D1B1 _xnamesav +0D1BA _ynamesav +0D1C3 _CustMType +0D1C3 _MCustM +0D1C4 _CustMLen +0D1C5 _CustMSav +0D1E3 _custmnames +0D279 _VARSAVECNT +0D27A _DELADJAMT +0D27D _TEMPINPUT +0D27E _TSYMPTR1 +0D280 _TSYMPTR2 +0D282 _P_CHKDELPTR3 +0D285 _P_CHKDELPTR4 +0D288 _P_TEMPMEM +0D28B _FPBASE +0D28D _FPS +0D28F _OPBASE +0D291 _OPS +0D293 _PTempCnt +0D295 _CLEANTMP +0D297 _P_PTEMP +0D29A _PTEMP_END +0D29D _FREE_MEM +0D2A0 _newdataptr +0D2A3 _SavBotRow +0D2B8 _curstatplot +0D2B9 _curstatplotprompt +0D2BA _difeqfieldmode +0D2BB _matedoldtype +0D2BC _modesave1 +0D2BD _statansfirst +0D2BF _statanslast +0D2C1 _statanscur +0D2C3 _charmap +0D2CB _altcharmap +0D2D3 _toktmp1 +0D2D4 _toktmp2 +0D2D5 _IOSAVOP1 +0D2DF _DELVAR_SAV_F +0D2E0 _DEL_SAV_OP1 +0D2EB _alt_asm_exec_btm +0D2ED _altlfontptr +0D2F0 _altsfontptr +0D2F3 _altonptr +0D2F6 _altslinkptr +0D2F9 _alt_ret_status +0D2FA _alt_ret_jmp_page +0D2FB _alt_ret_jmp_addr +0D2FD _alt_int_chksum +0D2FE _alt_interrupt_exec +0D3C6 _alt_slink_chksum +0D3C7 _alt_slink_exec +0D48F _alt_on_chksum +0D490 _alt_on_exec +0D558 _alt_off_chksum +0D559 _alt_off_exec +0D621 _altram_end +0D621 _asm_exec_btm +0D623 _ASAP_IND +0D624 _asm_reg_af +0D625 _asm_reg_a +0D626 _asm_reg_l +0D626 _asm_reg_hl +0D627 _asm_reg_h +0D628 _asm_reg_bc +0D628 _asm_reg_c +0D629 _asm_reg_b +0D62A _asm_reg_de +0D62A _asm_reg_e +0D62B _asm_reg_d +0D62C _mPrgmMATH +0D64C _mMath +0D65A _mMath_asap1 +0D65C _mMath_asap2 +0D65E _mMath_asap3 +0D66C _iASAP1 +0D678 _iASAP2 +0D684 _iASAP3 +0D690 _iASAP4 +0D69C _iASAP5 +0D6A8 _iASAP6 +0D6B4 _iASAP7 +0D6C0 _iASAP8 +0D6CC _iASAP9 +0D6D8 _asapnames +0D6D8 _asap_nl1 +0D6E1 _asap_nl2 +0D6EA _asap_nl3 +0D6FC _asapvar +0D706 _tokspell_asap1 +0D706 _tokspelltblptr +0D708 _tokspell_asap2 +0D70A _tokspell_asap3 +0D70E _numtokens +0D70E _numtok_asap1 +0D70F _numtok_asap2 +0D710 _numtok_asap3 +0D712 _eostblptr +0D712 _eostbl_asap1 +0D714 _eostbl_asap2 +0D716 _eostbl_asap3 +0D71A _Amenu_offset +0D722 _reinstall_asap1 +0D722 _reinstall_vec +0D724 _reinstall_asap2 +0D726 _reinstall_asap3 +0D72A _asap1_ram +0D734 _asap2_ram +0D73E _asap3_ram +0D748 _checkStart +0D748 _asm_exec_ram + +[flags] +00 trigflags +2,00 trigdeg,trigflags +00 doneflags +5,00 donePrgm,doneflags +02 plotflags +1,02 plotloc,plotflags +2,02 plotdisp,plotflags +02 grfmodeflags +4,02 grffuncm,grfmodeflags +5,02 grfpolarm,grfmodeflags +6,02 grfparamm,grfmodeflags +7,02 grfrecurm,grfmodeflags +03 graphflags +0,03 graphdraw,graphflags +2,03 graphcursor,graphflags +04 grfdbflags +0,04 grfdot,grfdbflags +1,04 grfsimul,grfdbflags +2,04 grfgrid,grfdbflags +3,04 grfpolar,grfdbflags +4,04 grfnocoord,grfdbflags +5,04 grfnoaxis,grfdbflags +6,04 grflabel,grfdbflags +05 textflags +1,05 textEraseBelow,textflags +2,05 textScrolled,textflags +3,05 textInverse,textflags +06 parsflag +07 parsflag2 +0,06 numop1,parsflag2 +08 apdflags +2,08 apdable,apdflags +3,08 apdlock,apdflags +4,08 apdwarmstart,apdflags +09 onflags +3,09 onRunning,onflags +4,09 onInterrupt,onflags +0A fmtflags +0,0A fmtExponent,fmtflags +1,0A fmtEng,fmtflags +2,0A fmtHex,fmtflags +3,0A fmtOct,fmtflags +4,0A fmtBin,fmtflags +0C curflags +2,0C curAble,curflags +3,0C curOn,curflags +4,0C curLock,curflags +0D appflags +1,0D appTextSave,appflags +2,0D appAutoScroll,appflags +12 indicflags +0,12 indicRun,indicflags +2,12 indicOnly,indicflags +12 shiftflags +3,12 shift2nd,shiftflags +4,12 shiftAlpha,shiftflags +5,12 shiftLwrAlph,shiftflags +6,12 shiftALock,shiftflags +16 asap_cmd_flag +7,16 ex_asap_cmd,asap_cmd_flag +17 NewDispf +6,17 ProgramExecuting,NewDispf +18 new_grf_flgs +6,18 textwrite,new_grf_flgs +1D statflags +6,1D statsvalid,statflags +1F anumeditflgs +2,1F ex_asm_module,anumeditflgs +23 exceptionflg +0,23 alt_font,exceptionflg +1,23 alt_vfont,exceptionflg +2,23 alt_int,exceptionflg +3,23 alt_on,exceptionflg +4,23 alt_link,exceptionflg +5,23 alt_sqrt,exceptionflg +6,23 alt_grphexpr,exceptionflg +7,23 alt_off,exceptionflg +24 exceptionflg2 +0,24 alt_parse,exceptionflg2 +1,24 alt_form,exceptionflg2 +2,24 alt_exec,exceptionflg2 +4,24 alt_home,exceptionflg2 +5,24 alt_cmdtok,exceptionflg2 +6,24 alt_key,exceptionflg2 +7,24 alt_grf,exceptionflg2 +28 asm_flag1 +29 asm_flag2 +2A asm_flag3 +2B asm_flag4 +2C asm_flag5 +2D asm_flag6 +2E asm_flag7 + diff --git a/tool/tilem-src/db/Makefile.in b/tool/tilem-src/db/Makefile.in new file mode 100644 index 0000000..89f981b --- /dev/null +++ b/tool/tilem-src/db/Makefile.in @@ -0,0 +1,59 @@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +bindir = @bindir@ +datadir = @datadir@ +pkgdatadir = @datadir@/tilem2 +mandir = @mandir@ + +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +@SET_MAKE@ + +AR = @AR@ +CC = @CC@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +DEFS = @DEFS@ +RANLIB = @RANLIB@ +SHELL = @SHELL@ + +objects = disasm.o listing.o lstfile.o + +compile = $(CC) -I$(top_builddir) -I$(top_srcdir)/emu -I$(srcdir) $(CFLAGS) $(CPPFLAGS) $(DEFS) + +all: libtilemdb.a + +libtilemdb.a: $(objects) + $(AR) cru libtilemdb.a $(objects) + $(RANLIB) libtilemdb.a + +# Disassembler + +disasm.o: disasm.c tilemdb.h ../emu/tilem.h ../config.h + $(compile) -c $(srcdir)/disasm.c + +# Listing file management + +listing.o: listing.c tilemdb.h ../emu/tilem.h ../config.h + $(compile) -c $(srcdir)/listing.c + +lstfile.o: lstfile.c tilemdb.h ../emu/tilem.h ../config.h + $(compile) -c $(srcdir)/lstfile.c + + +clean: + rm -f *.o + rm -f libtilemdb.a + + +Makefile: Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status + +$(top_builddir)/config.status: $(top_srcdir)/configure + cd $(top_builddir) && $(SHELL) ./config.status --recheck + +.PRECIOUS: Makefile $(top_builddir)/config.status +.PHONY: all clean diff --git a/tool/tilem-src/db/disasm.c b/tool/tilem-src/db/disasm.c new file mode 100644 index 0000000..3326d85 --- /dev/null +++ b/tool/tilem-src/db/disasm.c @@ -0,0 +1,1084 @@ +/* + * libtilemdb - Utilities for debugging Z80 assembly programs + * + * Copyright (C) 2010 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 +#include +#include +#include "tilemdb.h" + +typedef struct _TilemDisasmSymbol { + char* name; + dword value; +} TilemDisasmSymbol; + +typedef struct _TilemDisasmSymTable { + int nsyms; + int nsyms_a; + TilemDisasmSymbol* syms; +} TilemDisasmSymTable; + +struct _TilemDisasm { + TilemDisasmSymTable labels; + TilemDisasmSymTable romcalls; + TilemDisasmSymTable flags; + TilemDisasmSymTable macros; +}; + +TilemDisasm* tilem_disasm_new() +{ + TilemDisasm* dasm = tilem_new0(TilemDisasm, 1); + dasm->labels.syms = NULL; + dasm->romcalls.syms = NULL; + dasm->flags.syms = NULL; + dasm->macros.syms = NULL; + return dasm; +} + +static void tilem_disasm_sym_table_free(TilemDisasmSymTable* stab) +{ + int i; + + for (i = 0; i < stab->nsyms; i++) + tilem_free(stab->syms[i].name); + tilem_free(stab->syms); +} + +void tilem_disasm_free(TilemDisasm* dasm) +{ + if (!dasm) + return; + + tilem_disasm_sym_table_free(&dasm->labels); + tilem_disasm_sym_table_free(&dasm->romcalls); + tilem_disasm_sym_table_free(&dasm->flags); + tilem_disasm_sym_table_free(&dasm->macros); + tilem_free(dasm); +} + +/* Find symbol in the given table, if any */ +static TilemDisasmSymbol* find_symbol(const TilemDisasmSymTable* stab, + dword value) +{ + int start, end, i; + + start = 0; + end = stab->nsyms; + while (start < end) { + i = (start + end) / 2; + if (stab->syms[i].value == value) + return &stab->syms[i]; + else if (stab->syms[i].value <= value) + start = i + 1; + else + end = i; + } + return NULL; +} + +/* Find previous symbol in the given table, if any */ +static TilemDisasmSymbol* find_prev_symbol(const TilemDisasmSymTable* stab, + dword value) +{ + int start, end, i; + + start = 0; + end = stab->nsyms; + while (start < end) { + i = (start + end) / 2; + if (stab->syms[i].value <= value) + start = i + 1; + else + end = i; + } + if (start > 0) + return &stab->syms[start - 1]; + else + return NULL; +} + +/* Find symbol with given name */ +static TilemDisasmSymbol* find_symbol_by_name(const TilemDisasmSymTable* stab, + const char* name) +{ + int i; + for (i = 0; i < stab->nsyms; i++) + if (!strcmp(stab->syms[i].name, name)) + return &stab->syms[i]; + return NULL; +} + +/* Find a given symbol in the table, or create a new one */ +static TilemDisasmSymbol* add_symbol(TilemDisasmSymTable* stab, + dword value) +{ + int start, end, i; + TilemDisasmSymbol* syms; + + start = 0; + end = stab->nsyms; + + while (start < end) { + i = (start + end) / 2; + if (stab->syms[i].value == value) + return &stab->syms[i]; + else if (stab->syms[i].value < value) + start = i + 1; + else + end = i; + } + + /* insert new label into the array */ + if (stab->nsyms < stab->nsyms_a) { + if (start < stab->nsyms) + memmove(&stab->syms[start + 1], &stab->syms[start], + ((stab->nsyms - start) + * sizeof(TilemDisasmSymbol))); + } + else { + stab->nsyms_a = (stab->nsyms + 1) * 2; + syms = tilem_new(TilemDisasmSymbol, stab->nsyms_a); + if (start > 0) + memcpy(syms, stab->syms, + start * sizeof(TilemDisasmSymbol)); + if (start < stab->nsyms) + memcpy(syms + start + 1, stab->syms + start, + ((stab->nsyms - start) + * sizeof(TilemDisasmSymbol))); + tilem_free(stab->syms); + stab->syms = syms; + } + + stab->nsyms++; + + stab->syms[start].value = value; + stab->syms[start].name = NULL; + return &stab->syms[start]; +} + +/* Remove a symbol from the table */ +static void del_symbol(TilemDisasmSymTable* stab, + TilemDisasmSymbol* sym) +{ + int n = sym - stab->syms; + + tilem_free(sym->name); + + if (n < stab->nsyms - 1) { + memmove(sym, sym + 1, + (stab->nsyms - n - 1) * sizeof(TilemDisasmSymbol)); + } + + stab->nsyms--; +} + +static void set_symbol(TilemDisasmSymTable* stab, + const char* name, dword value) +{ + TilemDisasmSymbol* sym; + + if ((sym = find_symbol_by_name(stab, name))) { + if (sym->value == value) + return; + else + del_symbol(stab, sym); + } + + sym = add_symbol(stab, value); + tilem_free(sym->name); + sym->name = tilem_new_atomic(char, strlen(name) + 1); + strcpy(sym->name, name); +} + +static char* skipws(char* p) +{ + while (*p == ' ' || *p == '\t') + p++; + return p; +} + +static char* skipwc(char* p) +{ + while ((unsigned char) *p > ' ') + p++; + return p; +} + +static int parse_sym_value(const char* text, dword* value) +{ + char* p; + dword x; + + if (text[0] >= '0' && text[0] <= '7' && text[1] == ',') { + x = strtol(text + 2, &p, 16); + *value = 0x1000 + (x << 4) + (text[0] - '0'); + } + else { + *value = strtol(text, &p, 16); + } + + return (p != text && *p == 0); +} + +static int parse_sym_line(TilemDisasmSymTable* stab, char* line) +{ + char *w1end, *w2start, *w2end, *name; + dword value; + + if (line[0] == '#' || line[0] == ';') + return 1; + + w1end = skipwc(line); + w2start = skipws(w1end); + w2end = skipwc(w2start); + + if (w1end == line || w2start == w1end || w2end == w2start) + return 1; + if (*w2end) + return 1; + + *w1end = *w2end = 0; + + if (*line >= '0' && *line <= '9') { + name = w2start; + if (!parse_sym_value(line, &value)) + return 1; + } + else { + name = line; + if (!parse_sym_value(w2start, &value)) + return 1; + } + + set_symbol(stab, name, value); + return 0; +} + +int tilem_disasm_read_symbol_file(TilemDisasm* dasm, FILE* symfile) +{ + char buf[1024]; + char* p; + TilemDisasmSymTable* curtbl; + int status = 1; + + curtbl = &dasm->labels; + + while (fgets(buf, sizeof(buf), symfile)) { + p = buf + strlen(buf); + while (p != buf && (p[-1] == '\n' || p[-1] == '\r')) + p--; + *p = 0; + + if (!strcmp(buf, "[labels]")) + curtbl = &dasm->labels; + else if (!strcmp(buf, "[romcalls]")) + curtbl = &dasm->romcalls; + else if (!strcmp(buf, "[flags]")) + curtbl = &dasm->flags; + else if (!strcmp(buf, "[macros]")) + curtbl = &dasm->macros; + else if (!parse_sym_line(curtbl, buf)) + status = 0; + } + + return status; +} + +void tilem_disasm_set_label(TilemDisasm* dasm, const char* name, + dword value) +{ + set_symbol(&dasm->labels, name, value); +} + +int tilem_disasm_get_label(const TilemDisasm* dasm, const char* name, + dword* value) +{ + TilemDisasmSymbol* sym = find_symbol_by_name(&dasm->labels, name); + + if (!sym) + return 0; + else if (value) + *value = sym->value; + return 1; +} + +const char* tilem_disasm_get_label_at_address(const TilemDisasm* dasm, + dword addr) +{ + TilemDisasmSymbol* sym = find_symbol(&dasm->labels, addr); + + if (sym) + return sym->name; + else + return NULL; +} + +typedef struct _TilemDisasmInstruction { + int length; + const char* pattern; +} TilemDisasmInstruction; + +static const TilemDisasmInstruction insts_main[256] = { + {1,"NOP"}, {3,"LD~BC,%w"}, {1,"LD~(BC),A"}, {1,"INC~BC"}, + {1,"INC~B"}, {1,"DEC~B"}, {2,"LD~B,%b"}, {1,"RLCA"}, + {1,"EX~AF,AF'"}, {1,"ADD~HL,BC"}, {1,"LD~A,(BC)"}, {1,"DEC~BC"}, + {1,"INC~C"}, {1,"DEC~C"}, {2,"LD~C,%b"}, {1,"RRCA"}, + {2,"DJNZ~%r"}, {3,"LD~DE,%w"}, {1,"LD~(DE),A"}, {1,"INC~DE"}, + {1,"INC~D"}, {1,"DEC~D"}, {2,"LD~D,%b"}, {1,"RLA"}, + {2,"JR~%r"}, {1,"ADD~HL,DE"}, {1,"LD~A,(DE)"}, {1,"DEC~DE"}, + {1,"INC~E"}, {1,"DEC~E"}, {2,"LD~E,%b"}, {1,"RRA"}, + {2,"JR~NZ,%r"}, {3,"LD~HL,%w"}, {3,"LD~(%a),HL"}, {1,"INC~HL"}, + {1,"INC~H"}, {1,"DEC~H"}, {2,"LD~H,%b"}, {1,"DAA"}, + {2,"JR~Z,%r"}, {1,"ADD~HL,HL"}, {3,"LD~HL,(%a)"}, {1,"DEC~HL"}, + {1,"INC~L"}, {1,"DEC~L"}, {2,"LD~L,%b"}, {1,"CPL"}, + {2,"JR~NC,%r"}, {3,"LD~SP,%w"}, {3,"LD~(%a),A"}, {1,"INC~SP"}, + {1,"INC~(HL)"}, {1,"DEC~(HL)"}, {2,"LD~(HL),%b"}, {1,"SCF"}, + {2,"JR~C,%r"}, {1,"ADD~HL,SP"}, {3,"LD~A,(%a)"}, {1,"DEC~SP"}, + {1,"INC~A"}, {1,"DEC~A"}, {2,"LD~A,%b"}, {1,"CCF"}, + + {1,"LD~B,B"}, {1,"LD~B,C"}, {1,"LD~B,D"}, {1,"LD~B,E"}, + {1,"LD~B,H"}, {1,"LD~B,L"}, {1,"LD~B,(HL)"}, {1,"LD~B,A"}, + {1,"LD~C,B"}, {1,"LD~C,C"}, {1,"LD~C,D"}, {1,"LD~C,E"}, + {1,"LD~C,H"}, {1,"LD~C,L"}, {1,"LD~C,(HL)"}, {1,"LD~C,A"}, + {1,"LD~D,B"}, {1,"LD~D,C"}, {1,"LD~D,D"}, {1,"LD~D,E"}, + {1,"LD~D,H"}, {1,"LD~D,L"}, {1,"LD~D,(HL)"}, {1,"LD~D,A"}, + {1,"LD~E,B"}, {1,"LD~E,C"}, {1,"LD~E,D"}, {1,"LD~E,E"}, + {1,"LD~E,H"}, {1,"LD~E,L"}, {1,"LD~E,(HL)"}, {1,"LD~E,A"}, + {1,"LD~H,B"}, {1,"LD~H,C"}, {1,"LD~H,D"}, {1,"LD~H,E"}, + {1,"LD~H,H"}, {1,"LD~H,L"}, {1,"LD~H,(HL)"}, {1,"LD~H,A"}, + {1,"LD~L,B"}, {1,"LD~L,C"}, {1,"LD~L,D"}, {1,"LD~L,E"}, + {1,"LD~L,H"}, {1,"LD~L,L"}, {1,"LD~L,(HL)"}, {1,"LD~L,A"}, + {1,"LD~(HL),B"}, {1,"LD~(HL),C"}, {1,"LD~(HL),D"}, {1,"LD~(HL),E"}, + {1,"LD~(HL),H"}, {1,"LD~(HL),L"}, {1,"HALT"}, {1,"LD~(HL),A"}, + {1,"LD~A,B"}, {1,"LD~A,C"}, {1,"LD~A,D"}, {1,"LD~A,E"}, + {1,"LD~A,H"}, {1,"LD~A,L"}, {1,"LD~A,(HL)"}, {1,"LD~A,A"}, + + {1,"ADD~A,B"}, {1,"ADD~A,C"}, {1,"ADD~A,D"}, {1,"ADD~A,E"}, + {1,"ADD~A,H"}, {1,"ADD~A,L"}, {1,"ADD~A,(HL)"}, {1,"ADD~A,A"}, + {1,"ADC~A,B"}, {1,"ADC~A,C"}, {1,"ADC~A,D"}, {1,"ADC~A,E"}, + {1,"ADC~A,H"}, {1,"ADC~A,L"}, {1,"ADC~A,(HL)"}, {1,"ADC~A,A"}, + {1,"SUB~B"}, {1,"SUB~C"}, {1,"SUB~D"}, {1,"SUB~E"}, + {1,"SUB~H"}, {1,"SUB~L"}, {1,"SUB~(HL)"}, {1,"SUB~A"}, + {1,"SBC~A,B"}, {1,"SBC~A,C"}, {1,"SBC~A,D"}, {1,"SBC~A,E"}, + {1,"SBC~A,H"}, {1,"SBC~A,L"}, {1,"SBC~A,(HL)"}, {1,"SBC~A,A"}, + {1,"AND~B"}, {1,"AND~C"}, {1,"AND~D"}, {1,"AND~E"}, + {1,"AND~H"}, {1,"AND~L"}, {1,"AND~(HL)"}, {1,"AND~A"}, + {1,"XOR~B"}, {1,"XOR~C"}, {1,"XOR~D"}, {1,"XOR~E"}, + {1,"XOR~H"}, {1,"XOR~L"}, {1,"XOR~(HL)"}, {1,"XOR~A"}, + {1,"OR~B"}, {1,"OR~C"}, {1,"OR~D"}, {1,"OR~E"}, + {1,"OR~H"}, {1,"OR~L"}, {1,"OR~(HL)"}, {1,"OR~A"}, + {1,"CP~B"}, {1,"CP~C"}, {1,"CP~D"}, {1,"CP~E"}, + {1,"CP~H"}, {1,"CP~L"}, {1,"CP~(HL)"}, {1,"CP~A"}, + + {1,"RET~NZ"}, {1,"POP~BC"}, {3,"JP~NZ,%j"}, {3,"JP~%j"}, + {3,"CALL~NZ,%j"}, {1,"PUSH~BC"}, {2,"ADD~A,%b"}, {1,"RST~%z"}, + {1,"RET~Z"}, {1,"RET"}, {3,"JP~Z,%j"}, {1,0}, + {3,"CALL~Z,%j"}, {3,"CALL~%j"}, {2,"ADC~A,%b"}, {1,"RST~%z"}, + {1,"RET~NC"}, {1,"POP~DE"}, {3,"JP~NC,%j"}, {2,"OUT~(%b),A"}, + {3,"CALL~NC,%j"}, {1,"PUSH~DE"}, {2,"SUB~%b"}, {1,"RST~%z"}, + {1,"RET~C"}, {1,"EXX"}, {3,"JP~C,%j"}, {2,"IN~A,(%b)"}, + {3,"CALL~C,%j"}, {1,0}, {2,"SBC~A,%b"}, {1,"RST~%z"}, + {1,"RET~PO"}, {1,"POP~HL"}, {3,"JP~PO,%j"}, {1,"EX~(SP),HL"}, + {3,"CALL~PO,%j"}, {1,"PUSH~HL"}, {2,"AND~%b"}, {1,"RST~%z"}, + {1,"RET~PE"}, {1,"JP~(HL)"}, {3,"JP~PE,%j"}, {1,"EX~DE,HL"}, + {3,"CALL~PE,%j"}, {1,0}, {2,"XOR~%b"}, {1,"RST~%z"}, + {1,"RET~P"}, {1,"POP~AF"}, {3,"JP~P,%j"}, {1,"DI"}, + {3,"CALL~P,%j"}, {1,"PUSH~AF"}, {2,"OR~%b"}, {1,"RST~%z"}, + {1,"RET~M"}, {1,"LD~SP,HL"}, {3,"JP~M,%j"}, {1,"EI"}, + {3,"CALL~M,%j"}, {1,0}, {2,"CP~%b"}, {1,"RST~%z"}}; + +static const TilemDisasmInstruction insts_ddfd[256] = { + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {2,"ADD~%i,BC"}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {2,"ADD~%i,DE"}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {4,"LD~%i,%w"}, {4,"LD~(%a),%i"}, {2,"INC~%i"}, + {2,"INC~%iH"}, {2,"DEC~%iH"}, {3,"LD~%iH,%b"}, {1,0}, + {1,0}, {2,"ADD~%i,%i"}, {4,"LD~%i,(%a)"}, {2,"DEC~%i"}, + {2,"INC~%iL"}, {2,"DEC~%iL"}, {3,"LD~%iL,%b"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {3,"INC~(%i%s)"}, {3,"DEC~(%i%s)"}, {4,"LD~(%i%s),%b"}, {1,0}, + {1,0}, {2,"ADD~%i,SP"}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"LD~B,%iH"}, {2,"LD~B,%iL"}, {3,"LD~B,(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"LD~C,%iH"}, {2,"LD~C,%iL"}, {3,"LD~C,(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"LD~D,%iH"}, {2,"LD~D,%iL"}, {3,"LD~D,(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"LD~E,%iH"}, {2,"LD~E,%iL"}, {3,"LD~E,(%i%s)"}, {1,0}, + {2,"LD~%iH,B"}, {2,"LD~%iH,C"}, {2,"LD~%iH,D"}, {2,"LD~%iH,E"}, + {2,"LD~%iH,%iH"}, {2,"LD~%iH,%iL"}, {3,"LD~H,(%i%s)"}, {2,"LD~%iH,A"}, + {2,"LD~%iL,B"}, {2,"LD~%iL,C"}, {2,"LD~%iL,D"}, {2,"LD~%iL,E"}, + {2,"LD~%iL,%iH"}, {2,"LD~%iL,%iL"}, {3,"LD~L,(%i%s)"}, {2,"LD~%iL,A"}, + {3,"LD~(%i%s),B"}, {3,"LD~(%i%s),C"}, {3,"LD~(%i%s),D"}, {3,"LD~(%i%s),E"}, + {3,"LD~(%i%s),H"}, {3,"LD~(%i%s),L"}, {1,0}, {3,"LD~(%i%s),A"}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"LD~A,%iH"}, {2,"LD~A,%iL"}, {3,"LD~A,(%i%s)"}, {1,0}, + + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"ADD~A,%iH"}, {2,"ADD~A,%iL"}, {3,"ADD~A,(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"ADC~A,%iH"}, {2,"ADC~A,%iL"}, {3,"ADC~A,(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"SUB~%iH"}, {2,"SUB~%iL"}, {3,"SUB~(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"SBC~A,%iH"}, {2,"SBC~A,%iL"}, {3,"SBC~A,(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"AND~%iH"}, {2,"AND~%iL"}, {3,"AND~(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"XOR~%iH"}, {2,"XOR~%iL"}, {3,"XOR~(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"OR~%iH"}, {2,"OR~%iL"}, {3,"OR~(%i%s)"}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {2,"CP~%iH"}, {2,"CP~%iL"}, {3,"CP~(%i%s)"}, {1,0}, + + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {2,"POP~%i"}, {1,0}, {2,"EX~(SP),%i"}, + {1,0}, {2,"PUSH~%i"}, {1,0}, {1,0}, + {1,0}, {2,"JP~(%i)"}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}, + {1,0}, {2,"LD~SP,%i"}, {1,0}, {1,0}, + {1,0}, {1,0}, {1,0}, {1,0}}; + +static const TilemDisasmInstruction insts_cb[256] = { + {2,"RLC~B"}, {2,"RLC~C"}, {2,"RLC~D"}, {2,"RLC~E"}, + {2,"RLC~H"}, {2,"RLC~L"}, {2,"RLC~(HL)"}, {2,"RLC~A"}, + {2,"RRC~B"}, {2,"RRC~C"}, {2,"RRC~D"}, {2,"RRC~E"}, + {2,"RRC~H"}, {2,"RRC~L"}, {2,"RRC~(HL)"}, {2,"RRC~A"}, + {2,"RL~B"}, {2,"RL~C"}, {2,"RL~D"}, {2,"RL~E"}, + {2,"RL~H"}, {2,"RL~L"}, {2,"RL~(HL)"}, {2,"RL~A"}, + {2,"RR~B"}, {2,"RR~C"}, {2,"RR~D"}, {2,"RR~E"}, + {2,"RR~H"}, {2,"RR~L"}, {2,"RR~(HL)"}, {2,"RR~A"}, + {2,"SLA~B"}, {2,"SLA~C"}, {2,"SLA~D"}, {2,"SLA~E"}, + {2,"SLA~H"}, {2,"SLA~L"}, {2,"SLA~(HL)"}, {2,"SLA~A"}, + {2,"SRA~B"}, {2,"SRA~C"}, {2,"SRA~D"}, {2,"SRA~E"}, + {2,"SRA~H"}, {2,"SRA~L"}, {2,"SRA~(HL)"}, {2,"SRA~A"}, + {2,"SLIA~B"}, {2,"SLIA~C"}, {2,"SLIA~D"}, {2,"SLIA~E"}, + {2,"SLIA~H"}, {2,"SLIA~L"}, {2,"SLIA~(HL)"}, {2,"SLIA~A"}, + {2,"SRL~B"}, {2,"SRL~C"}, {2,"SRL~D"}, {2,"SRL~E"}, + {2,"SRL~H"}, {2,"SRL~L"}, {2,"SRL~(HL)"}, {2,"SRL~A"}, + + {2,"BIT~0,B"}, {2,"BIT~0,C"}, {2,"BIT~0,D"}, {2,"BIT~0,E"}, + {2,"BIT~0,H"}, {2,"BIT~0,L"}, {2,"BIT~0,(HL)"}, {2,"BIT~0,A"}, + {2,"BIT~1,B"}, {2,"BIT~1,C"}, {2,"BIT~1,D"}, {2,"BIT~1,E"}, + {2,"BIT~1,H"}, {2,"BIT~1,L"}, {2,"BIT~1,(HL)"}, {2,"BIT~1,A"}, + {2,"BIT~2,B"}, {2,"BIT~2,C"}, {2,"BIT~2,D"}, {2,"BIT~2,E"}, + {2,"BIT~2,H"}, {2,"BIT~2,L"}, {2,"BIT~2,(HL)"}, {2,"BIT~2,A"}, + {2,"BIT~3,B"}, {2,"BIT~3,C"}, {2,"BIT~3,D"}, {2,"BIT~3,E"}, + {2,"BIT~3,H"}, {2,"BIT~3,L"}, {2,"BIT~3,(HL)"}, {2,"BIT~3,A"}, + {2,"BIT~4,B"}, {2,"BIT~4,C"}, {2,"BIT~4,D"}, {2,"BIT~4,E"}, + {2,"BIT~4,H"}, {2,"BIT~4,L"}, {2,"BIT~4,(HL)"}, {2,"BIT~4,A"}, + {2,"BIT~5,B"}, {2,"BIT~5,C"}, {2,"BIT~5,D"}, {2,"BIT~5,E"}, + {2,"BIT~5,H"}, {2,"BIT~5,L"}, {2,"BIT~5,(HL)"}, {2,"BIT~5,A"}, + {2,"BIT~6,B"}, {2,"BIT~6,C"}, {2,"BIT~6,D"}, {2,"BIT~6,E"}, + {2,"BIT~6,H"}, {2,"BIT~6,L"}, {2,"BIT~6,(HL)"}, {2,"BIT~6,A"}, + {2,"BIT~7,B"}, {2,"BIT~7,C"}, {2,"BIT~7,D"}, {2,"BIT~7,E"}, + {2,"BIT~7,H"}, {2,"BIT~7,L"}, {2,"BIT~7,(HL)"}, {2,"BIT~7,A"}, + + {2,"RES~0,B"}, {2,"RES~0,C"}, {2,"RES~0,D"}, {2,"RES~0,E"}, + {2,"RES~0,H"}, {2,"RES~0,L"}, {2,"RES~0,(HL)"}, {2,"RES~0,A"}, + {2,"RES~1,B"}, {2,"RES~1,C"}, {2,"RES~1,D"}, {2,"RES~1,E"}, + {2,"RES~1,H"}, {2,"RES~1,L"}, {2,"RES~1,(HL)"}, {2,"RES~1,A"}, + {2,"RES~2,B"}, {2,"RES~2,C"}, {2,"RES~2,D"}, {2,"RES~2,E"}, + {2,"RES~2,H"}, {2,"RES~2,L"}, {2,"RES~2,(HL)"}, {2,"RES~2,A"}, + {2,"RES~3,B"}, {2,"RES~3,C"}, {2,"RES~3,D"}, {2,"RES~3,E"}, + {2,"RES~3,H"}, {2,"RES~3,L"}, {2,"RES~3,(HL)"}, {2,"RES~3,A"}, + {2,"RES~4,B"}, {2,"RES~4,C"}, {2,"RES~4,D"}, {2,"RES~4,E"}, + {2,"RES~4,H"}, {2,"RES~4,L"}, {2,"RES~4,(HL)"}, {2,"RES~4,A"}, + {2,"RES~5,B"}, {2,"RES~5,C"}, {2,"RES~5,D"}, {2,"RES~5,E"}, + {2,"RES~5,H"}, {2,"RES~5,L"}, {2,"RES~5,(HL)"}, {2,"RES~5,A"}, + {2,"RES~6,B"}, {2,"RES~6,C"}, {2,"RES~6,D"}, {2,"RES~6,E"}, + {2,"RES~6,H"}, {2,"RES~6,L"}, {2,"RES~6,(HL)"}, {2,"RES~6,A"}, + {2,"RES~7,B"}, {2,"RES~7,C"}, {2,"RES~7,D"}, {2,"RES~7,E"}, + {2,"RES~7,H"}, {2,"RES~7,L"}, {2,"RES~7,(HL)"}, {2,"RES~7,A"}, + + {2,"SET~0,B"}, {2,"SET~0,C"}, {2,"SET~0,D"}, {2,"SET~0,E"}, + {2,"SET~0,H"}, {2,"SET~0,L"}, {2,"SET~0,(HL)"}, {2,"SET~0,A"}, + {2,"SET~1,B"}, {2,"SET~1,C"}, {2,"SET~1,D"}, {2,"SET~1,E"}, + {2,"SET~1,H"}, {2,"SET~1,L"}, {2,"SET~1,(HL)"}, {2,"SET~1,A"}, + {2,"SET~2,B"}, {2,"SET~2,C"}, {2,"SET~2,D"}, {2,"SET~2,E"}, + {2,"SET~2,H"}, {2,"SET~2,L"}, {2,"SET~2,(HL)"}, {2,"SET~2,A"}, + {2,"SET~3,B"}, {2,"SET~3,C"}, {2,"SET~3,D"}, {2,"SET~3,E"}, + {2,"SET~3,H"}, {2,"SET~3,L"}, {2,"SET~3,(HL)"}, {2,"SET~3,A"}, + {2,"SET~4,B"}, {2,"SET~4,C"}, {2,"SET~4,D"}, {2,"SET~4,E"}, + {2,"SET~4,H"}, {2,"SET~4,L"}, {2,"SET~4,(HL)"}, {2,"SET~4,A"}, + {2,"SET~5,B"}, {2,"SET~5,C"}, {2,"SET~5,D"}, {2,"SET~5,E"}, + {2,"SET~5,H"}, {2,"SET~5,L"}, {2,"SET~5,(HL)"}, {2,"SET~5,A"}, + {2,"SET~6,B"}, {2,"SET~6,C"}, {2,"SET~6,D"}, {2,"SET~6,E"}, + {2,"SET~6,H"}, {2,"SET~6,L"}, {2,"SET~6,(HL)"}, {2,"SET~6,A"}, + {2,"SET~7,B"}, {2,"SET~7,C"}, {2,"SET~7,D"}, {2,"SET~7,E"}, + {2,"SET~7,H"}, {2,"SET~7,L"}, {2,"SET~7,(HL)"}, {2,"SET~7,A"}}; + +static const TilemDisasmInstruction insts_ddfdcb[256] = { + {4,"RLC~B,(%i%s)"}, {4,"RLC~C,(%i%s)"}, {4,"RLC~D,(%i%s)"}, {4,"RLC~E,(%i%s)"}, + {4,"RLC~H,(%i%s)"}, {4,"RLC~L,(%i%s)"}, {4,"RLC~(%i%s)"}, {4,"RLC~A,(%i%s)"}, + {4,"RRC~B,(%i%s)"}, {4,"RRC~C,(%i%s)"}, {4,"RRC~D,(%i%s)"}, {4,"RRC~E,(%i%s)"}, + {4,"RRC~H,(%i%s)"}, {4,"RRC~L,(%i%s)"}, {4,"RRC~(%i%s)"}, {4,"RRC~A,(%i%s)"}, + {4,"RL~B,(%i%s)"}, {4,"RL~C,(%i%s)"}, {4,"RL~D,(%i%s)"}, {4,"RL~E,(%i%s)"}, + {4,"RL~H,(%i%s)"}, {4,"RL~L,(%i%s)"}, {4,"RL~(%i%s)"}, {4,"RL~A,(%i%s)"}, + {4,"RR~B,(%i%s)"}, {4,"RR~C,(%i%s)"}, {4,"RR~D,(%i%s)"}, {4,"RR~E,(%i%s)"}, + {4,"RR~H,(%i%s)"}, {4,"RR~L,(%i%s)"}, {4,"RR~(%i%s)"}, {4,"RR~A,(%i%s)"}, + {4,"SLA~B,(%i%s)"}, {4,"SLA~C,(%i%s)"}, {4,"SLA~D,(%i%s)"}, {4,"SLA~E,(%i%s)"}, + {4,"SLA~H,(%i%s)"}, {4,"SLA~L,(%i%s)"}, {4,"SLA~(%i%s)"}, {4,"SLA~A,(%i%s)"}, + {4,"SRA~B,(%i%s)"}, {4,"SRA~C,(%i%s)"}, {4,"SRA~D,(%i%s)"}, {4,"SRA~E,(%i%s)"}, + {4,"SRA~H,(%i%s)"}, {4,"SRA~L,(%i%s)"}, {4,"SRA~(%i%s)"}, {4,"SRA~A,(%i%s)"}, + {4,"SLIA~B,(%i%s)"}, {4,"SLIA~C,(%i%s)"}, {4,"SLIA~D,(%i%s)"}, {4,"SLIA~E,(%i%s)"}, + {4,"SLIA~H,(%i%s)"}, {4,"SLIA~L,(%i%s)"}, {4,"SLIA~(%i%s)"}, {4,"SLIA~A,(%i%s)"}, + {4,"SRL~B,(%i%s)"}, {4,"SRL~C,(%i%s)"}, {4,"SRL~D,(%i%s)"}, {4,"SRL~E,(%i%s)"}, + {4,"SRL~H,(%i%s)"}, {4,"SRL~L,(%i%s)"}, {4,"SRL~(%i%s)"}, {4,"SRL~A,(%i%s)"}, + + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f*"}, + {4,"BIT~%f*"}, {4,"BIT~%f*"}, {4,"BIT~%f"}, {4,"BIT~%f*"}, + + {4,"RES~0,B,(%i%s)"}, {4,"RES~0,C,(%i%s)"}, {4,"RES~0,D,(%i%s)"}, {4,"RES~0,E,(%i%s)"}, + {4,"RES~0,H,(%i%s)"}, {4,"RES~0,L,(%i%s)"}, {4,"RES~%f"}, {4,"RES~0,A,(%i%s)"}, + {4,"RES~1,B,(%i%s)"}, {4,"RES~1,C,(%i%s)"}, {4,"RES~1,D,(%i%s)"}, {4,"RES~1,E,(%i%s)"}, + {4,"RES~1,H,(%i%s)"}, {4,"RES~1,L,(%i%s)"}, {4,"RES~%f"}, {4,"RES~1,A,(%i%s)"}, + {4,"RES~2,B,(%i%s)"}, {4,"RES~2,C,(%i%s)"}, {4,"RES~2,D,(%i%s)"}, {4,"RES~2,E,(%i%s)"}, + {4,"RES~2,H,(%i%s)"}, {4,"RES~2,L,(%i%s)"}, {4,"RES~%f"}, {4,"RES~2,A,(%i%s)"}, + {4,"RES~3,B,(%i%s)"}, {4,"RES~3,C,(%i%s)"}, {4,"RES~3,D,(%i%s)"}, {4,"RES~3,E,(%i%s)"}, + {4,"RES~3,H,(%i%s)"}, {4,"RES~3,L,(%i%s)"}, {4,"RES~%f"}, {4,"RES~3,A,(%i%s)"}, + {4,"RES~4,B,(%i%s)"}, {4,"RES~4,C,(%i%s)"}, {4,"RES~4,D,(%i%s)"}, {4,"RES~4,E,(%i%s)"}, + {4,"RES~4,H,(%i%s)"}, {4,"RES~4,L,(%i%s)"}, {4,"RES~%f"}, {4,"RES~4,A,(%i%s)"}, + {4,"RES~5,B,(%i%s)"}, {4,"RES~5,C,(%i%s)"}, {4,"RES~5,D,(%i%s)"}, {4,"RES~5,E,(%i%s)"}, + {4,"RES~5,H,(%i%s)"}, {4,"RES~5,L,(%i%s)"}, {4,"RES~%f"}, {4,"RES~5,A,(%i%s)"}, + {4,"RES~6,B,(%i%s)"}, {4,"RES~6,C,(%i%s)"}, {4,"RES~6,D,(%i%s)"}, {4,"RES~6,E,(%i%s)"}, + {4,"RES~6,H,(%i%s)"}, {4,"RES~6,L,(%i%s)"}, {4,"RES~%f"}, {4,"RES~6,A,(%i%s)"}, + {4,"RES~7,B,(%i%s)"}, {4,"RES~7,C,(%i%s)"}, {4,"RES~7,D,(%i%s)"}, {4,"RES~7,E,(%i%s)"}, + {4,"RES~7,H,(%i%s)"}, {4,"RES~7,L,(%i%s)"}, {4,"RES~%f"}, {4,"RES~7,A,(%i%s)"}, + + {4,"SET~0,B,(%i%s)"}, {4,"SET~0,C,(%i%s)"}, {4,"SET~0,D,(%i%s)"}, {4,"SET~0,E,(%i%s)"}, + {4,"SET~0,H,(%i%s)"}, {4,"SET~0,L,(%i%s)"}, {4,"SET~%f"}, {4,"SET~0,A,(%i%s)"}, + {4,"SET~1,B,(%i%s)"}, {4,"SET~1,C,(%i%s)"}, {4,"SET~1,D,(%i%s)"}, {4,"SET~1,E,(%i%s)"}, + {4,"SET~1,H,(%i%s)"}, {4,"SET~1,L,(%i%s)"}, {4,"SET~%f"}, {4,"SET~1,A,(%i%s)"}, + {4,"SET~2,B,(%i%s)"}, {4,"SET~2,C,(%i%s)"}, {4,"SET~2,D,(%i%s)"}, {4,"SET~2,E,(%i%s)"}, + {4,"SET~2,H,(%i%s)"}, {4,"SET~2,L,(%i%s)"}, {4,"SET~%f"}, {4,"SET~2,A,(%i%s)"}, + {4,"SET~3,B,(%i%s)"}, {4,"SET~3,C,(%i%s)"}, {4,"SET~3,D,(%i%s)"}, {4,"SET~3,E,(%i%s)"}, + {4,"SET~3,H,(%i%s)"}, {4,"SET~3,L,(%i%s)"}, {4,"SET~%f"}, {4,"SET~3,A,(%i%s)"}, + {4,"SET~4,B,(%i%s)"}, {4,"SET~4,C,(%i%s)"}, {4,"SET~4,D,(%i%s)"}, {4,"SET~4,E,(%i%s)"}, + {4,"SET~4,H,(%i%s)"}, {4,"SET~4,L,(%i%s)"}, {4,"SET~%f"}, {4,"SET~4,A,(%i%s)"}, + {4,"SET~5,B,(%i%s)"}, {4,"SET~5,C,(%i%s)"}, {4,"SET~5,D,(%i%s)"}, {4,"SET~5,E,(%i%s)"}, + {4,"SET~5,H,(%i%s)"}, {4,"SET~5,L,(%i%s)"}, {4,"SET~%f"}, {4,"SET~5,A,(%i%s)"}, + {4,"SET~6,B,(%i%s)"}, {4,"SET~6,C,(%i%s)"}, {4,"SET~6,D,(%i%s)"}, {4,"SET~6,E,(%i%s)"}, + {4,"SET~6,H,(%i%s)"}, {4,"SET~6,L,(%i%s)"}, {4,"SET~%f"}, {4,"SET~6,A,(%i%s)"}, + {4,"SET~7,B,(%i%s)"}, {4,"SET~7,C,(%i%s)"}, {4,"SET~7,D,(%i%s)"}, {4,"SET~7,E,(%i%s)"}, + {4,"SET~7,H,(%i%s)"}, {4,"SET~7,L,(%i%s)"}, {4,"SET~%f"}, {4,"SET~7,A,(%i%s)"}}; + +static const TilemDisasmInstruction insts_ed[256] = { + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + + {2,"IN~B,(C)"}, {2,"OUT~(C),B"}, {2,"SBC~HL,BC"}, {4,"LD~(%a),BC"}, + {2,"NEG"}, {2,"RETN"}, {2,"IM~0"}, {2,"LD~I,A"}, + {2,"IN~C,(C)"}, {2,"OUT~(C),C"}, {2,"ADC~HL,BC"}, {4,"LD~BC,(%a)"}, + {2,"NEG*"}, {2,"RETI"}, {2,"IM~0*"}, {2,"LD~R,A"}, + {2,"IN~D,(C)"}, {2,"OUT~(C),D"}, {2,"SBC~HL,DE"}, {4,"LD~(%a),DE"}, + {2,"NEG*"}, {2,"RETN*"}, {2,"IM~1"}, {2,"LD~A,I"}, + {2,"IN~E,(C)"}, {2,"OUT~(C),E"}, {2,"ADC~HL,DE"}, {4,"LD~DE,(%a)"}, + {2,"NEG*"}, {2,"RETN*"}, {2,"IM~2"}, {2,"LD~A,R"}, + {2,"IN~H,(C)"}, {2,"OUT~(C),H"}, {2,"SBC~HL,HL"}, {4,"LD~(%a),HL*"}, + {2,"NEG*"}, {2,"RETN*"}, {2,"IM~0*"}, {2,"RRD"}, + {2,"IN~L,(C)"}, {2,"OUT~(C),L"}, {2,"ADC~HL,HL"}, {4,"LD~HL,(%a)*"}, + {2,"NEG*"}, {2,"RETN*"}, {2,"IM~0*"}, {2,"RLD"}, + {2,"IN~(C)"}, {2,"OUT~(C),0"}, {2,"SBC~HL,SP"}, {4,"LD~(%a),SP"}, + {2,"NEG*"}, {2,"RETN*"}, {2,"IM~1*"}, {2,0}, + {2,"IN~A,(C)"}, {2,"OUT~(C),A"}, {2,"ADC~HL,SP"}, {4,"LD~SP,(%a)"}, + {2,"NEG*"}, {2,"RETN*"}, {2,"IM~2*"}, {2,0}, + + {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,"LDI"}, {2,"CPI"}, {2,"INI"}, {2,"OUTI"}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,"LDD"}, {2,"CPD"}, {2,"IND"}, {2,"OUTD"}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,"LDIR"}, {2,"CPIR"}, {2,"INIR"}, {2,"OTIR"}, + {2,0}, {2,0}, {2,0}, {2,0}, + {2,"LDDR"}, {2,"CPDR"}, {2,"INDR"}, {2,"OTDR"}, + {2,0}, {2,0}, {2,0}, {2,0}, + + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, + {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}, {2,0}}; + +/* Count number of bytes of arguments for a given instruction/macro + pattern */ +static int pattern_arg_size(const char* pattern) +{ + char* p; + int count = 0, offs; + + while (*pattern) { + if (*pattern != '%') + pattern++; + else { + pattern++; + + if (*pattern >= '0' && *pattern <= '9') { + offs = strtol(pattern, &p, 10); + pattern = p; + } + else { + offs = count; + } + + switch (*pattern) { + case 0: + pattern--; + break; + + case 'b': + case 'C': + case 'r': + case 's': + offs++; + break; + + case 'a': + case 'c': + case 'f': + case 'j': + case 'w': + offs += 2; + break; + } + + pattern++; + if (offs > count) + count = offs; + } + } + + return count; +} + +static void get_instruction_info(const TilemDisasm* dasm, + const byte* instr, + int* length, int* argbase, + const char** pattern) +{ + const TilemDisasmSymbol* sym; + const TilemDisasmInstruction* ii; + dword mvalue; + int i; + + mvalue = 0; + for (i = 0; i < 4; i++) { + mvalue = (mvalue << 8) | instr[i]; + if ((sym = find_symbol(&dasm->macros, mvalue))) { + *pattern = sym->name; + *length = i + 1 + pattern_arg_size(sym->name); + *argbase = i + 1; + return; + } + } + + if (instr[0] == 0xed) { + ii = &insts_ed[instr[1]]; + *argbase = 2; + } + else if (instr[0] == 0xdd || instr[0] == 0xfd) { + if (instr[1] == 0xcb) { + ii = &insts_ddfdcb[instr[3]]; + } + else { + ii = &insts_ddfd[instr[1]]; + } + *argbase = 2; + } + else if (instr[0] == 0xcb) { + ii = &insts_cb[instr[1]]; + *argbase = 2; + } + else { + ii = &insts_main[instr[0]]; + *argbase = 1; + } + + *length = ii->length; + + if (ii->pattern) { + *pattern = ii->pattern; + } + else { + *argbase = 0; + if (ii->length == 1) + *pattern = "DB~%b"; + else + *pattern = "DB~%b,%b"; + } +} + +static void TILEM_ATTR_PRINTF(3, 4) +printv(char** buf, int* bsize, const char* fmt, ...) +{ + va_list ap; + int n; + + if (*bsize == 0) + return; + + va_start(ap, fmt); + n = vsnprintf(*buf, *bsize, fmt, ap); + va_end(ap); + + if (n >= *bsize) { + *buf += *bsize - 1; + **buf = 0; + *bsize = 0; + } + else { + *buf += n; + **buf = 0; + *bsize -= n; + } +} + +static void print_byte(char** buf, int* bsize, unsigned int b) +{ + printv(buf, bsize, "$%02X", b); +} + +static void print_word(const TilemDisasm* dasm, char** buf, int* bsize, + dword w, int autonum, int autodiff) +{ + TilemDisasmSymbol* sym; + + if (autonum && w < 0x100) { + printv(buf, bsize, "$%04X", w); + return; + } + + sym = find_prev_symbol(&dasm->labels, w); + + if (sym && !strcmp(sym->name, "flags")) { + w -= sym->value; + sym = find_symbol(&dasm->flags, w); + if (sym) { + printv(buf, bsize, "flags + %s", sym->name); + } + else { + printv(buf, bsize, "flags + $%02X", w); + } + } + else if (sym && w == sym->value) { + printv(buf, bsize, "%s", sym->name); + } + else if (sym && autodiff && w > 0x8000 && w - sym->value < 64) { + printv(buf, bsize, "%s + %d", sym->name, w - sym->value); + } + else { + printv(buf, bsize, "$%04X", w); + } +} + +static void print_romcall(const TilemDisasm* dasm, char** buf, int* bsize, + dword w) +{ + TilemDisasmSymbol* sym; + + sym = find_symbol(&dasm->romcalls, w); + if (sym) { + printv(buf, bsize, "%s", sym->name); + } + else { + printv(buf, bsize, "$%04X", w); + } +} + +static void print_flag(const TilemDisasm* dasm, char** buf, int* bsize, + unsigned int bit, unsigned int offset, + unsigned int prefix) +{ + TilemDisasmSymbol* sym; + int i; + + if (prefix == 0xfd) { + sym = find_symbol(&dasm->flags, 0x1000 + (offset << 4) + bit); + if (sym) { + for (i = 0; sym->name[i]; i++) { + printv(buf, bsize, "%c", sym->name[i]); + if (sym->name[i] == ',') + printv(buf, bsize, " (IY + "); + } + printv(buf, bsize, ")"); + return; + } + + sym = find_symbol(&dasm->flags, offset); + if (sym) { + printv(buf, bsize, "%d, (IY + %s)", bit, sym->name); + return; + } + } + + printv(buf, bsize, "%d, (%s", bit, (prefix == 0xfd ? "IY" : "IX")); + + if (offset & 0x80) { + printv(buf, bsize, " - $%02X", 0x100 - offset); + } + else if (offset) { + printv(buf, bsize, " + $%02X", offset); + } + + printv(buf, bsize, ")"); +} + +static void disassemble_pattern(const TilemDisasm* dasm, const char* pattern, + const byte* ibuf, dword pc, int argbase, + char** buf, int* bsize) +{ + int argidx, offs; + char* p; + dword w; + TilemDisasmSymbol* sym; + + argidx = argbase; + + while (*bsize && *pattern) { + if (*pattern == '~') + printv(buf, bsize, "\t"); + else if (*pattern == ',') { + printv(buf, bsize, ", "); + } + else if (*pattern != '%') { + printv(buf, bsize, "%c", *pattern); + } + else { + pattern++; + if (*pattern >= '0' && *pattern <= '9') { + offs = argbase + strtol(pattern, &p, 10); + pattern = p; + } + else { + offs = argidx; + } + + switch (*pattern) { + case 0: + pattern--; + break; + + case '%': + printv(buf, bsize, "%%"); + break; + + case 'a': + /* %a: word value, always an address */ + w = ibuf[offs] | (ibuf[offs + 1] << 8); + print_word(dasm, buf, bsize, w, 0, 1); + offs += 2; + break; + + case 'b': + /* %b: byte value */ + print_byte(buf, bsize, ibuf[offs]); + offs++; + break; + + case 'c': + /* %c: word value, always a ROM call number */ + w = ibuf[offs] | (ibuf[offs + 1] << 8); + print_romcall(dasm, buf, bsize, w); + offs += 2; + break; + + case 'C': + /* %C: byte value, always a ROM call number */ + print_romcall(dasm, buf, bsize, ibuf[offs]); + offs++; + break; + + case 'f': + /* %f: flag value */ + print_flag(dasm, buf, bsize, + (ibuf[offs + 1] >> 3) & 7, + ibuf[offs], ibuf[0]); + offs += 2; + break; + + case 'i': + /* %i: IX or IY by instruction prefix */ + if (ibuf[0] == 0xdd) + printv(buf, bsize, "IX"); + else + printv(buf, bsize, "IY"); + break; + + case 'j': + /* %j: word value, always a jump address */ + w = ibuf[offs] | (ibuf[offs + 1] << 8); + print_word(dasm, buf, bsize, w, 0, 0); + offs += 2; + break; + + case 'r': + /* %r: one-byte PC-relative value */ + if (ibuf[offs] & 0x80) + w = pc + offs - 0xff + ibuf[offs]; + else + w = pc + offs + 1 + ibuf[offs]; + print_word(dasm, buf, bsize, w, 0, 0); + offs++; + break; + + case 's': + /* %s: one-byte signed displacement */ + if (ibuf[0] == 0xfd + && (sym = find_symbol(&dasm->flags, + ibuf[offs]))) { + printv(buf, bsize, " + %s", sym->name); + } + else if (ibuf[offs] & 0x80) { + printv(buf, bsize, " - "); + print_byte(buf, bsize, + 0x100 - ibuf[offs]); + } + else if (ibuf[offs]) { + printv(buf, bsize, " + "); + print_byte(buf, bsize, ibuf[offs]); + } + offs++; + break; + + case 'w': + /* %w: word value */ + w = ibuf[offs] | (ibuf[offs + 1] << 8); + print_word(dasm, buf, bsize, w, 1, 1); + offs += 2; + break; + + case 'z': + /* %z: RST target address */ + print_word(dasm, buf, bsize, ibuf[0] & 0x38, + 0, 0); + break; + } + if (offs > argidx) + argidx = offs; + } + + pattern++; + } +} + +void tilem_disasm_disassemble(const TilemDisasm* dasm, TilemCalc* calc, + int phys, dword addr, dword* nextaddr, + char* buffer, int bufsize) +{ + byte ibuf[64]; + dword a, addr_l, max; + int length, argbase, i; + const char* pattern; + + if (phys) { + max = calc->hw.romsize + calc->hw.ramsize; + for (i = 0; i < 4; i++) { + a = (addr + i) % max; + ibuf[i] = calc->mem[a]; + } + + addr_l = (*calc->hw.mem_ptol)(calc, addr); + if (addr_l == 0xffffffff) + addr_l = (addr & 0x3fff) | 0x4000; + } + else { + max = 0x10000; + for (i = 0; i < 4; i++) { + a = (addr + i) & 0xffff; + ibuf[i] = calc->mem[(*calc->hw.mem_ltop)(calc, a)]; + } + + addr_l = addr; + } + + get_instruction_info(dasm, ibuf, &length, &argbase, &pattern); + + if (phys) { + for (i = 0; i < length; i++) { + ibuf[i] = calc->mem[addr]; + addr = (addr + 1) % max; + } + } + else { + for (i = 0; i < length; i++) { + ibuf[i] = calc->mem[(*calc->hw.mem_ltop)(calc, addr)]; + addr = (addr + 1) & 0xffff; + } + } + + if (nextaddr) + *nextaddr = addr; + + if (buffer) { + disassemble_pattern(dasm, pattern, ibuf, addr_l, argbase, + &buffer, &bufsize); + } +} diff --git a/tool/tilem-src/db/listing.c b/tool/tilem-src/db/listing.c new file mode 100644 index 0000000..3936d6c --- /dev/null +++ b/tool/tilem-src/db/listing.c @@ -0,0 +1,379 @@ +/* + * libtilemdb - Utilities for debugging Z80 assembly programs + * + * Copyright (C) 2010 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 +#include +#include "tilemdb.h" + +struct _TilemListingLineCache { + dword minaddr; + dword maxaddr; + int* order; +}; + +static void sort_lines(TilemListing* lst, + int start, int end) +{ + int pivot, ncstart, ncend, n; + dword addr, pivotaddr; + int* order = lst->linecache->order; + + pivot = order[start]; + pivotaddr = lst->lines[pivot].address; + + ncstart = start + 1; + ncend = end; + + while (ncstart < ncend) { + n = order[ncstart]; + addr = lst->lines[n].address; + if (addr < pivotaddr) { + ncstart++; + } + else { + order[ncstart] = order[ncend - 1]; + order[ncend - 1] = n; + ncend--; + } + } + + ncstart--; + order[start] = order[ncstart]; + order[ncstart] = pivot; + + if (ncstart > start + 1) + sort_lines(lst, start, ncstart); + if (end > ncend + 1) + sort_lines(lst, ncend, end); +} + +static void ensure_order(TilemListing* lst) +{ + int i; + + if (lst->nlines == 0) { + lst->linecache->minaddr = 0xffff; + lst->linecache->maxaddr = 0; + } + else if (!lst->linecache->order) { + lst->linecache->order = tilem_new_atomic(int, lst->nlines); + for (i = 0; i < lst->nlines; i++) + lst->linecache->order[i] = i; + sort_lines(lst, 0, lst->nlines); + + i = lst->linecache->order[0]; + lst->linecache->minaddr = lst->lines[i].address; + i = lst->linecache->order[lst->nlines - 1]; + lst->linecache->maxaddr = lst->lines[i].address; + } +} + +static void order_destroyed(TilemListing* lst) +{ + tilem_free(lst->linecache->order); + lst->linecache->order = NULL; +} + +static int first_at_addr(TilemListing* lst, dword addr) +{ + int start, end, i; + + start = 0; + end = lst->nlines; + while (start < end) { + i = (start + end) / 2; + if (lst->lines[lst->linecache->order[i]].address < addr) + start = i + 1; + else + end = i; + } + return start; +} + +TilemListing* tilem_listing_new() +{ + TilemListing* lst = tilem_new0(TilemListing, 1); + lst->lines = NULL; + lst->linecache = tilem_new0(TilemListingLineCache, 1); + lst->linecache->order = NULL; + return lst; +} + +void tilem_listing_free(TilemListing* lst) +{ + int i; + + if (!lst) + return; + + for (i = 0; i < lst->nlines; i++) + tilem_free(lst->lines[i].text); + tilem_free(lst->lines); + tilem_free(lst->linecache->order); + tilem_free(lst->linecache); + tilem_free(lst); +} + +void tilem_listing_file_clear(TilemListing* lst) +{ + order_destroyed(lst); + lst->nlines = 0; + lst->nlines_a = 0; + tilem_free(lst->lines); + lst->lines = NULL; +} + +void tilem_listing_append_line(TilemListing* lst, int srclinenum, dword address, + int depth, int datasize, const byte* data, + const char* text, int is_expansion) +{ + TilemListingLine* line; + int i; + + order_destroyed(lst); + lst->nlines++; + if (lst->nlines >= lst->nlines_a) { + lst->nlines_a = lst->nlines * 2; + lst->lines = tilem_renew(TilemListingLine, + lst->lines, lst->nlines_a); + } + + line = &lst->lines[lst->nlines - 1]; + + line->listing = lst; + line->srclinenum = srclinenum; + line->address = address & 0xffff; + line->depth = depth; + + if (datasize > TILEM_MAX_LINE_BYTES) + datasize = TILEM_MAX_LINE_BYTES; + + line->datasize = datasize; + for (i = 0; i < datasize; i++) + line->data[i] = data[i]; + + if (text) { + line->text = tilem_new_atomic(char, strlen(text) + 1); + strcpy(line->text, text); + + if ((text[0] >= 'A' && text[0] <= 'Z') + || (text[0] >= 'a' && text[0] <= 'z') + || (text[0] & 0x80)) + line->is_label = 1; + else + line->is_label = 0; + } + else { + line->text = NULL; + line->is_label = 0; + } + + line->is_expansion = is_expansion; +} + +void tilem_listing_get_address_range(TilemListing* lst, dword* min, dword* max) +{ + ensure_order(lst); + if (min) + *min = lst->linecache->minaddr; + if (max) + *max = lst->linecache->maxaddr; +} + +TilemListingLine* tilem_listing_line_get_next(TilemListingLine* line) +{ + if (!line) + return NULL; + + if (line != line->listing->lines + line->listing->nlines) + return line + 1; + else + return NULL; +} + +TilemListingLine* tilem_listing_line_get_prev(TilemListingLine* line) +{ + if (!line) + return NULL; + + if (line != line->listing->lines) + return line - 1; + else + return NULL; + +} + +TilemListingLine* tilem_listing_get_loaded_line_at_addr(TilemListing* lst, + dword address, + TilemCalc* calc, + int match_internal) +{ + int i; + TilemListingLine *line; + + ensure_order(lst); + i = first_at_addr(lst, address + 1); + + while (--i >= 0) { + line = &lst->lines[lst->linecache->order[i]]; + + if (match_internal) { + if (line->address + TILEM_MAX_LINE_BYTES <= address) + return NULL; + else if (line->address + line->datasize <= address) + continue; + } + else { + if (line->address != address) + return NULL; + } + + if (tilem_listing_line_is_loaded(line, calc)) + return line; + } + return NULL; +} + +static inline unsigned int getbyte(TilemCalc* calc, dword addr) +{ + dword pa = (*calc->hw.mem_ltop)(calc, addr & 0xffff); + return calc->mem[pa]; +} + +static inline int line_load_count(const TilemListingLine* line, TilemCalc* calc) +{ + int i, n; + + for (i = n = 0; i < line->datasize; i++) + if (getbyte(calc, line->address + i) == line->data[i]) + n++; + return n; +} + +static inline TilemListingLine* get_next_local(TilemListingLine* line) +{ + TilemListingLine* nline = tilem_listing_line_get_next(line); + + if (nline && nline->address != line->address + line->datasize) + return NULL; + else + return nline; +} + +static inline TilemListingLine* get_prev_local(TilemListingLine* line) +{ + TilemListingLine* nline = tilem_listing_line_get_prev(line); + + if (nline && nline->address != line->address + line->datasize) + return NULL; + else + return nline; +} + +int tilem_listing_line_is_loaded(TilemListingLine* line, TilemCalc* calc) +{ + int nbytes, ngood; + TilemListingLine *nline; + + while (line->datasize == 0 && (nline = get_next_local(line))) + line = nline; + + if (line_load_count(line, calc) != line->datasize) + return 0; + else { + nbytes = ngood = line->datasize; + + nline = line; + while ((nline = get_next_local(nline)) + && nline->address < line->address + 32) { + nbytes += nline->datasize; + ngood += line_load_count(nline, calc); + } + + nline = line; + while ((nline = get_prev_local(nline)) + && line->address < nline->address + 32) { + nbytes += nline->datasize; + ngood += line_load_count(nline, calc); + } + + return (ngood > nbytes / 2); + } +} + +static int bptest_listing_line(TilemCalc* calc, dword addr TILEM_ATTR_UNUSED, + void* data) +{ + return tilem_listing_line_is_loaded(data, calc); +} + +int tilem_listing_line_add_breakpoint(TilemListingLine* line, + TilemCalc* calc, int bptype, + int match_internal) +{ + dword max; + + if (match_internal && line->datasize > 0) + max = line->address + line->datasize - 1; + else + max = line->address; + + return tilem_z80_add_breakpoint(calc, bptype, line->address, max, + 0xffff, &bptest_listing_line, + (void*) line); +} + +static int bptest_listing_int(TilemCalc* calc, dword addr, void* data) +{ + TilemListing* lst = data; + + if (tilem_listing_get_loaded_line_at_addr(lst, addr, calc, 1)) + return 1; + else + return 0; +} + +static int bptest_listing_top(TilemCalc* calc, dword addr, void* data) +{ + TilemListing* lst = data; + + if (tilem_listing_get_loaded_line_at_addr(lst, addr, calc, 0)) + return 1; + else + return 0; +} + +int tilem_listing_add_breakpoint(TilemListing* lst, TilemCalc* calc, + int bptype, int match_internal) +{ + dword min, max; + + tilem_listing_get_address_range(lst, &min, &max); + return tilem_z80_add_breakpoint(calc, bptype, min, max, 0xffff, + (match_internal + ? &bptest_listing_int + : &bptest_listing_top), lst); +} + diff --git a/tool/tilem-src/db/lstfile.c b/tool/tilem-src/db/lstfile.c new file mode 100644 index 0000000..938ce1c --- /dev/null +++ b/tool/tilem-src/db/lstfile.c @@ -0,0 +1,384 @@ +/* + * libtilemdb - Utilities for debugging Z80 assembly programs + * + * Copyright (C) 2010 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 +#include +#include +#include "tilemdb.h" + +/* Test if TEXT contains a correctly-formatted number WIDTH characters + wide. If PAD = 0, number is padded on left with zeroes; if PAD = + 1, number is padded on left with spaces; if PAD = -1, number is + padded on right with spaces. */ +static int match_num(const char* text, int width, int pad, int base, + int* value) +{ + int start, end; + char* p; + + if ((int) strlen(text) < width) + return 0; + + start = 0; + while (start < width && text[start] == ' ') + start++; + + end = width; + while (end > start && text[end - 1] == ' ') + end--; + + if (start == end) + return 0; + + if (pad != 1 && start != 0) + return 0; + + if (pad != -1 && end != width) + return 0; + + if (pad != 0 && text[start] == '0' && start != end - 1) + return 0; + + *value = strtol(text + start, &p, base); + + return (p == text + end); +} + +/* Test if string exactly matches a printf-like format string. + %d, %x match decimal and hex integers; + %s matches any string of exactly that width. */ +static int match_pattern(const char* text, const char* pattern, int* datasize, + byte* data, ...) +{ + int width, pad, value, *ip; + char **sp; + char *end; + va_list ap; + int failed = 0, lastdb = 0; + + va_start(ap, data); + + *datasize = 0; + + while (!failed && *pattern) { + if (*pattern != '%') { + if (*text == *pattern) + text++; + else + failed = 1; + pattern++; + } + else { + pattern++; + if (*pattern == '0') { + pad = 0; + pattern++; + } + else if (*pattern == '-') { + pad = -1; + pattern++; + } + else + pad = 1; + + width = strtol(pattern, &end, 10); + pattern = end; + if (!width) + width = strlen(text); + else if (width > (int) strlen(text)) { + failed = 1; + break; + } + + switch (*pattern) { + case '%': + if (*text == '%') + text++; + else + failed = 1; + break; + + case 'B': + if (text[0] == ' ' && text[1] == ' ') { + lastdb = 1; + text += 2; + break; + } + case 'b': + if (match_num(text, width, pad, 16, &value) + && !lastdb) { + data[*datasize] = value; + (*datasize)++; + text += width; + } + else { + failed = 1; + } + break; + + case 'd': + ip = va_arg(ap, int *); + if (match_num(text, width, pad, 10, ip)) { + text += width; + } + else { + failed = 1; + } + break; + + case 'x': + ip = va_arg(ap, int *); + if (match_num(text, width, pad, 16, ip)) { + text += width; + } + else { + failed = 1; + } + break; + + case 's': + sp = va_arg(ap, char **); + *sp = (char*) text; + text += width; + break; + } + pattern++; + } + } + + va_end(ap); + + return (!failed && *text == 0); +} + +static int get_tasm_depth(const char* s) +{ + if (!strncmp(s, "+++", 3)) + return 4; + else if (!strncmp(s, "++ ", 3)) + return 3; + else if (!strncmp(s, "+ ", 3)) + return 2; + else if (!strncmp(s, " ", 3)) + return 1; + else + return 0; +} + +static int parse_listing(const char* line, int* linenum, int* addr, int* depth, + int* datasize, byte* data, char** text, int* isexp, + const TilemListingLine* prevline) +{ + int dummy; + char *p, *q; + + /* tasm */ + + if (match_pattern(line, "%04d%3s%04x%1s%02B %02B %02B %02B %s", + datasize, data, linenum, &p, addr, &q, text)) { + *depth = get_tasm_depth(p); + *isexp = 0; + return 1; + } + if (match_pattern(line, "%04d%3s%04x%1s%02b %02b %02b ", + datasize, data, linenum, &p, addr, &q) + || match_pattern(line, "%04d%3s%04x%1s%02b %02b ", + datasize, data, linenum, &p, addr, &q) + || match_pattern(line, "%04d%3s%04x%1s%02b ", + datasize, data, linenum, &p, addr, &q)) { + *depth = get_tasm_depth(p); + *text = NULL; + *isexp = 0; + return 1; + } + + /* zmasm */ + + if (match_pattern(line, "%08x %02B %02B %02B %02B %02B %02B %4s %5d %s", + datasize, data, addr, &p, linenum, text) + && p[0] >= 'A' && p[0] <= 'Z') { + *depth = p[0] - 'A' + 1; + *isexp = (p[1] == '+'); + return 1; + } + if (match_pattern(line, "%08x %02B %02B %02B %02B %02B %02B %4s %5d", + datasize, data, addr, &p, linenum) + && p[0] >= 'A' && p[0] <= 'Z') { + *text = NULL; + *depth = p[0] - 'A' + 1; + *isexp = (p[1] == '+'); + return 1; + } + if (match_pattern(line, " %4s %5d %s", + datasize, data, &p, linenum, text) + && p[0] >= 'A' && p[0] <= 'Z') { + *addr = prevline->address + prevline->datasize; + *depth = p[0] - 'A' + 1; + *isexp = (p[1] == '+'); + return 1; + } + if (match_pattern(line, " %08x %4s %5d %s", + datasize, data, &dummy, &p, linenum, text) + && p[0] >= 'A' && p[0] <= 'Z') { + *addr = prevline->address + prevline->datasize; + *depth = p[0] - 'A' + 1; + *isexp = (p[1] == '+'); + return 1; + } + + /* spasm - old and new versions */ + + if (match_pattern(line, "%5d %04x: %02b %02b %02b %02b %s", + datasize, data, linenum, addr, text) + || match_pattern(line, "%5d %04x: %02b %02b %02b - %s", + datasize, data, linenum, addr, text) + || match_pattern(line, "%5d %04x: %02b %02b - - %s", + datasize, data, linenum, addr, text) + || match_pattern(line, "%5d %04x: %02b - - - %s", + datasize, data, linenum, addr, text) + || match_pattern(line, "%5d %04x: - - - - %s", + datasize, data, linenum, addr, text) + + || match_pattern(line, "%5d %02x:%04x %02b %02b %02b %02b %s", + datasize, data, linenum, &dummy, addr, text) + || match_pattern(line, "%5d %02x:%04x %02b %02b %02b - %s", + datasize, data, linenum, &dummy, addr, text) + || match_pattern(line, "%5d %02x:%04x %02b %02b - - %s", + datasize, data, linenum, &dummy, addr, text) + || match_pattern(line, "%5d %02x:%04x %02b - - - %s", + datasize, data, linenum, &dummy, addr, text) + || match_pattern(line, "%5d %02x:%04x - - - - %s", + datasize, data, linenum, &dummy, addr, text)) { + *depth = *isexp = 0; + return 1; + } + if (match_pattern(line, " %02b %02b %02b %02b %s", + datasize, data, text) + || match_pattern(line, " %02b %02b %02b - %s", + datasize, data, text) + || match_pattern(line, " %02b %02b - - %s", + datasize, data, text) + || match_pattern(line, " %02b - - - %s", + datasize, data, text) + + || match_pattern(line, " %02b %02b %02b %02b %s", + datasize, data, text) + || match_pattern(line, " %02b %02b %02b - %s", + datasize, data, text) + || match_pattern(line, " %02b %02b - - %s", + datasize, data, text) + || match_pattern(line, " %02b - - - %s", + datasize, data, text)) { + *linenum = prevline->srclinenum; + *addr = prevline->address + prevline->datasize; + *depth = *isexp = 0; + return 1; + } + + /* tpasm or miniasm */ + + if (match_pattern(line, "%-5d %08x %02B %02B %02B %02B %02B %2s%s", + datasize, data, linenum, addr, &p, text) + || match_pattern(line, "%5d %08x %02B %02B %02B %02B %02B %2s%s", + datasize, data, linenum, addr, &p, text)) { + *depth = 0; + *isexp = (*p == 'm' || *p == 'a'); + return 1; + } + if (match_pattern(line, "%-5d %08x (%08x) %2s%s", + datasize, data, linenum, addr, &dummy, &p, text) + || match_pattern(line, "%5d %08x (%08x) %2s%s", + datasize, data, linenum, addr, &dummy, &p, text)) { + *depth = 0; + *isexp = (*p == 'm' || *p == 'a'); + return 1; + } + if (match_pattern(line, " %02B %02B %02B %02B %02B %1s", + datasize, data, &p)) { + *text = NULL; + *linenum = prevline->srclinenum; + *addr = prevline->address + prevline->datasize; + *depth = 0; + *isexp = (*p == 'm' || *p == 'a'); + return 1; + } + if (match_pattern(line, " %02b %02b %02b %02b %02b", + datasize, data) + || match_pattern(line, " %02b %02b %02b %02b", + datasize, data) + || match_pattern(line, " %02b %02b %02b", + datasize, data) + || match_pattern(line, " %02b %02b", + datasize, data) + || match_pattern(line, " %02b", + datasize, data)) { + *text = NULL; + *linenum = prevline->srclinenum; + *addr = prevline->address + prevline->datasize; + *depth = *isexp = 0; + return 1; + } + + printf("***\t%s\n", line); + + return 0; +} + +int tilem_listing_read_file(TilemListing* lst, FILE* lstfile) +{ + char buf[1024]; + int linenum, addr, depth, isexp, datasize; + byte data[TILEM_MAX_LINE_BYTES]; + char *text; + int i, status = 1; + TilemListingLine prevline; + + prevline.text = NULL; + prevline.srclinenum = 0; + prevline.address = 0xffffffff; + prevline.depth = 0; + prevline.datasize = 0; + + while (fgets(buf, sizeof(buf), lstfile)) { + i = strlen(buf); + while (i > 0 && (buf[i - 1] == '\r' || buf[i - 1] == '\n')) + i--; + buf[i] = 0; + + if (parse_listing(buf, &linenum, &addr, &depth, &datasize, + data, &text, &isexp, &prevline)) { + + if (linenum) + status = 0; + + tilem_listing_append_line(lst, linenum, addr, depth, + datasize, data, text, isexp); + prevline = lst->lines[lst->nlines - 1]; + } + } + + return status; +} + diff --git a/tool/tilem-src/db/tilemdb.h b/tool/tilem-src/db/tilemdb.h new file mode 100644 index 0000000..daef3dc --- /dev/null +++ b/tool/tilem-src/db/tilemdb.h @@ -0,0 +1,149 @@ +/* + * libtilemdb - Utilities for debugging Z80 assembly programs + * + * Copyright (C) 2010 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 + * . + */ + +#ifndef _TILEMDB_H +#define _TILEMDB_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Disassembler */ + +typedef struct _TilemDisasm TilemDisasm; + +/* Create a new disassembly context. */ +TilemDisasm* tilem_disasm_new(void); + +/* Free a disassembly context. */ +void tilem_disasm_free(TilemDisasm* dasm); + +/* Read symbols from SYMFILE. */ +int tilem_disasm_read_symbol_file(TilemDisasm* dasm, FILE* symfile); + +/* Set symbol NAME to value VALUE. */ +void tilem_disasm_set_label(TilemDisasm* dasm, const char* name, + dword value); + +/* Check if symbol NAME is defined. If symbol is defined and VALUE is + non-null, set *VALUE to symbol's value. */ +int tilem_disasm_get_label(const TilemDisasm* dasm, const char* name, + dword* value); + +/* Check if a label is defined at the given address. */ +const char* tilem_disasm_get_label_at_address(const TilemDisasm* dasm, + dword addr); + +/* Disassemble a line starting at address ADDR. Store text (up to + BUFSIZE characters) in BUFFER, and set *NEXTADDR to the address of + the following line. If PHYS is 0, use logical addresses; otherwise + use physical addresses. */ +void tilem_disasm_disassemble(const TilemDisasm* dasm, TilemCalc* calc, + int phys, dword addr, dword* nextaddr, + char* buffer, int bufsize); + + +/* Assembly listing files */ + +typedef struct _TilemListing TilemListing; +typedef struct _TilemListingLine TilemListingLine; +typedef struct _TilemListingLineCache TilemListingLineCache; + +#define TILEM_MAX_LINE_BYTES 6 + +struct _TilemListingLine { + TilemListing* listing; /* Listing to which this line + belongs */ + char* text; /* Text of source line */ + int srclinenum; /* Line number in original + source file */ + dword address; /* Address */ + int depth; /* Source file inclusion + depth */ + byte datasize; /* Number of data bytes */ + unsigned is_label : 1; /* = 1 if line appears to + contain a label */ + unsigned is_expansion : 1; /* = 1 if line is part of a + macro expansion */ + byte data[TILEM_MAX_LINE_BYTES]; /* Data bytes on this line */ +}; + +struct _TilemListing { + int nlines; + int nlines_a; + TilemListingLine* lines; + TilemListingLineCache* linecache; +}; + +/* Create new assembly listing. */ +TilemListing* tilem_listing_new(void); + +/* Free listing data. */ +void tilem_listing_free(TilemListing* lst); + +/* Clear listing file contents. */ +void tilem_listing_clear(TilemListing* lst); + +/* Add a line to the end of the listing file. */ +void tilem_listing_append_line(TilemListing* lst, int srclinenum, dword address, + int depth, int datasize, const byte* data, + const char* text, int is_expansion); + +/* Calculate minimum and maximum address used by a listing file. */ +void tilem_listing_get_address_range(TilemListing* lst, dword* min, dword* max); + +/* Get next line, if any. */ +TilemListingLine* tilem_listing_line_get_next(TilemListingLine* line); + +/* Get previous line, if any. */ +TilemListingLine* tilem_listing_line_get_prev(TilemListingLine* line); + +/* Find the line (if any) currently loaded at the given address. If + MATCH_INTERNAL = 0, find only lines that begin at that address. */ +TilemListingLine* tilem_listing_get_loaded_line_at_addr(TilemListing* lst, + dword address, + TilemCalc* calc, + int match_internal); + +/* Check if given line is currently loaded (and mapped into Z80 memory + space.) */ +int tilem_listing_line_is_loaded(TilemListingLine* line, TilemCalc* calc); + +/* Set a breakpoint to be triggered on the given line. */ +int tilem_listing_line_add_breakpoint(TilemListingLine* line, + TilemCalc* calc, int bptype, + int match_internal); + +/* Set a breakpoint to be triggered on any line in the listing. */ +int tilem_listing_add_breakpoint(TilemListing* lst, TilemCalc* calc, + int bptype, int match_internal); + +/* Read assembly listing from LSTFILE. */ +int tilem_listing_read_file(TilemListing* lst, FILE* lstfile); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tool/tilem-src/emu/Makefile.in b/tool/tilem-src/emu/Makefile.in new file mode 100644 index 0000000..915d6d8 --- /dev/null +++ b/tool/tilem-src/emu/Makefile.in @@ -0,0 +1,218 @@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +bindir = @bindir@ +datadir = @datadir@ +pkgdatadir = @datadir@/tilem2 +mandir = @mandir@ + +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +@SET_MAKE@ + +AR = @AR@ +CC = @CC@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +DEFS = @DEFS@ +OPT_CFLAGS = @OPT_CFLAGS@ +RANLIB = @RANLIB@ +SHELL = @SHELL@ + +core_objects = calcs.o z80.o state.o rom.o flash.o link.o keypad.o lcd.o \ + cert.o md5.o timers.o monolcd.o graylcd.o grayimage.o graycolor.o + +x7_objects = x7_init.o x7_io.o x7_memory.o x7_subcore.o +x1_objects = x1_init.o x1_io.o x1_memory.o x1_subcore.o +x2_objects = x2_init.o x2_io.o x2_memory.o x2_subcore.o +x3_objects = x3_init.o x3_io.o x3_memory.o x3_subcore.o +xp_objects = xp_init.o xp_io.o xp_memory.o xp_subcore.o +xs_objects = xs_init.o xs_io.o xs_memory.o xs_subcore.o +x4_objects = x4_init.o x4_io.o x4_memory.o x4_subcore.o +xz_objects = xz_init.o xz_io.o xz_memory.o xz_subcore.o +xn_objects = xn_init.o xn_io.o xn_memory.o xn_subcore.o +x5_objects = x5_init.o x5_io.o x5_memory.o x5_subcore.o +x6_objects = x6_init.o x6_io.o x6_memory.o x6_subcore.o + +objects = $(core_objects) $(x7_objects) $(x1_objects) $(x2_objects) \ + $(x3_objects) $(xp_objects) $(xs_objects) $(x4_objects) $(xz_objects) \ + $(xn_objects) $(x5_objects) $(x6_objects) + +compile = $(CC) -I$(top_builddir) -I$(srcdir) $(CFLAGS) $(CPPFLAGS) $(DEFS) $(OPT_CFLAGS) + +all: libtilemcore.a + +libtilemcore.a: $(objects) + $(AR) cru libtilemcore.a $(objects) + $(RANLIB) libtilemcore.a + +# Main emulator core + +calcs.o: calcs.c tilem.h z80.h ../config.h + $(compile) -c $(srcdir)/calcs.c +z80.o: z80.c z80.h z80cmds.h z80main.h z80cb.h z80ddfd.h z80ed.h tilem.h ../config.h + $(compile) -c $(srcdir)/z80.c +state.o: state.c tilem.h z80.h ../config.h + $(compile) -c $(srcdir)/state.c +rom.o: rom.c tilem.h ../config.h + $(compile) -c $(srcdir)/rom.c +flash.o: flash.c tilem.h ../config.h + $(compile) -c $(srcdir)/flash.c +link.o: link.c tilem.h ../config.h + $(compile) -c $(srcdir)/link.c +keypad.o: keypad.c tilem.h scancodes.h ../config.h + $(compile) -c $(srcdir)/keypad.c +lcd.o: lcd.c tilem.h ../config.h + $(compile) -c $(srcdir)/lcd.c +cert.o: cert.c tilem.h ../config.h + $(compile) -c $(srcdir)/cert.c +md5.o: md5.c tilem.h ../config.h + $(compile) -c $(srcdir)/md5.c +timers.o: timers.c tilem.h ../config.h + $(compile) -c $(srcdir)/timers.c +monolcd.o: monolcd.c tilem.h ../config.h + $(compile) -c $(srcdir)/monolcd.c +graylcd.o: graylcd.c tilem.h graylcd.h ../config.h + $(compile) -c $(srcdir)/graylcd.c +grayimage.o: grayimage.c tilem.h graylcd.h ../config.h + $(compile) -c $(srcdir)/grayimage.c +graycolor.o: graycolor.c tilem.h ../config.h + $(compile) -c $(srcdir)/graycolor.c + +# TI-73 + +x7_init.o: x7/x7_init.c x7/x7.h tilem.h ../config.h + $(compile) -c $(srcdir)/x7/x7_init.c +x7_io.o: x7/x7_io.c x7/x7.h tilem.h ../config.h + $(compile) -c $(srcdir)/x7/x7_io.c +x7_memory.o: x7/x7_memory.c x7/x7.h tilem.h ../config.h + $(compile) -c $(srcdir)/x7/x7_memory.c +x7_subcore.o: x7/x7_subcore.c x7/x7.h tilem.h ../config.h + $(compile) -c $(srcdir)/x7/x7_subcore.c + +# TI-81 + +x1_init.o: x1/x1_init.c x1/x1.h tilem.h ../config.h + $(compile) -c $(srcdir)/x1/x1_init.c +x1_io.o: x1/x1_io.c x1/x1.h tilem.h ../config.h + $(compile) -c $(srcdir)/x1/x1_io.c +x1_memory.o: x1/x1_memory.c x1/x1.h tilem.h ../config.h + $(compile) -c $(srcdir)/x1/x1_memory.c +x1_subcore.o: x1/x1_subcore.c x1/x1.h tilem.h ../config.h + $(compile) -c $(srcdir)/x1/x1_subcore.c + +# TI-82 + +x2_init.o: x2/x2_init.c x2/x2.h tilem.h ../config.h + $(compile) -c $(srcdir)/x2/x2_init.c +x2_io.o: x2/x2_io.c x2/x2.h tilem.h ../config.h + $(compile) -c $(srcdir)/x2/x2_io.c +x2_memory.o: x2/x2_memory.c x2/x2.h tilem.h ../config.h + $(compile) -c $(srcdir)/x2/x2_memory.c +x2_subcore.o: x2/x2_subcore.c x2/x2.h tilem.h ../config.h + $(compile) -c $(srcdir)/x2/x2_subcore.c + +# TI-83 + +x3_init.o: x3/x3_init.c x3/x3.h tilem.h ../config.h + $(compile) -c $(srcdir)/x3/x3_init.c +x3_io.o: x3/x3_io.c x3/x3.h tilem.h ../config.h + $(compile) -c $(srcdir)/x3/x3_io.c +x3_memory.o: x3/x3_memory.c x3/x3.h tilem.h ../config.h + $(compile) -c $(srcdir)/x3/x3_memory.c +x3_subcore.o: x3/x3_subcore.c x3/x3.h tilem.h ../config.h + $(compile) -c $(srcdir)/x3/x3_subcore.c + +# TI-83 Plus + +xp_init.o: xp/xp_init.c xp/xp.h tilem.h ../config.h + $(compile) -c $(srcdir)/xp/xp_init.c +xp_io.o: xp/xp_io.c xp/xp.h tilem.h ../config.h + $(compile) -c $(srcdir)/xp/xp_io.c +xp_memory.o: xp/xp_memory.c xp/xp.h tilem.h ../config.h + $(compile) -c $(srcdir)/xp/xp_memory.c +xp_subcore.o: xp/xp_subcore.c xp/xp.h tilem.h ../config.h + $(compile) -c $(srcdir)/xp/xp_subcore.c + +# TI-83 Plus SE + +xs_init.o: xs/xs_init.c xs/xs.h tilem.h ../config.h + $(compile) -c $(srcdir)/xs/xs_init.c +xs_io.o: xs/xs_io.c xs/xs.h tilem.h ../config.h + $(compile) -c $(srcdir)/xs/xs_io.c +xs_memory.o: xs/xs_memory.c xs/xs.h tilem.h ../config.h + $(compile) -c $(srcdir)/xs/xs_memory.c +xs_subcore.o: xs/xs_subcore.c xs/xs.h tilem.h ../config.h + $(compile) -c $(srcdir)/xs/xs_subcore.c + +# TI-84 Plus + +x4_init.o: x4/x4_init.c x4/x4.h tilem.h ../config.h + $(compile) -c $(srcdir)/x4/x4_init.c +x4_io.o: x4/x4_io.c x4/x4.h tilem.h ../config.h + $(compile) -c $(srcdir)/x4/x4_io.c +x4_memory.o: x4/x4_memory.c x4/x4.h tilem.h ../config.h + $(compile) -c $(srcdir)/x4/x4_memory.c +x4_subcore.o: x4/x4_subcore.c x4/x4.h tilem.h ../config.h + $(compile) -c $(srcdir)/x4/x4_subcore.c + +# TI-84 Plus SE + +xz_init.o: xz/xz_init.c xz/xz.h tilem.h ../config.h + $(compile) -c $(srcdir)/xz/xz_init.c +xz_io.o: xz/xz_io.c xz/xz.h tilem.h ../config.h + $(compile) -c $(srcdir)/xz/xz_io.c +xz_memory.o: xz/xz_memory.c xz/xz.h tilem.h ../config.h + $(compile) -c $(srcdir)/xz/xz_memory.c +xz_subcore.o: xz/xz_subcore.c xz/xz.h tilem.h ../config.h + $(compile) -c $(srcdir)/xz/xz_subcore.c + +# TI-Nspire 84 Plus emulator + +xn_init.o: xn/xn_init.c xn/xn.h tilem.h ../config.h + $(compile) -c $(srcdir)/xn/xn_init.c +xn_io.o: xn/xn_io.c xn/xn.h tilem.h ../config.h + $(compile) -c $(srcdir)/xn/xn_io.c +xn_memory.o: xn/xn_memory.c xn/xn.h tilem.h ../config.h + $(compile) -c $(srcdir)/xn/xn_memory.c +xn_subcore.o: xn/xn_subcore.c xn/xn.h tilem.h ../config.h + $(compile) -c $(srcdir)/xn/xn_subcore.c + +# TI-85 + +x5_init.o: x5/x5_init.c x5/x5.h tilem.h ../config.h + $(compile) -c $(srcdir)/x5/x5_init.c +x5_io.o: x5/x5_io.c x5/x5.h tilem.h ../config.h + $(compile) -c $(srcdir)/x5/x5_io.c +x5_memory.o: x5/x5_memory.c x5/x5.h tilem.h ../config.h + $(compile) -c $(srcdir)/x5/x5_memory.c +x5_subcore.o: x5/x5_subcore.c x5/x5.h tilem.h ../config.h + $(compile) -c $(srcdir)/x5/x5_subcore.c + +# TI-86 + +x6_init.o: x6/x6_init.c x6/x6.h tilem.h ../config.h + $(compile) -c $(srcdir)/x6/x6_init.c +x6_io.o: x6/x6_io.c x6/x6.h tilem.h ../config.h + $(compile) -c $(srcdir)/x6/x6_io.c +x6_memory.o: x6/x6_memory.c x6/x6.h tilem.h ../config.h + $(compile) -c $(srcdir)/x6/x6_memory.c +x6_subcore.o: x6/x6_subcore.c x6/x6.h tilem.h ../config.h + $(compile) -c $(srcdir)/x6/x6_subcore.c + + +clean: + rm -f *.o + rm -f libtilemcore.a + + +Makefile: Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status + +$(top_builddir)/config.status: $(top_srcdir)/configure + cd $(top_builddir) && $(SHELL) ./config.status --recheck + +.PRECIOUS: Makefile $(top_builddir)/config.status +.PHONY: clean all diff --git a/tool/tilem-src/emu/calcs.c b/tool/tilem-src/emu/calcs.c new file mode 100644 index 0000000..b251f60 --- /dev/null +++ b/tool/tilem-src/emu/calcs.c @@ -0,0 +1,186 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 +#include "tilem.h" +#include "z80.h" + +extern const TilemHardware hardware_ti73, hardware_ti76, + hardware_ti81, hardware_ti82, hardware_ti83, + hardware_ti83p, hardware_ti83pse, hardware_ti84p, + hardware_ti84pse, hardware_ti84pns, + hardware_ti85, hardware_ti86; + +const TilemHardware* hwmodels[] = { + &hardware_ti73, + &hardware_ti76, + &hardware_ti81, + &hardware_ti82, + &hardware_ti83, + &hardware_ti83p, + &hardware_ti83pse, + &hardware_ti84p, + &hardware_ti84pse, + &hardware_ti84pns, + &hardware_ti85, + &hardware_ti86 }; + +#define NUM_MODELS (sizeof(hwmodels) / sizeof(TilemHardware*)) + +void tilem_get_supported_hardware(const TilemHardware*** models, + int* nmodels) +{ + *models = hwmodels; + *nmodels = NUM_MODELS; +} + +void tilem_calc_reset(TilemCalc* calc) +{ + tilem_z80_reset(calc); + tilem_lcd_reset(calc); + tilem_linkport_reset(calc); + tilem_keypad_reset(calc); + tilem_flash_reset(calc); + tilem_md5_assist_reset(calc); + tilem_user_timers_reset(calc); + if (calc->hw.reset) + (*calc->hw.reset)(calc); +} + +TilemCalc* tilem_calc_new(char id) +{ + int i; + TilemCalc* calc; + dword msize; + + for (i = 0; i < (int) NUM_MODELS; i++) { + if (hwmodels[i]->model_id == id) { + calc = tilem_try_new0(TilemCalc, 1); + if (!calc) { + return NULL; + } + + calc->hw = *hwmodels[i]; + + calc->poweronhalt = 1; + calc->battery = 60; + calc->hwregs = tilem_try_new_atomic(dword, calc->hw.nhwregs); + if (!calc->hwregs) { + tilem_free(calc); + return NULL; + } + + memset(calc->hwregs, 0, calc->hw.nhwregs * sizeof(dword)); + + msize = (calc->hw.romsize + calc->hw.ramsize + + calc->hw.lcdmemsize); + + calc->mem = tilem_try_new_atomic(byte, msize); + if (!calc->mem) { + tilem_free(calc->hwregs); + tilem_free(calc); + return NULL; + } + + calc->ram = calc->mem + calc->hw.romsize; + calc->lcdmem = calc->ram + calc->hw.ramsize; + + memset(calc->ram, 0, msize - calc->hw.romsize); + + calc->lcd.emuflags = TILEM_LCD_REQUIRE_DELAY; + calc->flash.emuflags = TILEM_FLASH_REQUIRE_DELAY; + + tilem_calc_reset(calc); + return calc; + } + } + + fprintf(stderr, "INTERNAL ERROR: invalid model ID '%c'\n", id); + return NULL; +} + +TilemCalc* tilem_calc_copy(TilemCalc* calc) +{ + TilemCalc* newcalc; + dword msize; + + newcalc = tilem_try_new(TilemCalc, 1); + if (!newcalc) + return NULL; + memcpy(newcalc, calc, sizeof(TilemCalc)); + + newcalc->hwregs = tilem_try_new_atomic(dword, calc->hw.nhwregs); + if (!newcalc->hwregs) { + tilem_free(newcalc); + return NULL; + } + memcpy(newcalc->hwregs, calc->hwregs, calc->hw.nhwregs * sizeof(dword)); + + newcalc->z80.timers = tilem_try_new(TilemZ80Timer, + newcalc->z80.ntimers); + if (!newcalc->z80.timers) { + tilem_free(newcalc->hwregs); + tilem_free(newcalc); + return NULL; + } + memcpy(newcalc->z80.timers, calc->z80.timers, + newcalc->z80.ntimers * sizeof(TilemZ80Timer)); + + newcalc->z80.breakpoints = tilem_try_new(TilemZ80Breakpoint, + newcalc->z80.nbreakpoints); + if (!newcalc->z80.breakpoints) { + tilem_free(newcalc->z80.timers); + tilem_free(newcalc->hwregs); + tilem_free(newcalc); + return NULL; + } + memcpy(newcalc->z80.breakpoints, calc->z80.breakpoints, + newcalc->z80.nbreakpoints * sizeof(TilemZ80Breakpoint)); + + msize = (calc->hw.romsize + calc->hw.ramsize + calc->hw.lcdmemsize); + newcalc->mem = tilem_try_new_atomic(byte, msize); + if (!newcalc->mem) { + tilem_free(newcalc->z80.breakpoints); + tilem_free(newcalc->z80.timers); + tilem_free(newcalc->hwregs); + tilem_free(newcalc); + return NULL; + } + memcpy(newcalc->mem, calc->mem, msize * sizeof(byte)); + + newcalc->ram = newcalc->mem + calc->hw.romsize; + newcalc->lcdmem = newcalc->ram + calc->hw.ramsize; + + return newcalc; +} + +void tilem_calc_free(TilemCalc* calc) +{ + tilem_free(calc->mem); + tilem_free(calc->hwregs); + tilem_free(calc->z80.breakpoints); + tilem_free(calc->z80.timers); + tilem_free(calc); +} diff --git a/tool/tilem-src/emu/cert.c b/tool/tilem-src/emu/cert.c new file mode 100644 index 0000000..5c2375c --- /dev/null +++ b/tool/tilem-src/emu/cert.c @@ -0,0 +1,136 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2009-2011 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 +#include "tilem.h" + +static int certificate_valid(byte* cert) +{ + int i, n; + + if (cert[0] != 0) + return 0; + + i = 1; + + /* check that the actual certificate area consists of valid + certificate fields */ + while (cert[i] <= 0x0F) { + switch (cert[i + 1] & 0x0F) { + case 0x0D: + n = cert[i + 2] + 3; + break; + case 0x0E: + n = (cert[i + 2] << 8) + cert[i + 3] + 4; + break; + case 0x0F: + n = 6; + break; + default: + n = (cert[i + 1] & 0xf) + 2; + } + i += n; + if (i >= 0x2000) + return 0; + } + + /* check that the fields end with FF */ + if (cert[i] != 0xFF) + return 0; + + /* if there are fields present, assume the certificate is OK */ + if (i > 1) + return 1; + + /* no fields present -> this could be an incompletely-patched + certificate from an older version of TilEm; verify that the + next 4k bytes are truly empty */ + while (i < 0x1000) { + if (cert[i] != 0xFF) + return 0; + i++; + } + + return 1; +} + +void tilem_calc_fix_certificate(TilemCalc* calc, byte* cert, + int app_start, int app_end, + unsigned exptab_offset) +{ + int i, base, max_apps, page; + unsigned insttab_offset = 0x1fe0; + + /* If the ROM was dumped from an unpatched OS, the certificate + needs to be patched for some calculator functions to + work. */ + + /* First, check if the certificate is already valid */ + + if (cert[0x2000] == 0) + base = 0x2000; + else + base = 0; + + if (certificate_valid(cert + base)) { + return; + } + + tilem_message(calc, "Repairing certificate area..."); + + memset(cert, 0xff, 16384); + + cert[0] = 0; + + cert[insttab_offset] = 0xfe; + + if (app_start < app_end) + max_apps = app_end - app_start + 1; + else + max_apps = app_start - app_end + 1; + + for (i = 0; i < max_apps; i++) { + if (app_start < app_end) + page = app_start + i; + else + page = app_start - i; + + /* Clear installed bit / set expiration count for + existing apps. (If this incorrectly detects pages + that aren't really apps, don't worry about it; + better to err on the side of caution.) */ + if (calc->mem[page << 14] != 0x80 + || calc->mem[(page << 14) + 1] != 0x0f) + continue; + + tilem_message(calc, "Found application at page %02x (index %d)", + page, i); + + cert[insttab_offset + ((i + 1) / 8)] &= ~(1 << ((i + 1) % 8)); + + cert[exptab_offset + 2 * i] = 0x80; + cert[exptab_offset + 2 * i + 1] = 0x00; + } +} diff --git a/tool/tilem-src/emu/flash.c b/tool/tilem-src/emu/flash.c new file mode 100644 index 0000000..eefef6f --- /dev/null +++ b/tool/tilem-src/emu/flash.c @@ -0,0 +1,326 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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" + +#define FLASH_READ 0 +#define FLASH_AA 1 +#define FLASH_55 2 +#define FLASH_PROG 3 +#define FLASH_ERASE 4 +#define FLASH_ERAA 5 +#define FLASH_ER55 6 +#define FLASH_ERROR 7 +#define FLASH_FASTMODE 8 +#define FLASH_FASTPROG 9 +#define FLASH_FASTEXIT 10 + +#define FLASH_BUSY_PROGRAM 1 +#define FLASH_BUSY_ERASE_WAIT 2 +#define FLASH_BUSY_ERASE 3 + +/* Still to do: + - autoselect + - erase suspend + - fast program + - CFI + */ + +#define WARN(xxx) \ + tilem_warning(calc, "Flash error (" xxx ")") +#define WARN2(xxx, yyy, zzz) \ + tilem_warning(calc, "Flash error (" xxx ")", (yyy), (zzz)) + +void tilem_flash_reset(TilemCalc* calc) +{ + calc->flash.unlock = 0; + calc->flash.state = FLASH_READ; + calc->flash.busy = 0; +} + +void tilem_flash_delay_timer(TilemCalc* calc, void* data TILEM_ATTR_UNUSED) +{ + if (calc->flash.busy == FLASH_BUSY_ERASE_WAIT) { + calc->flash.busy = FLASH_BUSY_ERASE; + tilem_z80_set_timer(calc, TILEM_TIMER_FLASH_DELAY, + 200000, 0, 1); + } + else { + calc->flash.busy = 0; + } +} + +#ifdef DISABLE_FLASH_DELAY +# define set_busy(fl, bm, t) +#else +static inline void set_busy(TilemCalc* calc, int busymode, int time) +{ + if (!(calc->flash.emuflags & TILEM_FLASH_REQUIRE_DELAY)) + return; + + calc->flash.busy = busymode; + tilem_z80_set_timer(calc, TILEM_TIMER_FLASH_DELAY, + time, 0, 1); +} +#endif + +static inline void program_byte(TilemCalc* calc, dword a, byte v) +{ + calc->mem[a] &= v; + calc->flash.progaddr = a; + calc->flash.progbyte = v; + + if (calc->mem[a] != v) { + WARN2("bad program %02x over %02x", v, calc->mem[a]); + calc->flash.state = FLASH_ERROR; + } + else { + calc->flash.state = FLASH_READ; + } + + set_busy(calc, FLASH_BUSY_PROGRAM, 7); +} + +static inline void erase_sector(TilemCalc* calc, dword a, dword l) +{ + dword i; + + calc->flash.progaddr = a; + for (i = 0; i < l; i++) + calc->mem[a + i]=0xFF; + calc->flash.state = FLASH_READ; + + set_busy(calc, FLASH_BUSY_ERASE_WAIT, 50); +} + +static const TilemFlashSector* get_sector(TilemCalc* calc, dword pa) +{ + int i; + const TilemFlashSector* sec; + + for (i = 0; i < calc->hw.nflashsectors; i++) { + sec = &calc->hw.flashsectors[i]; + if (pa >= sec->start && pa < sec->start + sec->size) + return sec; + } + + return NULL; +} + +static int sector_writable(TilemCalc* calc, const TilemFlashSector* sec) +{ + return !(sec->protectgroup & ~calc->flash.overridegroup); +} + +byte tilem_flash_read_byte(TilemCalc* calc, dword pa) +{ + byte value; + + if (calc->flash.busy == FLASH_BUSY_PROGRAM) { + if (pa != calc->flash.progaddr) + WARN("reading from Flash while programming"); + value = (~calc->flash.progbyte & 0x80); + value |= calc->flash.toggles; + calc->flash.toggles ^= 0x40; + return (value); + } + else if (calc->flash.busy == FLASH_BUSY_ERASE) { + if ((pa >> 16) != (calc->flash.progaddr >> 16)) + WARN("reading from Flash while erasing"); + value = calc->flash.toggles | 0x08; + calc->flash.toggles ^= 0x44; + return (value); + } + else if (calc->flash.busy == FLASH_BUSY_ERASE_WAIT) { + if ((pa >> 16) != (calc->flash.progaddr >> 16)) + WARN("reading from Flash while erasing"); + value = calc->flash.toggles; + calc->flash.toggles ^= 0x44; + return (value); + } + + if (calc->flash.state == FLASH_ERROR) { + value = ((~calc->flash.progbyte & 0x80) | 0x20); + value |= calc->flash.toggles; + calc->flash.toggles ^= 0x40; + return (value); + } + else if (calc->flash.state == FLASH_FASTMODE) { + return (calc->mem[pa]); + } + else if (calc->flash.state == FLASH_READ) { + return (calc->mem[pa]); + } + else { + WARN("reading during program/erase sequence"); + calc->flash.state = FLASH_READ; + return (calc->mem[pa]); + } +} + +void tilem_flash_erase_address(TilemCalc* calc, dword pa) +{ + const TilemFlashSector* sec = get_sector(calc, pa); + + if (sector_writable(calc, sec)) { + tilem_message(calc, "Erasing Flash sector at %06x", pa); + erase_sector(calc, sec->start, sec->size); + } + else { + WARN("erasing protected sector"); + } +} + +void tilem_flash_write_byte(TilemCalc* calc, dword pa, byte v) +{ + int oldstate; + int i; + const TilemFlashSector* sec; + + if (!calc->flash.unlock) + return; + +#ifndef DISABLE_FLASH_DELAY + if (calc->flash.busy == FLASH_BUSY_PROGRAM + || calc->flash.busy == FLASH_BUSY_ERASE) + return; +#endif + + oldstate = calc->flash.state; + calc->flash.state = FLASH_READ; + + switch (oldstate) { + case FLASH_READ: + if (((pa&0xFFF) == 0xAAA) && (v == 0xAA)) + calc->flash.state = FLASH_AA; + return; + + case FLASH_AA: + if (((pa&0xFFF) == 0x555) && (v == 0x55)) + calc->flash.state = FLASH_55; + else if (v != 0xF0) { + WARN2("undefined command %02x->%06x after AA", v, pa); + } + return; + + case FLASH_55: + if ((pa&0xFFF) == 0xAAA) { + switch (v) { + case 0x10: + case 0x30: + WARN("attempt to erase without pre-erase"); + return; + case 0x20: + //WARN("entering fast mode"); + calc->flash.state = FLASH_FASTMODE; + return; + case 0x80: + calc->flash.state = FLASH_ERASE; + return; + case 0x90: + WARN("autoselect is not implemented"); + return; + case 0xA0: + calc->flash.state = FLASH_PROG; + return; + } + } + if (v != 0xF0) + WARN2("undefined command %02x->%06x after AA,55", v, pa); + return; + + case FLASH_PROG: + sec = get_sector(calc, pa); + if (!sector_writable(calc, sec)) + WARN("programming protected sector"); + else + program_byte(calc, pa, v); + return; + + case FLASH_FASTMODE: + //WARN2("fast mode cmd %02x->%06x", v, pa); + if ( v == 0x90 ) + calc->flash.state = FLASH_FASTEXIT; + else if ( v == 0xA0 ) + calc->flash.state = FLASH_FASTPROG; + else + // TODO : figure out whether mixing is allowed on real HW + WARN2("mixing fast programming with regular programming : %02x->%06x", v, pa); + return; + + case FLASH_FASTPROG: + //WARN2("fast prog %02x->%06x", v, pa); + sec = get_sector(calc, pa); + if (!sector_writable(calc, sec)) + WARN("programming protected sector"); + else + program_byte(calc, pa, v); + calc->flash.state = FLASH_FASTMODE; + return; + + case FLASH_FASTEXIT: + //WARN("leaving fast mode"); + if ( v != 0xF0 ) + { + WARN2("undefined command %02x->%06x after fast mode pre-exit 90", v, pa); + // TODO : figure out whether fast mode remains in such a case + calc->flash.state = FLASH_FASTMODE; + } + return; + + case FLASH_ERASE: + if (((pa&0xFFF) == 0xAAA) && (v == 0xAA)) + calc->flash.state = FLASH_ERAA; + else if (v != 0xF0) + WARN2("undefined command %02x->%06x after pre-erase", v, pa); + return; + + case FLASH_ERAA: + if (((pa&0xFFF) == 0x555) && (v == 0x55)) + calc->flash.state = FLASH_ER55; + else if (v != 0xF0) + WARN2("undefined command %02x->%06x after pre-erase AA", v, pa); + return; + + case FLASH_ER55: + if (((pa&0xFFF) == 0xAAA) && v==0x10) { + tilem_message(calc, "Erasing entire Flash chip"); + + for (i = 0; i < calc->hw.nflashsectors; i++) { + sec = &calc->hw.flashsectors[i]; + if (sector_writable(calc, sec)) + erase_sector(calc, sec->start, + sec->size); + } + } + else if (v == 0x30) { + tilem_flash_erase_address(calc, pa); + } + else if (v != 0xF0) + WARN2("undefined command %02x->%06x after pre-erase AA,55", v, pa); + return; + } +} diff --git a/tool/tilem-src/emu/graycolor.c b/tool/tilem-src/emu/graycolor.c new file mode 100644 index 0000000..5864c0b --- /dev/null +++ b/tool/tilem-src/emu/graycolor.c @@ -0,0 +1,90 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2010 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 +#include "tilem.h" + +dword* tilem_color_palette_new(int rlight, int glight, int blight, + int rdark, int gdark, int bdark, + double gamma) +{ + dword* pal = tilem_new_atomic(dword, 256); + double r0, g0, b0, dr, dg, db; + double igamma = 1.0 / gamma; + double s = (1.0 / 255.0); + int r, g, b, i; + + r0 = pow(rlight * s, gamma); + g0 = pow(glight * s, gamma); + b0 = pow(blight * s, gamma); + dr = (pow(rdark * s, gamma) - r0) * s; + dg = (pow(gdark * s, gamma) - g0) * s; + db = (pow(bdark * s, gamma) - b0) * s; + + pal[0] = (rlight << 16) | (glight << 8) | blight; + + for (i = 1; i < 255; i++) { + r = pow(r0 + i * dr, igamma) * 255.0 + 0.5; + if (r < 0) r = 0; + if (r > 255) r = 255; + + g = pow(g0 + i * dg, igamma) * 255.0 + 0.5; + if (g < 0) g = 0; + if (g > 255) g = 255; + + b = pow(b0 + i * db, igamma) * 255.0 + 0.5; + if (b < 0) b = 0; + if (b > 255) b = 255; + + pal[i] = (r << 16) | (g << 8) | b; + } + + pal[255] = (rdark << 16) | (gdark << 8) | bdark; + + return pal; +} + +byte* tilem_color_palette_new_packed(int rlight, int glight, int blight, + int rdark, int gdark, int bdark, + double gamma) +{ + dword* palette; + byte* packed; + int i; + + palette = tilem_color_palette_new(rlight, glight, blight, + rdark, gdark, bdark, gamma); + + packed = tilem_new_atomic(byte, 256 * 3); + for (i = 0; i < 256; i++) { + packed[i * 3] = palette[i] >> 16; + packed[i * 3 + 1] = palette[i] >> 8; + packed[i * 3 + 2] = palette[i]; + } + + tilem_free(palette); + + return packed; +} diff --git a/tool/tilem-src/emu/grayimage.c b/tool/tilem-src/emu/grayimage.c new file mode 100644 index 0000000..46ee275 --- /dev/null +++ b/tool/tilem-src/emu/grayimage.c @@ -0,0 +1,324 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2010-2011 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 +#include "tilem.h" + +/* Scale the input buffer, multiply by F * INCOUNT, and add to the + output buffer (which must be an exact multiple of the size of the + input buffer.) */ +static inline void add_scale1d_exact(const byte * restrict in, int incount, + unsigned int * restrict out, + int outcount, int f) +{ + int i, j; + + for (i = 0; i < incount; i++) { + for (j = 0; j < outcount / incount; j++) { + *out += *in * f * incount; + out++; + } + in++; + } +} + +/* Scale a 1-dimensional buffer, multiply by F * INCOUNT, and add to + the output buffer. */ +static inline void add_scale1d_smooth(const byte * restrict in, int incount, + unsigned int * restrict out, + int outcount, int f) +{ + int in_rem, out_rem; + unsigned int outv; + int i; + + in_rem = outcount; + out_rem = incount; + outv = 0; + i = outcount; + while (i > 0) { + if (in_rem < out_rem) { + out_rem -= in_rem; + outv += in_rem * *in * f; + in++; + in_rem = outcount; + } + else { + in_rem -= out_rem; + outv += out_rem * *in * f; + *out += outv; + outv = 0; + out++; + out_rem = incount; + i--; + } + } +} + +/* Scale a 2-dimensional buffer, multiply by INWIDTH * INHEIGHT, and + store in the output buffer. */ +static void scale2d_smooth(const byte * restrict in, + int inwidth, int inheight, int inrowstride, + unsigned int * restrict out, + int outwidth, int outheight, int outrowstride) +{ + int in_rem, out_rem; + int i; + + memset(out, 0, outrowstride * outheight * sizeof(int)); + + in_rem = outheight; + out_rem = inheight; + i = outheight; + while (i > 0) { + if (in_rem < out_rem) { + if (in_rem) { + if (outwidth % inwidth) + add_scale1d_smooth(in, inwidth, out, + outwidth, in_rem); + else + add_scale1d_exact(in, inwidth, out, + outwidth, in_rem); + } + out_rem -= in_rem; + in += inrowstride; + in_rem = outheight; + } + else { + in_rem -= out_rem; + if (outwidth % inwidth) + add_scale1d_smooth(in, inwidth, out, outwidth, + out_rem); + else + add_scale1d_exact(in, inwidth, out, outwidth, + out_rem); + out += outrowstride; + out_rem = inheight; + i--; + } + } +} + +/* Quickly scale a 1-dimensional buffer and store in the output + buffer. */ +static inline void scale1d_fast(const byte * restrict in, int incount, + byte * restrict out, int outcount) +{ + int i, e; + + e = outcount - incount / 2; + i = outcount; + while (i > 0) { + if (e >= 0) { + *out = *in; + out++; + e -= incount; + i--; + } + else { + e += outcount; + in++; + } + } +} + +/* Quickly scale a 2-dimensional buffer and store in the output + buffer. */ +static void scale2d_fast(const byte * restrict in, + int inwidth, int inheight, int inrowstride, + byte * restrict out, + int outwidth, int outheight, int outrowstride) +{ + int i, e; + + e = outheight - inheight / 2; + i = outheight; + while (i > 0) { + if (e >= 0) { + scale1d_fast(in, inwidth, out, outwidth); + out += outrowstride; + e -= inheight; + i--; + } + else { + e += outheight; + in += inrowstride; + } + } +} + +/* Determine range of linear pixel values corresponding to a given + contrast level. */ +static void get_contrast_settings(unsigned int contrast, + int *cbase, int *cfact) +{ + if (contrast < 32) { + *cbase = 0; + *cfact = contrast * 8; + } + else { + *cbase = (contrast - 32) * 8; + *cfact = 255 - *cbase; + } +} + +#define GETSCALEBUF(ttt, www, hhh) \ + ((ttt *) alloc_scalebuf(buf, (www) * (hhh) * sizeof(ttt))) + +static void* alloc_scalebuf(TilemLCDBuffer *buf, unsigned int size) +{ + if (TILEM_UNLIKELY(size > buf->tmpbufsize)) { + buf->tmpbufsize = size; + tilem_free(buf->tmpbuf); + buf->tmpbuf = tilem_malloc_atomic(size); + } + + return buf->tmpbuf; +} + +void tilem_draw_lcd_image_indexed(TilemLCDBuffer * restrict buf, + byte * restrict buffer, + int imgwidth, int imgheight, + int rowstride, int scaletype) +{ + int dwidth = buf->width; + int dheight = buf->height; + int i, j, v; + unsigned int * restrict ibuf; + int cbase, cfact; + byte cindex[129]; + + if (dwidth == 0 || dheight == 0 || buf->contrast == 0) { + for (i = 0; i < imgheight; i++) { + for (j = 0; j < imgwidth; j++) + buffer[j] = 0; + buffer += rowstride; + } + return; + } + + get_contrast_settings(buf->contrast, &cbase, &cfact); + + for (i = 0; i <= 128; i++) + cindex[i] = ((i * cfact) >> 7) + cbase; + + if (scaletype == TILEM_SCALE_FAST + || (imgwidth % dwidth == 0 && imgheight % dheight == 0)) { + scale2d_fast(buf->data, dwidth, dheight, buf->rowstride, + buffer, imgwidth, imgheight, rowstride); + + for (i = 0; i < imgwidth * imgheight; i++) + buffer[i] = cindex[buffer[i]]; + } + else { + ibuf = GETSCALEBUF(unsigned int, imgwidth, imgheight); + + scale2d_smooth(buf->data, dwidth, dheight, buf->rowstride, + ibuf, imgwidth, imgheight, imgwidth); + + for (i = 0; i < imgheight; i++) { + for (j = 0; j < imgwidth; j++) { + v = ibuf[j] / (dwidth * dheight); + buffer[j] = cindex[v]; + } + ibuf += imgwidth; + buffer += rowstride; + } + } +} + +void tilem_draw_lcd_image_rgb(TilemLCDBuffer * restrict buf, + byte * restrict buffer, + int imgwidth, int imgheight, int rowstride, + int pixbytes, const dword * restrict palette, + int scaletype) +{ + int dwidth = buf->width; + int dheight = buf->height; + int i, j, v; + int padbytes = rowstride - (imgwidth * pixbytes); + byte * restrict bbuf; + unsigned int * restrict ibuf; + int cbase, cfact; + dword cpalette[129]; + + if (dwidth == 0 || dheight == 0 || buf->contrast == 0) { + for (i = 0; i < imgheight; i++) { + for (j = 0; j < imgwidth; j++) { + buffer[0] = palette[0] >> 16; + buffer[1] = palette[0] >> 8; + buffer[2] = palette[0]; + buffer += pixbytes; + } + buffer += padbytes; + } + return; + } + + get_contrast_settings(buf->contrast, &cbase, &cfact); + + for (i = 0; i <= 128; i++) { + v = ((i * cfact) >> 7) + cbase; + cpalette[i] = palette[v]; + } + + if (scaletype == TILEM_SCALE_FAST + || (imgwidth % dwidth == 0 && imgheight % dheight == 0)) { + bbuf = GETSCALEBUF(byte, imgwidth, imgheight); + + scale2d_fast(buf->data, dwidth, dheight, buf->rowstride, + bbuf, imgwidth, imgheight, imgwidth); + + for (i = 0; i < imgheight; i++) { + for (j = 0; j < imgwidth; j++) { + v = bbuf[j]; + buffer[0] = cpalette[v] >> 16; + buffer[1] = cpalette[v] >> 8; + buffer[2] = cpalette[v]; + buffer += pixbytes; + } + bbuf += imgwidth; + buffer += padbytes; + } + } + else { + ibuf = GETSCALEBUF(unsigned int, imgwidth, imgheight); + + scale2d_smooth(buf->data, dwidth, dheight, buf->rowstride, + ibuf, imgwidth, imgheight, imgwidth); + + for (i = 0; i < imgheight; i++) { + for (j = 0; j < imgwidth; j++) { + v = (ibuf[j] / (dwidth * dheight)); + buffer[0] = cpalette[v] >> 16; + buffer[1] = cpalette[v] >> 8; + buffer[2] = cpalette[v]; + buffer += pixbytes; + } + ibuf += imgwidth; + buffer += padbytes; + } + } +} diff --git a/tool/tilem-src/emu/graylcd.c b/tool/tilem-src/emu/graylcd.c new file mode 100644 index 0000000..d27ddcc --- /dev/null +++ b/tool/tilem-src/emu/graylcd.c @@ -0,0 +1,263 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2010-2011 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 +#include "tilem.h" +#include "graylcd.h" + +/* Read screen contents and update pixels that have changed */ +static void tmr_screen_update(TilemCalc *calc, void *data) +{ + TilemGrayLCD *glcd = data; + byte *np, *op, nb, ob, d; + int i, j, n; + dword delta; + + glcd->t++; + + if (calc->z80.lastlcdwrite == glcd->lcdupdatetime) + return; + glcd->lcdupdatetime = calc->z80.lastlcdwrite; + + (*calc->hw.get_lcd)(calc, glcd->newbits); + + np = glcd->newbits; + op = glcd->oldbits; + glcd->oldbits = np; + glcd->newbits = op; + n = 0; + + for (i = 0; i < glcd->bwidth * glcd->height; i++) { + nb = *np; + ob = *op; + d = nb ^ ob; + for (j = 0; j < 8; j++) { + if (d & (0x80 >> j)) { + delta = glcd->t - glcd->tchange[n]; + glcd->tchange[n] = glcd->t; + + if (ob & (0x80 >> j)) { + glcd->curpixels[n].ndark += delta; + glcd->curpixels[n].ndarkseg++; + } + else { + glcd->curpixels[n].nlight += delta; + glcd->curpixels[n].nlightseg++; + } + } + n++; + } + + np++; + op++; + } +} + +TilemGrayLCD* tilem_gray_lcd_new(TilemCalc *calc, int windowsize, int sampleint) +{ + TilemGrayLCD *glcd = tilem_new(TilemGrayLCD, 1); + int nbytes, npixels, i; + + glcd->bwidth = (calc->hw.lcdwidth + 7) / 8; + glcd->height = calc->hw.lcdheight; + nbytes = glcd->bwidth * glcd->height; + npixels = nbytes * 8; + + glcd->oldbits = tilem_new_atomic(byte, nbytes); + glcd->newbits = tilem_new_atomic(byte, nbytes); + glcd->tchange = tilem_new_atomic(dword, npixels); + glcd->tframestart = tilem_new_atomic(dword, windowsize); + glcd->framestamp = tilem_new_atomic(dword, windowsize); + glcd->curpixels = tilem_new_atomic(TilemGrayLCDPixel, npixels); + glcd->framebasepixels = tilem_new_atomic(TilemGrayLCDPixel, + npixels * windowsize); + + memset(glcd->oldbits, 0, nbytes); + memset(glcd->tchange, 0, npixels * sizeof(dword)); + memset(glcd->tframestart, 0, windowsize * sizeof(dword)); + memset(glcd->curpixels, 0, npixels * sizeof(TilemGrayLCDPixel)); + memset(glcd->framebasepixels, 0, (npixels * windowsize + * sizeof(TilemGrayLCDPixel))); + + glcd->calc = calc; + glcd->timer_id = tilem_z80_add_timer(calc, sampleint / 2, sampleint, 1, + &tmr_screen_update, glcd); + + /* assign arbitrary but unique timestamps to the initial n + frames */ + for (i = 0; i < windowsize; i++) + glcd->framestamp[i] = calc->z80.lastlcdwrite - i; + + glcd->lcdupdatetime = calc->z80.lastlcdwrite - 1; + glcd->t = 0; + glcd->windowsize = windowsize; + glcd->sampleint = sampleint; + glcd->framenum = 0; + + return glcd; +} + +void tilem_gray_lcd_free(TilemGrayLCD *glcd) +{ + tilem_z80_remove_timer(glcd->calc, glcd->timer_id); + + tilem_free(glcd->oldbits); + tilem_free(glcd->newbits); + tilem_free(glcd->tchange); + tilem_free(glcd->tframestart); + tilem_free(glcd->framestamp); + tilem_free(glcd->curpixels); + tilem_free(glcd->framebasepixels); + tilem_free(glcd); +} + +/* Update levelbuf with values based on the accumulated grayscale + data */ +void tilem_gray_lcd_get_frame(TilemGrayLCD * restrict glcd, + TilemLCDBuffer * restrict buf) +{ + int i, j, n; + unsigned int current, delta, fd, fl; + word ndark, nlight, ndarkseg, nlightseg; + dword tbase, tlimit; + dword lastwrite; + byte * restrict bp; + byte * restrict op; + TilemGrayLCDPixel * restrict pix; + TilemGrayLCDPixel * restrict basepix; + dword * restrict tchange; + + if (TILEM_UNLIKELY(buf->height != glcd->height + || buf->rowstride != glcd->bwidth * 8)) { + /* reallocate data buffer */ + tilem_free(buf->data); + buf->data = tilem_new_atomic(byte, + glcd->height * glcd->bwidth * 8); + buf->rowstride = glcd->bwidth * 8; + buf->height = glcd->height; + } + + buf->width = glcd->calc->hw.lcdwidth; + + if (!glcd->calc->lcd.active + || (glcd->calc->z80.halted && !glcd->calc->poweronhalt)) { + /* screen is turned off */ + buf->stamp = glcd->calc->z80.lastlcdwrite; + buf->contrast = 0; + return; + } + + buf->contrast = glcd->calc->lcd.contrast; + + /* If LCD remains unchanged throughout the window, set + timestamp to the time when the LCD was last changed, so + that consecutive frames have the same timestamp. If LCD + has changed during the window, values of gray pixels will + vary from one frame to another, so use a unique timestamp + for each frame */ + lastwrite = glcd->calc->z80.lastlcdwrite; + if (glcd->framestamp[glcd->framenum] == lastwrite) + buf->stamp = lastwrite; + else + buf->stamp = glcd->calc->z80.clock + 0x80000000; + glcd->framestamp[glcd->framenum] = lastwrite; + + /* set tbase to the sample number where the window began; this + is used to limit the weight of unchanging pixels */ + tbase = glcd->tframestart[glcd->framenum]; + glcd->tframestart[glcd->framenum] = glcd->t; + tlimit = glcd->t - tbase; /* number of samples per window */ + + bp = glcd->newbits; + op = buf->data; + pix = glcd->curpixels; + basepix = glcd->framebasepixels + (glcd->framenum * glcd->height + * glcd->bwidth * 8); + tchange = glcd->tchange; + + (*glcd->calc->hw.get_lcd)(glcd->calc, bp); + + n = 0; + + for (i = 0; i < glcd->bwidth * glcd->height; i++) { + for (j = 0; j < 8; j++) { + /* check if pixel is currently set */ + current = *bp & (0x80 >> j); + + /* compute number of dark and light samples + within the window */ + ndark = pix[n].ndark - basepix[n].ndark; + nlight = pix[n].nlight - basepix[n].nlight; + + /* compute number of dark and light segments + within the window */ + ndarkseg = pix[n].ndarkseg - basepix[n].ndarkseg; + nlightseg = pix[n].nlightseg - basepix[n].nlightseg; + + /* average light segment in this window is + (nlight / nlightseg); average dark segment + is (ndark / ndarkseg) */ + + /* ensure tchange is later than or equal to tbase */ + if (tchange[n] - tbase > tlimit) { + tchange[n] = tbase; + } + + /* if current segment is longer than average, + count it as well */ + delta = glcd->t - tchange[n]; + + if (current) { + if (delta * ndarkseg >= ndark) { + ndark += delta; + ndarkseg++; + } + } + else { + if (delta * nlightseg >= nlight) { + nlight += delta; + nlightseg++; + } + } + + fd = ndark * nlightseg; + fl = nlight * ndarkseg; + + if (fd + fl == 0) + *op = (ndark ? 128 : 0); + else + *op = ((fd * 128) / (fd + fl)); + + n++; + op++; + } + bp++; + } + + memcpy(basepix, pix, (glcd->height * glcd->bwidth * 8 + * sizeof(TilemGrayLCDPixel))); + + glcd->framenum = (glcd->framenum + 1) % glcd->windowsize; +} diff --git a/tool/tilem-src/emu/graylcd.h b/tool/tilem-src/emu/graylcd.h new file mode 100644 index 0000000..dbbf8da --- /dev/null +++ b/tool/tilem-src/emu/graylcd.h @@ -0,0 +1,57 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2010-2011 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 + * . + */ + +#ifndef _TILEM_GRAYLCD_H +#define _TILEM_GRAYLCD_H + +typedef struct _TilemGrayLCDPixel { + word ndark; /* Sum of lengths of dark intervals */ + word nlight; /* Sum of lengths of light intervals */ + word ndarkseg; /* Number of dark intervals */ + word nlightseg; /* Number of light intervals */ +} TilemGrayLCDPixel; + +struct _TilemGrayLCD { + TilemCalc *calc; /* Calculator */ + int timer_id; /* Screen update timer */ + dword lcdupdatetime; /* CPU time of last known LCD update */ + + dword t; /* Time counter */ + int windowsize; /* Number of frames in the sampling + window */ + int framenum; /* Current frame number */ + int sampleint; /* Microseconds per sample */ + + int bwidth; /* Width of LCD, bytes */ + int height; /* Height of LCD, pixels */ + byte *oldbits; /* Original pixel values (current buffer) */ + byte *newbits; /* Original pixel values (alternate buffer) */ + + dword *tchange; /* Time when pixels changed */ + dword *tframestart; /* Time at start of frame */ + dword *framestamp; /* LCD update time at start of frame */ + + TilemGrayLCDPixel *curpixels; /* Current pixel counters */ + TilemGrayLCDPixel *framebasepixels; /* Pixel counters as of + the start of each + frame */ +}; + +#endif diff --git a/tool/tilem-src/emu/keypad.c b/tool/tilem-src/emu/keypad.c new file mode 100644 index 0000000..37953e7 --- /dev/null +++ b/tool/tilem-src/emu/keypad.c @@ -0,0 +1,91 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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" +#include "scancodes.h" + +void tilem_keypad_reset(TilemCalc* calc) +{ + int i; + + calc->keypad.group = 0xff; + calc->keypad.onkeydown = 0; + calc->keypad.onkeyint = 0; + for (i = 0; i < 8; i++) + calc->keypad.keysdown[i] = 0; +} + +void tilem_keypad_set_group(TilemCalc* calc, byte group) +{ + calc->keypad.group = group; +} + +byte tilem_keypad_read_keys(TilemCalc* calc) +{ + int i; + byte keys, old; + + keys = 0; + for (i = 0; i < 8; i++) { + if (!(calc->keypad.group & (1 << i))) + keys |= calc->keypad.keysdown[i]; + } + + do { + old = keys; + for (i = 0; i < 8; i++) { + if (keys & calc->keypad.keysdown[i]) + keys |= calc->keypad.keysdown[i]; + } + } while (keys != old); + + return ~keys; +} + +void tilem_keypad_press_key(TilemCalc* calc, int scancode) +{ + if (scancode == TILEM_KEY_ON) { + if (!calc->keypad.onkeydown && calc->keypad.onkeyint) + calc->z80.interrupts |= TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeydown = 1; + } + else if (scancode > 0 && scancode < 65) { + scancode--; + calc->keypad.keysdown[scancode / 8] |= (1 << (scancode % 8)); + } +} + +void tilem_keypad_release_key(TilemCalc* calc, int scancode) +{ + if (scancode == TILEM_KEY_ON) { + if (calc->keypad.onkeydown && calc->keypad.onkeyint) + calc->z80.interrupts |= TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeydown = 0; + } + else if (scancode > 0 && scancode < 65) { + scancode--; + calc->keypad.keysdown[scancode / 8] &= ~(1 << (scancode % 8)); + } +} diff --git a/tool/tilem-src/emu/lcd.c b/tool/tilem-src/emu/lcd.c new file mode 100644 index 0000000..1820805 --- /dev/null +++ b/tool/tilem-src/emu/lcd.c @@ -0,0 +1,268 @@ +/* + * 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; + } +} diff --git a/tool/tilem-src/emu/link.c b/tool/tilem-src/emu/link.c new file mode 100644 index 0000000..0bd5ca9 --- /dev/null +++ b/tool/tilem-src/emu/link.c @@ -0,0 +1,456 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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" + +/* Internal link port control */ + +static inline void dbus_interrupt(TilemCalc* calc, dword inttype, + dword mask) +{ + if (!(calc->linkport.mode & mask)) + return; + + calc->z80.interrupts |= inttype; +} + +static inline void dbus_set_lines(TilemCalc* calc, byte lines) +{ + if (lines != calc->linkport.lines) { + calc->linkport.lines = lines; + if (calc->linkport.linkemu == TILEM_LINK_EMULATOR_BLACK) { + tilem_z80_stop(calc, TILEM_STOP_LINK_STATE); + } + } +} + +static inline void dbus_set_extlines(TilemCalc* calc, byte lines) +{ + if ((lines ^ calc->linkport.extlines) & ~calc->linkport.lines) { + dbus_interrupt(calc, TILEM_INTERRUPT_LINK_ACTIVE, + TILEM_LINK_MODE_INT_ON_ACTIVE); + } + calc->linkport.extlines = lines; +} + +void tilem_linkport_assist_timer(TilemCalc* calc, + void* data TILEM_ATTR_UNUSED) +{ + TilemLinkport* lp = &calc->linkport; + + if (lp->assistflags & TILEM_LINK_ASSIST_WRITE_BUSY) { + lp->assistflags &= ~TILEM_LINK_ASSIST_WRITE_BUSY; + lp->assistflags |= TILEM_LINK_ASSIST_WRITE_ERROR; + } + else if (lp->assistflags & TILEM_LINK_ASSIST_READ_BUSY) { + lp->assistflags &= ~TILEM_LINK_ASSIST_READ_BUSY; + lp->assistflags |= TILEM_LINK_ASSIST_READ_ERROR; + } + else + return; + + dbus_interrupt(calc, TILEM_INTERRUPT_LINK_ERROR, + TILEM_LINK_MODE_INT_ON_ERROR); +} + +static inline void assist_set_timeout(TilemCalc* calc) +{ + if (calc->linkport.mode & TILEM_LINK_MODE_NO_TIMEOUT) + return; + + tilem_z80_set_timer(calc, TILEM_TIMER_LINK_ASSIST, 2000000, 0, 1); +} + +static inline void assist_clear_timeout(TilemCalc* calc) +{ + tilem_z80_set_timer(calc, TILEM_TIMER_LINK_ASSIST, 0, 0, 0); +} + +static void assist_update_write(TilemCalc* calc) +{ + switch (calc->linkport.extlines) { + case 0: + if (calc->linkport.lines == 0 && calc->linkport.assistoutbits > 0) { + /* Ready to send next bit */ + if (calc->linkport.assistout & 1) + dbus_set_lines(calc, 2); + else + dbus_set_lines(calc, 1); + calc->linkport.assistout >>= 1; + calc->linkport.assistoutbits--; + assist_set_timeout(calc); /* other device must + respond within 2 + seconds */ + } + else if (calc->linkport.lines == 0) { + /* Finished sending a byte */ + calc->linkport.assistflags &= ~TILEM_LINK_ASSIST_WRITE_BUSY; + assist_clear_timeout(calc); + } + break; + + case 1: + case 2: + if (calc->linkport.extlines == (calc->linkport.lines ^ 3)) { + /* Other device acknowledged our bit. Note + that the timeout is NOT set at this point. + My experiments indicate that the assist + will wait, apparently indefinitely, for the + other device to bring its lines high. */ + dbus_set_lines(calc, 0); + assist_clear_timeout(calc); + } + break; + + case 3: + /* illegal line state; flag error */ + calc->linkport.assistflags &= ~TILEM_LINK_ASSIST_WRITE_BUSY; + calc->linkport.assistflags |= TILEM_LINK_ASSIST_WRITE_ERROR; + dbus_set_lines(calc, 0); + assist_clear_timeout(calc); + dbus_interrupt(calc, TILEM_INTERRUPT_LINK_ERROR, + TILEM_LINK_MODE_INT_ON_ERROR); + break; + } +} + +static void assist_update_read(TilemCalc* calc) +{ + switch (calc->linkport.extlines) { + case 0: + /* Finished receiving a bit */ + if (calc->linkport.lines == 1) { + calc->linkport.assistin >>= 1; + calc->linkport.assistin |= 0x80; + calc->linkport.assistinbits++; + } + else if (calc->linkport.lines == 2) { + calc->linkport.assistin >>= 1; + calc->linkport.assistinbits++; + } + + if (calc->linkport.assistinbits >= 8) { + /* finished receiving a byte */ + calc->linkport.assistlastbyte = calc->linkport.assistin; + calc->linkport.assistflags &= ~TILEM_LINK_ASSIST_READ_BUSY; + calc->linkport.assistflags |= TILEM_LINK_ASSIST_READ_BYTE; + assist_clear_timeout(calc); + } + else { + assist_set_timeout(calc); /* other device must + send next bit + within 2 + seconds */ + } + + dbus_set_lines(calc, 0); /* indicate we're ready to + receive */ + break; + + case 1: + /* other device sent a zero; acknowledge it */ + calc->linkport.assistflags |= TILEM_LINK_ASSIST_READ_BUSY; + dbus_set_lines(calc, 2); + assist_set_timeout(calc); /* other device must bring + both lines high again + within 2 seconds */ + break; + + case 2: + /* same as above, but other device sent a one */ + calc->linkport.assistflags |= TILEM_LINK_ASSIST_READ_BUSY; + dbus_set_lines(calc, 1); + assist_set_timeout(calc); /* other device must bring + both lines high again + within 2 seconds */ + break; + + case 3: + /* illegal line state; flag error */ + calc->linkport.assistflags &= ~TILEM_LINK_ASSIST_READ_BUSY; + calc->linkport.assistflags |= TILEM_LINK_ASSIST_READ_ERROR; + dbus_set_lines(calc, 0); + assist_clear_timeout(calc); + dbus_interrupt(calc, TILEM_INTERRUPT_LINK_ERROR, + TILEM_LINK_MODE_INT_ON_ERROR); + break; + } +} + +static void graylink_update_write(TilemCalc* calc) +{ + switch (calc->linkport.lines) { + case 0: + if (calc->linkport.extlines == 0 && calc->linkport.graylinkoutbits > 1) { + /* Ready to send next bit */ + if (calc->linkport.graylinkout & 1) + dbus_set_extlines(calc, 2); + else + dbus_set_extlines(calc, 1); + calc->linkport.graylinkout >>= 1; + calc->linkport.graylinkoutbits--; + } + else if (calc->linkport.extlines == 0) { + /* Finished sending a byte */ + calc->linkport.graylinkoutbits = 0; + tilem_z80_stop(calc, TILEM_STOP_LINK_WRITE_BYTE); + } + break; + + case 1: + case 2: + if (calc->linkport.extlines == (calc->linkport.lines ^ 3)) + /* Other device acknowledged our bit */ + dbus_set_extlines(calc, 0); + break; + + case 3: + /* illegal line state; flag error */ + dbus_set_extlines(calc, 0); + calc->linkport.graylinkoutbits = 0; + tilem_z80_stop(calc, TILEM_STOP_LINK_ERROR); + break; + } +} + +static void graylink_update_read(TilemCalc* calc) +{ + switch (calc->linkport.lines) { + case 0: + /* Finished receiving a bit */ + if (calc->linkport.extlines == 1) { + calc->linkport.graylinkin >>= 1; + calc->linkport.graylinkin |= 0x80; + calc->linkport.graylinkinbits++; + } + else if (calc->linkport.extlines == 2) { + calc->linkport.graylinkin >>= 1; + calc->linkport.graylinkinbits++; + } + + if (calc->linkport.graylinkinbits >= 8) { + /* finished receiving a byte */ + tilem_z80_stop(calc, TILEM_STOP_LINK_READ_BYTE); + } + + dbus_set_extlines(calc, 0); + break; + + case 1: + /* other device sent a zero; acknowledge it */ + dbus_set_extlines(calc, 2); + break; + + case 2: + /* same as above, but other device sent a one */ + dbus_set_extlines(calc, 1); + break; + + case 3: + /* illegal line state; flag error */ + dbus_set_extlines(calc, 0); + calc->linkport.graylinkinbits = 0; + tilem_z80_stop(calc, TILEM_STOP_LINK_ERROR); + break; + } +} + +static void dbus_update(TilemCalc* calc) +{ + byte oldlines; + + do { + if (calc->linkport.linkemu == TILEM_LINK_EMULATOR_GRAY) { + if (calc->linkport.graylinkoutbits) { + graylink_update_write(calc); + } + else if (calc->linkport.graylinkinbits != 8) { + graylink_update_read(calc); + } + } + + oldlines = calc->linkport.lines; + if (calc->linkport.assistflags & TILEM_LINK_ASSIST_WRITE_BUSY) { + assist_update_write(calc); + } + else if (calc->linkport.mode & TILEM_LINK_MODE_ASSIST + && !(calc->linkport.assistflags & TILEM_LINK_ASSIST_READ_BYTE)) { + assist_update_read(calc); + } + } while (oldlines != calc->linkport.lines); + + if ((calc->linkport.assistflags & TILEM_LINK_ASSIST_READ_BYTE) + && (calc->linkport.mode & TILEM_LINK_MODE_INT_ON_READ)) + calc->z80.interrupts |= TILEM_INTERRUPT_LINK_READ; + else + calc->z80.interrupts &= ~TILEM_INTERRUPT_LINK_READ; + + if (!(calc->linkport.assistflags & (TILEM_LINK_ASSIST_READ_BUSY + | TILEM_LINK_ASSIST_WRITE_BUSY)) + && (calc->linkport.mode & TILEM_LINK_MODE_INT_ON_IDLE)) + calc->z80.interrupts |= TILEM_INTERRUPT_LINK_IDLE; + else + calc->z80.interrupts &= ~TILEM_INTERRUPT_LINK_IDLE; +} + +void tilem_linkport_reset(TilemCalc* calc) +{ + dbus_set_lines(calc, 0); + assist_clear_timeout(calc); + calc->linkport.mode = 0; + calc->linkport.assistflags = 0; + calc->linkport.assistin = 0; + calc->linkport.assistinbits = 0; + calc->linkport.assistout = 0; + calc->linkport.assistoutbits = 0; + calc->linkport.assistlastbyte = 0; +} + +byte tilem_linkport_get_lines(TilemCalc* calc) +{ + //dbus_update(calc); + return (~calc->linkport.lines & ~calc->linkport.extlines & 3); +} + +void tilem_linkport_set_lines(TilemCalc* calc, byte lines) +{ + if (!(calc->linkport.mode & TILEM_LINK_MODE_ASSIST) + && !(calc->linkport.assistflags & TILEM_LINK_ASSIST_WRITE_BUSY)) + dbus_set_lines(calc, lines & 3); + + dbus_update(calc); +} + +byte tilem_linkport_read_byte(TilemCalc* calc) +{ + byte value = calc->linkport.assistin; + calc->linkport.assistflags &= ~(TILEM_LINK_ASSIST_READ_BYTE + | TILEM_LINK_ASSIST_READ_BUSY); + calc->linkport.assistinbits = 0; + dbus_update(calc); + return value; +} + +void tilem_linkport_write_byte(TilemCalc* calc, byte data) +{ + if (calc->linkport.assistflags & (TILEM_LINK_ASSIST_READ_BUSY + | TILEM_LINK_ASSIST_WRITE_BUSY)) + return; + + dbus_set_lines(calc, 0); + calc->linkport.assistout = data; + calc->linkport.assistoutbits = 8; + calc->linkport.assistflags |= TILEM_LINK_ASSIST_WRITE_BUSY; + dbus_update(calc); +} + +unsigned int tilem_linkport_get_assist_flags(TilemCalc* calc) +{ + //dbus_update(calc); + return calc->linkport.assistflags; +} + +void tilem_linkport_set_mode(TilemCalc* calc, unsigned int mode) +{ + if ((mode ^ calc->linkport.mode) & TILEM_LINK_MODE_ASSIST) { + dbus_set_lines(calc, 0); + calc->linkport.assistflags &= ~(TILEM_LINK_ASSIST_READ_BUSY + | TILEM_LINK_ASSIST_WRITE_BUSY); + assist_clear_timeout(calc); + } + + if (!(mode & TILEM_LINK_MODE_INT_ON_ACTIVE)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_LINK_ACTIVE; + if (!(mode & TILEM_LINK_MODE_INT_ON_ERROR)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_LINK_ERROR; + + calc->linkport.mode = mode; + + dbus_update(calc); +} + + +/* External BlackLink emulation */ + +void tilem_linkport_blacklink_set_lines(TilemCalc* calc, byte lines) +{ + dbus_set_extlines(calc, lines & 3); + dbus_update(calc); +} + +byte tilem_linkport_blacklink_get_lines(TilemCalc* calc) +{ + dbus_update(calc); + return (~calc->linkport.lines & ~calc->linkport.extlines & 3); +} + + +/* External GrayLink emulation */ + +void tilem_linkport_graylink_reset(TilemCalc* calc) +{ + calc->linkport.graylinkin = 0; + calc->linkport.graylinkinbits = 0; + calc->linkport.graylinkout = 0; + calc->linkport.graylinkoutbits = 0; + dbus_set_extlines(calc, 0); + dbus_update(calc); +} + +int tilem_linkport_graylink_ready(TilemCalc* calc) +{ + if (calc->linkport.graylinkoutbits + || calc->linkport.graylinkinbits) + return 0; + else + return 1; +} + +int tilem_linkport_graylink_send_byte(TilemCalc* calc, byte value) +{ + if (!tilem_linkport_graylink_ready(calc)) + return -1; + + dbus_set_extlines(calc, 0); + + /* set to 9 because we want to wait for the calc to bring both + link lines low before we send the first bit, and also after + we send the last bit */ + calc->linkport.graylinkoutbits = 9; + + calc->linkport.graylinkout = value; + dbus_update(calc); + return 0; +} + +int tilem_linkport_graylink_get_byte(TilemCalc* calc) +{ + dbus_update(calc); + if (calc->linkport.graylinkinbits != 8) + return -1; + + calc->linkport.graylinkinbits = 0; + return calc->linkport.graylinkin; +} diff --git a/tool/tilem-src/emu/md5.c b/tool/tilem-src/emu/md5.c new file mode 100644 index 0000000..14f8996 --- /dev/null +++ b/tool/tilem-src/emu/md5.c @@ -0,0 +1,86 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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" + +void tilem_md5_assist_reset(TilemCalc* calc) +{ + int i; + + for (i = 0; i < 6; i++) + calc->md5assist.regs[i] = 0; + calc->md5assist.shift = 0; + calc->md5assist.mode = 0; +} + +dword tilem_md5_assist_get_value(TilemCalc* calc) +{ + /* Return the result of a complete MD5 operation: + b + ((a + f(b,c,d) + X + T) <<< s) */ + dword a, b, c, d, x, t, result; + byte mode, s; + + mode = calc->md5assist.mode; + a = calc->md5assist.regs[TILEM_MD5_REG_A]; + b = calc->md5assist.regs[TILEM_MD5_REG_B]; + c = calc->md5assist.regs[TILEM_MD5_REG_C]; + d = calc->md5assist.regs[TILEM_MD5_REG_D]; + x = calc->md5assist.regs[TILEM_MD5_REG_X]; + t = calc->md5assist.regs[TILEM_MD5_REG_T]; + s = calc->md5assist.shift; + + switch (mode) { + case TILEM_MD5_FUNC_FF: + /* F(X,Y,Z) = XY v not(X) Z */ + result = (b & c) | ((~b) & d); + break; + + case TILEM_MD5_FUNC_GG: + /* G(X,Y,Z) = XZ v Y not(Z) */ + result = (b & d) | (c & (~d)); + break; + + case TILEM_MD5_FUNC_HH: + /* H(X,Y,Z) = X xor Y xor Z */ + result = b ^ c ^ d; + break; + + case TILEM_MD5_FUNC_II: + /* I(X,Y,Z) = Y xor (X v not(Z)) */ + result = c ^ (b | (~d)); + break; + + default: + tilem_internal(calc, "Invalid MD5 mode %d", mode); + return 0; + } + + result += a + x + t; + result &= 0xffffffff; + result = (result << s) | (result >> (32 - s)); + result += b; + + return result; +} diff --git a/tool/tilem-src/emu/monolcd.c b/tool/tilem-src/emu/monolcd.c new file mode 100644 index 0000000..6c6f173 --- /dev/null +++ b/tool/tilem-src/emu/monolcd.c @@ -0,0 +1,148 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2011 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 +#include "tilem.h" + +TilemLCDBuffer* tilem_lcd_buffer_new() +{ + return tilem_new0(TilemLCDBuffer, 1); +} + +void tilem_lcd_buffer_free(TilemLCDBuffer *buf) +{ + tilem_free(buf->data); + tilem_free(buf->tmpbuf); + tilem_free(buf); +} + +void tilem_lcd_get_frame(TilemCalc * restrict calc, + TilemLCDBuffer * restrict buf) +{ + byte * restrict bp; + byte * restrict op; + int dwidth = calc->hw.lcdwidth; + int dheight = calc->hw.lcdheight; + unsigned int size; + int bwidth = ((calc->hw.lcdwidth + 7) / 8); + int i, j; + + if (TILEM_UNLIKELY(buf->height != dheight + || buf->rowstride != bwidth * 8)) { + /* reallocate data buffer */ + tilem_free(buf->data); + buf->data = tilem_new_atomic(byte, dwidth * bwidth * 8); + buf->rowstride = bwidth * 8; + buf->height = dheight; + } + + size = bwidth * dheight * sizeof(byte); + if (TILEM_UNLIKELY(buf->tmpbufsize < size)) { + /* reallocate temp buffer */ + tilem_free(buf->tmpbuf); + buf->tmpbuf = tilem_malloc_atomic(size); + buf->tmpbufsize = size; + } + + buf->width = dwidth; + + buf->stamp = calc->z80.lastlcdwrite; + + if (!calc->lcd.active || (calc->z80.halted && !calc->poweronhalt)) { + /* screen is turned off */ + buf->contrast = 0; + return; + } + + buf->contrast = calc->lcd.contrast; + + bp = buf->tmpbuf; + op = buf->data; + (*calc->hw.get_lcd)(calc, bp); + + for (i = 0; i < bwidth * dheight; i++) { + for (j = 0; j < 8; j++) { + *op = (*bp << j) & 0x80; + op++; + } + bp++; + } +} + +/* Do the same thing as tilem_lcd_get_frame, but output is only 0 and 1 */ +void tilem_lcd_get_frame1(TilemCalc * restrict calc, + TilemLCDBuffer * restrict buf) +{ + byte * restrict bp; + byte * restrict op; + int dwidth = calc->hw.lcdwidth; + int dheight = calc->hw.lcdheight; + unsigned int size; + int bwidth = ((calc->hw.lcdwidth + 7) / 8); + int i, j; + + if (TILEM_UNLIKELY(buf->height != dheight + || buf->rowstride != bwidth * 8)) { + /* reallocate data buffer */ + tilem_free(buf->data); + buf->data = tilem_new_atomic(byte, dwidth * bwidth * 8); + buf->rowstride = bwidth * 8; + buf->height = dheight; + } + + size = bwidth * dheight * sizeof(byte); + if (TILEM_UNLIKELY(buf->tmpbufsize < size)) { + /* reallocate temp buffer */ + tilem_free(buf->tmpbuf); + buf->tmpbuf = tilem_malloc_atomic(size); + buf->tmpbufsize = size; + } + + buf->width = dwidth; + + buf->stamp = calc->z80.lastlcdwrite; + + if (!calc->lcd.active || (calc->z80.halted && !calc->poweronhalt)) { + /* screen is turned off */ + buf->contrast = 0; + return; + } + + buf->contrast = calc->lcd.contrast; + + bp = buf->tmpbuf; + op = buf->data; + (*calc->hw.get_lcd)(calc, bp); + + for (i = 0; i < bwidth * dheight; i++) { + for (j = 0; j < 8; j++) { + *op = (*bp << j) & 0x80; + if(*op != 0) + *op = 1; + op++; + } + bp++; + } +} diff --git a/tool/tilem-src/emu/rom.c b/tool/tilem-src/emu/rom.c new file mode 100644 index 0000000..9cc24c9 --- /dev/null +++ b/tool/tilem-src/emu/rom.c @@ -0,0 +1,118 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 +#include "tilem.h" + +static int find_string(const char *str, FILE *romfile, + dword start, dword limit) +{ + char buf[256]; + int pos = 0; + int len, i; + + len = strlen(str); + + fseek(romfile, (long int) start, SEEK_SET); + + for (i = 0; i < len-1; i++) { + buf[pos] = fgetc(romfile); + pos = (pos+1)%256; + limit--; + } + + while (limit > 0 && !feof(romfile) && !ferror(romfile)) { + buf[pos] = fgetc(romfile); + pos = (pos+1)%256; + limit--; + + for (i = 0; i < len; i++) { + if (str[i] != buf[(pos + 256 - len + i)%256]) + break; + } + if (i == len) + return 1; + } + return 0; +} + +char tilem_guess_rom_type(FILE* romfile) +{ + unsigned long initpos; + dword size; + char result; + + initpos = ftell(romfile); + + fseek(romfile, 0L, SEEK_END); + size = ftell(romfile); + + if (size >= 0x8000 && size < 0x9000) { + /* 32k: TI-81 (old or new) */ + result = TILEM_CALC_TI81; + } + else if (size >= 0x20000 && size < 0x2C000) { + /* 128k: TI-82 or TI-86 */ + if (find_string("CATALOG", romfile, 0, 0x20000)) + result = TILEM_CALC_TI85; + else + result = TILEM_CALC_TI82; + } + else if (size >= 0x40000 && size < 0x4C000) { + /* 256k: TI-83 (or a variant) or TI-86 */ + if (!find_string("TI82", romfile, 0, 0x40000)) + result = TILEM_CALC_TI86; + else if (find_string("Termin\x96", romfile, 0, 0x40000)) + result = TILEM_CALC_TI76; + else + result = TILEM_CALC_TI83; + } + else if (size >= 0x80000 && size < 0x8C000) { + /* 512k: TI-83 Plus or TI-73 */ + if (find_string("TI-83 Plus", romfile, 0, 8 * 0x4000)) + result = TILEM_CALC_TI83P; + else + result = TILEM_CALC_TI73; + } + else if (size >= 0x100000 && size < 0x124000) { + /* 1024k: TI-84 Plus */ + result = TILEM_CALC_TI84P; + } + else if (size >= 0x200000 && size < 0x224000) { + /* 2048k: TI-83 Plus SE, TI-84 Plus SE */ + if (find_string("\xed\xef", romfile, 0x1FC000, 0x4000)) + result = TILEM_CALC_TI84P_NSPIRE; + else if (find_string("Operating", romfile, 0x1FC000, 0x4000)) + result = TILEM_CALC_TI84P_SE; + else + result = TILEM_CALC_TI83P_SE; + } + else { + result = 0; + } + + fseek(romfile, initpos, SEEK_SET); + return result; +} diff --git a/tool/tilem-src/emu/scancodes.h b/tool/tilem-src/emu/scancodes.h new file mode 100644 index 0000000..7662a18 --- /dev/null +++ b/tool/tilem-src/emu/scancodes.h @@ -0,0 +1,85 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#ifndef _TILEM_SCANCODES_H +#define _TILEM_SCANCODES_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + TILEM_KEY_DOWN = 0x01, + TILEM_KEY_LEFT = 0x02, + TILEM_KEY_RIGHT = 0x03, + TILEM_KEY_UP = 0x04, + TILEM_KEY_ENTER = 0x09, + TILEM_KEY_ADD = 0x0A, + TILEM_KEY_SUB = 0x0B, + TILEM_KEY_MUL = 0x0C, + TILEM_KEY_DIV = 0x0D, + TILEM_KEY_POWER = 0x0E, + TILEM_KEY_CLEAR = 0x0F, + TILEM_KEY_CHS = 0x11, + TILEM_KEY_3 = 0x12, + TILEM_KEY_6 = 0x13, + TILEM_KEY_9 = 0x14, + TILEM_KEY_RPAREN = 0x15, + TILEM_KEY_TAN = 0x16, + TILEM_KEY_VARS = 0x17, + TILEM_KEY_DECPNT = 0x19, + TILEM_KEY_2 = 0x1A, + TILEM_KEY_5 = 0x1B, + TILEM_KEY_8 = 0x1C, + TILEM_KEY_LPAREN = 0x1D, + TILEM_KEY_COS = 0x1E, + TILEM_KEY_PRGM = 0x1F, + TILEM_KEY_STAT = 0x20, + TILEM_KEY_0 = 0x21, + TILEM_KEY_1 = 0x22, + TILEM_KEY_4 = 0x23, + TILEM_KEY_7 = 0x24, + TILEM_KEY_COMMA = 0x25, + TILEM_KEY_SIN = 0x26, + TILEM_KEY_MATRIX = 0x27, + TILEM_KEY_GRAPHVAR = 0x28, + TILEM_KEY_ON = 0x29, + TILEM_KEY_STORE = 0x2A, + TILEM_KEY_LN = 0x2B, + TILEM_KEY_LOG = 0x2C, + TILEM_KEY_SQUARE = 0x2D, + TILEM_KEY_RECIP = 0x2E, + TILEM_KEY_MATH = 0x2F, + TILEM_KEY_ALPHA = 0x30, + TILEM_KEY_GRAPH = 0x31, + TILEM_KEY_TRACE = 0x32, + TILEM_KEY_ZOOM = 0x33, + TILEM_KEY_WINDOW = 0x34, + TILEM_KEY_YEQU = 0x35, + TILEM_KEY_2ND = 0x36, + TILEM_KEY_MODE = 0x37, + TILEM_KEY_DEL = 0x38 +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tool/tilem-src/emu/state.c b/tool/tilem-src/emu/state.c new file mode 100644 index 0000000..2447866 --- /dev/null +++ b/tool/tilem-src/emu/state.c @@ -0,0 +1,892 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2009-2012 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 +#include +#include "tilem.h" +#include "z80.h" + +static void set_hw_reg(TilemCalc* calc, const char* name, dword value) +{ + int i; + + for (i = 0; i < calc->hw.nhwregs; i++) { + if (!strcmp(name, calc->hw.hwregnames[i])) { + calc->hwregs[i] = value; + return; + } + } + + tilem_warning(calc, "Unknown hwreg %s", name); +} + +static const char* get_timer_name(TilemCalc* calc, int id) +{ + if (id == TILEM_TIMER_LCD_DELAY) + return "lcddelay"; + else if (id == TILEM_TIMER_FLASH_DELAY) + return "flashdelay"; + else if (id == TILEM_TIMER_LINK_ASSIST) + return "linkassist"; + else if (id == TILEM_TIMER_USER1) + return "user1"; + else if (id == TILEM_TIMER_USER2) + return "user2"; + else if (id == TILEM_TIMER_USER3) + return "user3"; + else if (id <= TILEM_NUM_SYS_TIMERS) + abort(); + + id -= TILEM_NUM_SYS_TIMERS + 1; + if (id < calc->hw.nhwtimers) + return calc->hw.hwtimernames[id]; + else + return NULL; +} + +static void set_ptimer(TilemCalc* calc, const char* name, dword value, + dword period, int rt) +{ + int i; + const char* tname; + + for (i = 1; i <= calc->z80.ntimers; i++) { + tname = get_timer_name(calc, i); + if (tname && !strcmp(name, tname)) { + tilem_z80_set_timer(calc, i, value, period, rt); + return; + } + } + + tilem_warning(calc, "Unknown timer %s", name); +} + +static int load_old_sav_file(TilemCalc* calc, FILE* savfile) +{ + byte b[76]; + dword regs[19]; + int i, le, be, c; + unsigned int pageA, pageB; + + /* Read memory mapping */ + + if (fread(calc->mempagemap, 1, 4, savfile) < 4) + return 1; + + /* Read CPU registers */ + + if (fread(b, 1, 76, savfile) < 76) + return 1; + + be = le = 0; + + /* determine if file is in big-endian or little-endian + format */ + + for (i = 0; i < 19; i++) { + if (b[i * 4] || b[i * 4 + 1]) + le++; + if (b[i * 4 + 2] || b[i * 4 + 3]) + be++; + } + + if (le > be) { + for (i = 0; i < 19; i++) { + regs[i] = b[i * 4] + (b[i * 4 + 1] << 8); + } + } + else { + for (i = 0; i < 19; i++) { + regs[i] = b[i * 4 + 3] + (b[i * 4 + 2] << 8); + } + } + + calc->z80.r.af.d = regs[0]; + calc->z80.r.bc.d = regs[1]; + calc->z80.r.de.d = regs[2]; + calc->z80.r.hl.d = regs[3]; + calc->z80.r.ix.d = regs[4]; + calc->z80.r.iy.d = regs[5]; + calc->z80.r.pc.d = regs[6]; + calc->z80.r.sp.d = regs[7]; + calc->z80.r.af2.d = regs[8]; + calc->z80.r.bc2.d = regs[9]; + calc->z80.r.de2.d = regs[10]; + calc->z80.r.hl2.d = regs[11]; + calc->z80.r.iff1 = regs[12] ? 1 : 0; + calc->z80.r.iff2 = regs[13] ? 1 : 0; + calc->z80.r.im = regs[15]; + calc->z80.r.ir.b.h = regs[16]; + calc->z80.r.ir.b.l = regs[17]; + calc->z80.r.r7 = regs[18] & 0x80; + + if (calc->hw.model_id == '2' || calc->hw.model_id == '3') { + if (fread(b, 1, 5, savfile) < 5) + return 1; + + if (calc->hw.model_id == '3') + set_hw_reg(calc, "rom_bank", calc->mempagemap[1] & 0x08); + calc->hw.z80_out(calc, 0x02, b[4]); + } + + /* Read RAM contents: old save files for TI-82/83/85 store RAM + pages in logical rather than physical order */ + + if (calc->hw.model_id == '2' || calc->hw.model_id == '3' + || calc->hw.model_id == '5') { + if (fread(calc->mem + calc->hw.romsize + 0x4000, 1, + 0x4000, savfile) < 0x4000) + return 1; + if (fread(calc->mem + calc->hw.romsize, 1, + 0x4000, savfile) < 0x4000) + return 1; + } + else { + if (fread(calc->mem + calc->hw.romsize, 1, + calc->hw.ramsize, savfile) < calc->hw.ramsize) + return 1; + } + + /* Read LCD contents */ + + if (calc->hw.flags & TILEM_CALC_HAS_T6A04) { + calc->lcd.rowstride = 12; /* old save files only + support the visible + portion of the screen */ + if (fread(calc->lcdmem, 1, 768, savfile) < 768) + return 1; + } + + /* Read additional HW state */ + + switch (calc->hw.model_id) { + case '1': + break; + + case '2': + case '3': + if ((c = fgetc(savfile)) != EOF) + calc->lcd.mode = c; + if ((c = fgetc(savfile)) != EOF) + calc->lcd.x = c; + if ((c = fgetc(savfile)) != EOF) + calc->lcd.y = c; + break; + + case '5': + pageA = calc->mempagemap[1]; + if (pageA >= 0x08) + pageA += 0x38; + calc->hw.z80_out(calc, 0x05, pageA); + + if ((c = fgetc(savfile)) != EOF) + calc->hw.z80_out(calc, 0x06, c); + break; + + case '6': + pageA = calc->mempagemap[1]; + pageB = calc->mempagemap[2]; + if (pageA >= 0x10) + pageA += 0x30; + if (pageB >= 0x10) + pageB += 0x30; + + calc->hw.z80_out(calc, 0x05, pageA); + calc->hw.z80_out(calc, 0x06, pageB); + break; + + default: /* TI-73/83+ series */ + if ((c = fgetc(savfile)) != EOF) + calc->lcd.mode = c; + if ((c = fgetc(savfile)) != EOF) + calc->lcd.x = c; + if ((c = fgetc(savfile)) != EOF) + calc->lcd.y = c; + if ((c = fgetc(savfile)) != EOF) + calc->lcd.inc = c; + + if ((c = fgetc(savfile)) == EOF) + c = 0; + if (c) { + pageA = calc->mempagemap[2]; + pageB = calc->mempagemap[3]; + calc->hw.z80_out(calc, 0x04, 0x77); + } + else { + pageA = calc->mempagemap[1]; + pageB = calc->mempagemap[2]; + calc->hw.z80_out(calc, 0x04, 0x76); + } + + if (pageA >= (calc->hw.romsize >> 14)) + pageA = ((pageA & 0x1f) | calc->hw.rampagemask); + if (pageB >= (calc->hw.romsize >> 14)) + pageB = ((pageB & 0x1f) | calc->hw.rampagemask); + + calc->hw.z80_out(calc, 0x06, pageA); + calc->hw.z80_out(calc, 0x07, pageB); + + if ((c = fgetc(savfile)) != EOF) + calc->flash.state = c; + if ((c = fgetc(savfile)) != EOF) + calc->flash.unlock = c; + + if ((c = fgetc(savfile)) != EOF) + calc->hw.z80_out(calc, 0x20, c); + if ((c = fgetc(savfile)) != EOF) + set_hw_reg(calc, "port21", c); + if ((c = fgetc(savfile)) != EOF) + set_hw_reg(calc, "port22", c); + if ((c = fgetc(savfile)) != EOF) + set_hw_reg(calc, "port23", c); + if ((c = fgetc(savfile)) != EOF) + calc->hw.z80_out(calc, 0x27, c); + if ((c = fgetc(savfile)) != EOF) + calc->hw.z80_out(calc, 0x28, c); + break; + } + + calc->poweronhalt = calc->lcd.active = 1; + + return 0; +} + +static int read_sav_line(FILE* savfile, char **buf) +{ + int c, n, na; + + tilem_free(*buf); + + na = 100; + *buf = tilem_malloc_atomic(na); + n = 0; + + while ((c = fgetc(savfile)) != EOF) { + if (c == '\r' || c == '\n') + break; + + n++; + if (n >= na) { + na = n * 2; + *buf = tilem_realloc(*buf, na); + } + + if (c == '#') + c = 0; + (*buf)[n - 1] = c; + } + + if (n == 0 && c == EOF) { + tilem_free(*buf); + *buf = NULL; + return 0; + } + else { + (*buf)[n] = 0; + return 1; + } +} + +static int parse_sav_definition(char* line, char** value) +{ + char *p; + + p = strchr(line, '='); + if (!p) + return 0; + + while (p != line && p[-1] == ' ') + p--; + *p = 0; + p++; + while (*p == ' ' || *p == '=') + p++; + *value = p; + return 1; +} + +static int load_new_sav_file(TilemCalc* calc, FILE* savfile) +{ + char *buf = NULL; + char *p, *q; + dword value, length; + byte *data; + int ok = 0; + byte digit; + int firstdigit; + dword period; + int rt; + + while (read_sav_line(savfile, &buf)) { + if (!parse_sav_definition(buf, &p)) + continue; + + if (*p == '{') { + p++; + if (!strcmp(buf, "RAM")) { + length = calc->hw.ramsize; + data = calc->ram; + } + else if (!strcmp(buf, "LCD")) { + length = calc->hw.lcdmemsize; + data = calc->lcdmem; + } + else { + length = 0; + data = NULL; + } + + value = 0; + firstdigit = 1; + + while (*p != '}') { + if (*p == 0 || *p == '#') { + if (!read_sav_line(savfile, &buf)) + return 1; + p = buf; + continue; + } + + if (*p >= '0' && *p <= '9') { + digit = *p - '0'; + p++; + } + else if (*p >= 'A' && *p <= 'F') { + digit = *p + 10 - 'A'; + p++; + } + else if (*p >= 'a' && *p <= 'f') { + digit = *p + 10 - 'a'; + p++; + } + else { + p++; + continue; + } + + if (firstdigit) { + value = digit << 4; + firstdigit = 0; + } + else { + value |= digit; + if (length != 0) { + *data = value; + data++; + length--; + } + firstdigit = 1; + } + } + + continue; + } + + if (!strcmp(buf, "MODEL")) { + q = p; + while (*q >= ' ') + q++; + *q = 0; + if (strcmp(p, calc->hw.name)) { + tilem_free(buf); + return 1; + } + ok = 1; + continue; + } + + value = strtol(p, &q, 16); + + /* Persistent timers */ + if (!strncmp(buf, "timer:", 6)) { + while (*q == ' ') + q++; + if (*q != ',') + continue; + q++; + while (*q == ' ') + q++; + period = strtol(q, &q, 16); + + while (*q == ' ') + q++; + if (*q != ',') + continue; + q++; + while (*q == ' ') + q++; + rt = strtol(q, &q, 16); + + set_ptimer(calc, buf + 6, value, period, rt); + continue; + } + + /* Z80 */ + if (!strcmp(buf, "af")) calc->z80.r.af.d = value; + else if (!strcmp(buf, "bc")) calc->z80.r.bc.d = value; + else if (!strcmp(buf, "de")) calc->z80.r.de.d = value; + else if (!strcmp(buf, "hl")) calc->z80.r.hl.d = value; + else if (!strcmp(buf, "af'")) calc->z80.r.af2.d = value; + else if (!strcmp(buf, "bc'")) calc->z80.r.bc2.d = value; + else if (!strcmp(buf, "de'")) calc->z80.r.de2.d = value; + else if (!strcmp(buf, "hl'")) calc->z80.r.hl2.d = value; + else if (!strcmp(buf, "ix")) calc->z80.r.ix.d = value; + else if (!strcmp(buf, "iy")) calc->z80.r.iy.d = value; + else if (!strcmp(buf, "pc")) calc->z80.r.pc.d = value; + else if (!strcmp(buf, "sp")) calc->z80.r.sp.d = value; + else if (!strcmp(buf, "ir")) { + calc->z80.r.ir.d = value; + calc->z80.r.r7 = value & 0x80; + } + else if (!strcmp(buf, "wz")) calc->z80.r.wz.d = value; + else if (!strcmp(buf, "wz'")) calc->z80.r.wz2.d = value; + else if (!strcmp(buf, "iff1")) calc->z80.r.iff1 = value; + else if (!strcmp(buf, "iff2")) calc->z80.r.iff2 = value; + else if (!strcmp(buf, "im")) calc->z80.r.im = value; + else if (!strcmp(buf, "interrupts")) + calc->z80.interrupts = value; + else if (!strcmp(buf, "clockspeed")) + calc->z80.clockspeed = value; + else if (!strcmp(buf, "halted")) calc->z80.halted = value; + + /* LCD */ + else if (!strcmp(buf, "lcd.active")) + calc->lcd.active = value; + else if (!strcmp(buf, "lcd.addr")) + calc->lcd.addr = value; + else if (!strcmp(buf, "lcd.rowshift")) + calc->lcd.rowshift = value; + else if (!strcmp(buf, "lcd.contrast")) + calc->lcd.contrast = value; + else if (!strcmp(buf, "lcd.inc")) + calc->lcd.inc = value; + else if (!strcmp(buf, "lcd.mode")) + calc->lcd.mode = value; + else if (!strcmp(buf, "lcd.x")) + calc->lcd.x = value; + else if (!strcmp(buf, "lcd.y")) + calc->lcd.y = value; + else if (!strcmp(buf, "lcd.nextbyte")) + calc->lcd.nextbyte = value; + else if (!strcmp(buf, "lcd.rowstride")) + calc->lcd.rowstride = value; + else if (!strcmp(buf, "lcd.busy")) + calc->lcd.busy = value; + + /* Link port */ + else if (!strcmp(buf, "linkport.lines")) + calc->linkport.lines = value; + else if (!strcmp(buf, "linkport.mode")) + calc->linkport.mode = value; + else if (!strcmp(buf, "linkport.assistflags")) + calc->linkport.assistflags = value; + else if (!strcmp(buf, "linkport.assistin")) + calc->linkport.assistin = value; + else if (!strcmp(buf, "linkport.assistinbits")) + calc->linkport.assistinbits = value; + else if (!strcmp(buf, "linkport.assistout")) + calc->linkport.assistout = value; + else if (!strcmp(buf, "linkport.assistoutbits")) + calc->linkport.assistoutbits = value; + else if (!strcmp(buf, "linkport.assistlastbyte")) + calc->linkport.assistlastbyte = value; + + /* Keypad */ + else if (!strcmp(buf, "keypad.group")) + calc->keypad.group = value; + else if (!strcmp(buf, "keypad.onkeyint")) + calc->keypad.onkeyint = value; + + /* MD5 assist */ + else if (!strcmp(buf, "md5assist.a")) + calc->md5assist.regs[0] = value; + else if (!strcmp(buf, "md5assist.b")) + calc->md5assist.regs[1] = value; + else if (!strcmp(buf, "md5assist.c")) + calc->md5assist.regs[2] = value; + else if (!strcmp(buf, "md5assist.d")) + calc->md5assist.regs[3] = value; + else if (!strcmp(buf, "md5assist.x")) + calc->md5assist.regs[4] = value; + else if (!strcmp(buf, "md5assist.t")) + calc->md5assist.regs[5] = value; + else if (!strcmp(buf, "md5assist.shift")) + calc->md5assist.shift = value; + else if (!strcmp(buf, "md5assist.mode")) + calc->md5assist.mode = value; + + /* Programmable timers */ + else if (!strcmp(buf, "usertimer0.frequency")) + calc->usertimers[0].frequency = value; + else if (!strcmp(buf, "usertimer0.loopvalue")) + calc->usertimers[0].loopvalue = value; + else if (!strcmp(buf, "usertimer0.status")) + calc->usertimers[0].status = value; + else if (!strcmp(buf, "usertimer1.frequency")) + calc->usertimers[1].frequency = value; + else if (!strcmp(buf, "usertimer1.loopvalue")) + calc->usertimers[1].loopvalue = value; + else if (!strcmp(buf, "usertimer1.status")) + calc->usertimers[1].status = value; + else if (!strcmp(buf, "usertimer2.frequency")) + calc->usertimers[2].frequency = value; + else if (!strcmp(buf, "usertimer2.loopvalue")) + calc->usertimers[2].loopvalue = value; + else if (!strcmp(buf, "usertimer2.status")) + calc->usertimers[2].status = value; + + /* Main power */ + else if (!strcmp(buf, "poweronhalt")) + calc->poweronhalt = value; + + /* Battery */ + else if (!strcmp(buf, "battery")) + calc->battery = value; + + /* Memory */ + else if (!strcmp(buf, "mempagemap0")) + calc->mempagemap[0] = value; + else if (!strcmp(buf, "mempagemap1")) + calc->mempagemap[1] = value; + else if (!strcmp(buf, "mempagemap2")) + calc->mempagemap[2] = value; + else if (!strcmp(buf, "mempagemap3")) + calc->mempagemap[3] = value; + else if (!strcmp(buf, "flash.unlock")) + calc->flash.unlock = value; + else if (!strcmp(buf, "flash.state")) + calc->flash.state = value; + else if (!strcmp(buf, "flash.busy")) + calc->flash.busy = value; + else if (!strcmp(buf, "flash.progaddr")) + calc->flash.progaddr = value; + else if (!strcmp(buf, "flash.progbyte")) + calc->flash.progbyte = value; + else if (!strcmp(buf, "flash.toggles")) + calc->flash.toggles = value; + else if (!strcmp(buf, "flash.overridegroup")) + calc->flash.overridegroup = value; + + else + set_hw_reg(calc, buf, value); + } + + tilem_free(buf); + + return !ok; +} + +int tilem_calc_load_state(TilemCalc* calc, FILE* romfile, FILE* savfile) +{ + int b; + int savtype = 0; + + if (romfile) { + if (fread(calc->mem, 1, calc->hw.romsize, romfile) + != calc->hw.romsize) + return 1; + } + + tilem_calc_reset(calc); + + if (savfile) { + /* first byte of old save files is always zero */ + b = fgetc(savfile); + fseek(savfile, 0L, SEEK_SET); + + if (b == 0) { + if (load_old_sav_file(calc, savfile)) { + tilem_calc_reset(calc); + return 1; + } + else + savtype = 1; + } + else { + if (load_new_sav_file(calc, savfile)) { + tilem_calc_reset(calc); + return 1; + } + else + savtype = 2; + } + } + + if (calc->hw.stateloaded) + (*calc->hw.stateloaded)(calc, savtype); + + return 0; +} + +char tilem_get_sav_type(FILE* savfile) +{ + int b; + char *buf = NULL, *p, *q; + const TilemHardware **models; + int nmodels, i; + char id = 0; + + tilem_get_supported_hardware(&models, &nmodels); + + /* first byte of old save files is always zero */ + b = fgetc(savfile); + fseek(savfile, 0L, SEEK_SET); + if (b == 0) + return 0; /* old files give no way to detect model */ + + while (read_sav_line(savfile, &buf)) { + if (parse_sav_definition(buf, &p) + && !strcmp(buf, "MODEL")) { + q = p; + while (*q >= ' ') + q++; + *q = 0; + + for (i = 0; i < nmodels; i++) + if (!strcmp(p, models[i]->name)) + id = models[i]->model_id; + + break; + } + } + + fseek(savfile, 0L, SEEK_SET); + tilem_free(buf); + return id; +} + +int tilem_calc_save_state(TilemCalc* calc, FILE* romfile, FILE* savfile) +{ + dword i; + dword t; + int j; + const char* tname; + unsigned int rowstride; + + if (romfile) { + if (fwrite(calc->mem, 1, calc->hw.romsize, romfile) + != calc->hw.romsize) + return 1; + } + + if (savfile) { + fprintf(savfile, "# Tilem II State File\n# Version: %s\n", + PACKAGE_VERSION); + fprintf(savfile, "MODEL = %s\n", calc->hw.name); + + fprintf(savfile, "\n## CPU ##\n"); + fprintf(savfile, "af = %04X\n", calc->z80.r.af.w.l); + fprintf(savfile, "bc = %04X\n", calc->z80.r.bc.w.l); + fprintf(savfile, "de = %04X\n", calc->z80.r.de.w.l); + fprintf(savfile, "hl = %04X\n", calc->z80.r.hl.w.l); + fprintf(savfile, "af' = %04X\n", calc->z80.r.af2.w.l); + fprintf(savfile, "bc' = %04X\n", calc->z80.r.bc2.w.l); + fprintf(savfile, "de' = %04X\n", calc->z80.r.de2.w.l); + fprintf(savfile, "hl' = %04X\n", calc->z80.r.hl2.w.l); + fprintf(savfile, "ix = %04X\n", calc->z80.r.ix.w.l); + fprintf(savfile, "iy = %04X\n", calc->z80.r.iy.w.l); + fprintf(savfile, "pc = %04X\n", calc->z80.r.pc.w.l); + fprintf(savfile, "sp = %04X\n", calc->z80.r.sp.w.l); + fprintf(savfile, "ir = %04X\n", + ((calc->z80.r.ir.w.l & ~0x80) | calc->z80.r.r7)); + fprintf(savfile, "wz = %04X\n", calc->z80.r.wz.w.l); + fprintf(savfile, "wz' = %04X\n", calc->z80.r.wz2.w.l); + fprintf(savfile, "iff1 = %X\n", calc->z80.r.iff1); + fprintf(savfile, "iff2 = %X\n", calc->z80.r.iff2); + fprintf(savfile, "im = %X\n", calc->z80.r.im); + fprintf(savfile, "interrupts = %08X\n", calc->z80.interrupts); + fprintf(savfile, "clockspeed = %X\n", calc->z80.clockspeed); + fprintf(savfile, "halted = %X\n", calc->z80.halted); + + fprintf(savfile, "\n## LCD Driver ##\n"); + fprintf(savfile, "lcd.active = %X\n", + calc->lcd.active); + fprintf(savfile, "lcd.contrast = %X\n", + calc->lcd.contrast); + fprintf(savfile, "lcd.rowstride = %X\n", + calc->lcd.rowstride); + if (calc->hw.flags & TILEM_CALC_HAS_T6A04) { + fprintf(savfile, "lcd.rowshift = %X\n", + calc->lcd.rowshift); + fprintf(savfile, "lcd.inc = %X\n", + calc->lcd.inc); + fprintf(savfile, "lcd.mode = %X\n", + calc->lcd.mode); + fprintf(savfile, "lcd.x = %02X\n", + calc->lcd.x); + fprintf(savfile, "lcd.y = %02X\n", + calc->lcd.y); + fprintf(savfile, "lcd.nextbyte = %02X\n", + calc->lcd.nextbyte); + fprintf(savfile, "lcd.busy = %X\n", + calc->lcd.busy); + } + fprintf(savfile, "lcd.addr = %X\n", calc->lcd.addr); + + if (calc->hw.flags & TILEM_CALC_HAS_LINK) { + fprintf(savfile, "\n## Link Port ##\n"); + fprintf(savfile, "linkport.lines = %X\n", + calc->linkport.lines); + fprintf(savfile, "linkport.mode = %08X\n", + calc->linkport.mode); + } + if (calc->hw.flags & TILEM_CALC_HAS_LINK_ASSIST) { + fprintf(savfile, "linkport.assistflags = %08X\n", + calc->linkport.assistflags); + fprintf(savfile, "linkport.assistin = %02X\n", + calc->linkport.assistin); + fprintf(savfile, "linkport.assistinbits = %X\n", + calc->linkport.assistinbits); + fprintf(savfile, "linkport.assistout = %02X\n", + calc->linkport.assistout); + fprintf(savfile, "linkport.assistoutbits = %X\n", + calc->linkport.assistoutbits); + fprintf(savfile, "linkport.assistlastbyte = %02X\n", + calc->linkport.assistlastbyte); + } + + fprintf(savfile, "\n## Keypad ##\n"); + fprintf(savfile, "keypad.group = %X\n", calc->keypad.group); + fprintf(savfile, "keypad.onkeyint = %X\n", + calc->keypad.onkeyint); + + fprintf(savfile, "\n## Memory mapping ##\n"); + fprintf(savfile, "mempagemap0 = %X\n", calc->mempagemap[0]); + fprintf(savfile, "mempagemap1 = %X\n", calc->mempagemap[1]); + fprintf(savfile, "mempagemap2 = %X\n", calc->mempagemap[2]); + fprintf(savfile, "mempagemap3 = %X\n", calc->mempagemap[3]); + + fprintf(savfile, "\n## Power ##\n"); + fprintf(savfile, "poweronhalt = %X\n", calc->poweronhalt); + fprintf(savfile, "battery = %X\n", calc->battery); + + if (calc->hw.flags & TILEM_CALC_HAS_FLASH) { + fprintf(savfile, "\n## Flash ##\n"); + fprintf(savfile, "flash.unlock = %X\n", + calc->flash.unlock); + fprintf(savfile, "flash.state = %X\n", + calc->flash.state); + fprintf(savfile, "flash.busy = %X\n", + calc->flash.busy); + fprintf(savfile, "flash.progaddr = %X\n", + calc->flash.progaddr); + fprintf(savfile, "flash.progbyte = %X\n", + calc->flash.progbyte); + fprintf(savfile, "flash.toggles = %X\n", + calc->flash.toggles); + fprintf(savfile, "flash.overridegroup = %X\n", + calc->flash.overridegroup); + } + + if (calc->hw.flags & TILEM_CALC_HAS_MD5_ASSIST) { + fprintf(savfile, "\n## MD5 assist ##\n"); + fprintf(savfile, "md5assist.a = %X\n", + calc->md5assist.regs[0]); + fprintf(savfile, "md5assist.b = %X\n", + calc->md5assist.regs[1]); + fprintf(savfile, "md5assist.c = %X\n", + calc->md5assist.regs[2]); + fprintf(savfile, "md5assist.d = %X\n", + calc->md5assist.regs[3]); + fprintf(savfile, "md5assist.x = %X\n", + calc->md5assist.regs[4]); + fprintf(savfile, "md5assist.t = %X\n", + calc->md5assist.regs[5]); + fprintf(savfile, "md5assist.shift = %X\n", + calc->md5assist.shift); + fprintf(savfile, "md5assist.mode = %X\n", + calc->md5assist.mode); + } + + for (j = 0; j < calc->hw.nusertimers; j++) { + fprintf(savfile, + "\n## Programmable timer %d ##\n", j); + fprintf(savfile, "usertimer%d.frequency = %X\n", + j, calc->usertimers[j].frequency); + fprintf(savfile, "usertimer%d.loopvalue = %X\n", + j, calc->usertimers[j].loopvalue); + fprintf(savfile, "usertimer%d.status = %X\n", + j, calc->usertimers[j].status); + } + + fprintf(savfile, "\n## Model-specific ##\n"); + for (j = 0; j < calc->hw.nhwregs; j++) { + fprintf(savfile, "%s = %X\n", calc->hw.hwregnames[j], + calc->hwregs[j]); + } + + fprintf(savfile, "\n## Timers ##\n"); + for (j = calc->z80.timer_cpu; j; + j = calc->z80.timers[j].next) { + tname = get_timer_name(calc, j); + if (tname) { + t = tilem_z80_get_timer_clocks(calc, j); + fprintf(savfile, "timer:%s = %X, %X, 0\n", + tname, t, calc->z80.timers[j].period); + } + } + for (j = calc->z80.timer_rt; j; + j = calc->z80.timers[j].next) { + tname = get_timer_name(calc, j); + if (tname) { + t = tilem_z80_get_timer_microseconds(calc, j); + fprintf(savfile, "timer:%s = %X, %X, 1\n", + tname, t, calc->z80.timers[j].period); + } + } + + fprintf(savfile, "\n## RAM contents ##\n"); + fprintf(savfile, "RAM = {\n"); + for (i = 0; i < calc->hw.ramsize; i++) { + if (i % 256 == 0) { + fprintf(savfile, "# %02X:%04X\n", + (i >> 14), (i & 0x3fff)); + } + + fprintf(savfile, "%02X", + calc->mem[i + calc->hw.romsize]); + if (i % 32 == 31) + fprintf(savfile, "\n"); + } + fprintf(savfile, "}\n## End of RAM contents ##\n"); + + if (calc->hw.lcdmemsize) { + fprintf(savfile, "\n## LCD contents ##\n"); + fprintf(savfile, "LCD = {\n"); + rowstride = calc->lcd.rowstride; + if (rowstride == 0) + rowstride = 32; + + for (i = 0; i < calc->hw.lcdmemsize; i++) { + fprintf(savfile, "%02X", calc->lcdmem[i]); + if (i % rowstride == (rowstride - 1)) + fprintf(savfile, "\n"); + } + fprintf(savfile, "}\n## End of LCD contents ##\n"); + } + } + + return 0; +} diff --git a/tool/tilem-src/emu/tilem.h b/tool/tilem-src/emu/tilem.h new file mode 100644 index 0000000..01878ad --- /dev/null +++ b/tool/tilem-src/emu/tilem.h @@ -0,0 +1,950 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2009-2012 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 + * . + */ + +#ifndef _TILEM_H +#define _TILEM_H + +#include "tilemint.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Basic integer types */ +typedef uint8_t byte; +typedef uint16_t word; +typedef uint32_t dword; +typedef uint64_t qword; + +/* Structure types */ +typedef struct _TilemHardware TilemHardware; +typedef struct _TilemCalc TilemCalc; + +/* Useful macros */ +#if __GNUC__ >= 3 +# define TILEM_ATTR_PURE __attribute__((__pure__)) +# define TILEM_ATTR_UNUSED __attribute__((__unused__)) +# define TILEM_ATTR_MALLOC __attribute__((__malloc__)) +# define TILEM_ATTR_PRINTF(x,y) __attribute__((__format__(__printf__,x,y))) +# define TILEM_LIKELY(xxx) (__builtin_expect((xxx), 1)) +# define TILEM_UNLIKELY(xxx) (__builtin_expect((xxx), 0)) +#else +# define TILEM_ATTR_PURE +# define TILEM_ATTR_UNUSED +# define TILEM_ATTR_MALLOC +# define TILEM_ATTR_PRINTF(x,y) +# define TILEM_LIKELY(xxx) (xxx) +# define TILEM_UNLIKELY(xxx) (xxx) +#endif + +#define TILEM_DWORD_TO_PTR(xxx) ((void*)(uintptr_t)(xxx)) +#define TILEM_PTR_TO_DWORD(xxx) ((dword)(uintptr_t)(xxx)) + +/* Memory allocation */ +void* tilem_malloc(size_t size) TILEM_ATTR_MALLOC; +void* tilem_malloc0(size_t size) TILEM_ATTR_MALLOC; +void* tilem_malloc_atomic(size_t size) TILEM_ATTR_MALLOC; +void* tilem_try_malloc(size_t size) TILEM_ATTR_MALLOC; +void* tilem_try_malloc0(size_t size) TILEM_ATTR_MALLOC; +void* tilem_try_malloc_atomic(size_t size) TILEM_ATTR_MALLOC; +void* tilem_realloc(void* ptr, size_t size) TILEM_ATTR_MALLOC; +void tilem_free(void* ptr); +#define tilem_new(ttt, nnn) ((ttt*) tilem_malloc((nnn) * sizeof(ttt))); +#define tilem_new0(ttt, nnn) ((ttt*) tilem_malloc0((nnn) * sizeof(ttt))); +#define tilem_new_atomic(ttt, nnn) ((ttt*) tilem_malloc_atomic((nnn) * sizeof(ttt))); + +#define tilem_try_new(ttt, nnn) ((ttt*) tilem_try_malloc((nnn) * sizeof(ttt))); +#define tilem_try_new0(ttt, nnn) ((ttt*) tilem_try_malloc0((nnn) * sizeof(ttt))); +#define tilem_try_new_atomic(ttt, nnn) ((ttt*) tilem_try_malloc_atomic((nnn) * sizeof(ttt))); +#define tilem_renew(ttt, ppp, nnn) ((ttt*) tilem_realloc((ppp), (nnn) * sizeof(ttt))) + +/* Message/error logging */ + +/* Write an informative message. This can be used to notify the user + of major occurences, such as changes in the Flash protection. + These messages can occur regularly in normal operation and are + provided chiefly to aid in debugging. */ +void tilem_message(TilemCalc* calc, const char* msg, ...) + TILEM_ATTR_PRINTF(2, 3); + +/* Write a warning message. These messages occur when the calculator + software (either the OS or a user program) performs an invalid + operation; these messages often indicate a bug in the calculator + software, but are otherwise harmless. */ +void tilem_warning(TilemCalc* calc, const char* msg, ...) + TILEM_ATTR_PRINTF(2, 3); + +/* Write a warning about an internal error. These messages should + never occur and indicate a bug in TilEm. */ +void tilem_internal(TilemCalc* calc, const char* msg, ...) + TILEM_ATTR_PRINTF(2, 3); + + +/* Z80 CPU */ + +/* This union allows us to manipulate register pairs as either byte or + word values. It may need to be modified for really unusual host + CPUs. */ +typedef union _TilemZ80Reg { +#ifdef WORDS_BIGENDIAN + struct { + byte h3, h2, h, l; + } b; + + struct { + word h, l; + } w; + + dword d; +#else + struct { + byte l, h, h2, h3; + } b; + + struct { + word l, h; + } w; + + dword d; +#endif +} TilemZ80Reg; + +typedef struct _TilemZ80Regs { + TilemZ80Reg af, bc, de, hl; + TilemZ80Reg ix, iy, pc, sp; + TilemZ80Reg ir, wz, wz2; + TilemZ80Reg af2, bc2, de2, hl2; + int iff1, iff2, im; + byte r7; +} TilemZ80Regs; + +/* Breakpoint types */ +enum { + TILEM_BREAK_MEM_READ = 1, /* Break after reading from memory */ + TILEM_BREAK_MEM_EXEC, /* Break prior to executing from memory */ + TILEM_BREAK_MEM_WRITE, /* Break after writing to memory */ + TILEM_BREAK_PORT_READ, /* Break after reading from port */ + TILEM_BREAK_PORT_WRITE, /* Break after writing to port */ + TILEM_BREAK_EXECUTE, /* Break after executing opcode */ + + TILEM_BREAK_TYPE_MASK = 0xffff, + + TILEM_BREAK_PHYSICAL = 0x10000, /* Use physical addresses */ + TILEM_BREAK_DISABLED = 0x20000 /* Disabled breakpoint */ +}; + +/* Emulation flags */ +enum { + TILEM_Z80_BREAK_INVALID = 1, /* Break on invalid + instructions */ + TILEM_Z80_BREAK_UNDOCUMENTED = 2, /* Break on undocumented + instructions */ + TILEM_Z80_SKIP_UNDOCUMENTED = 4, /* Ignore undocumented + instructions entirely + (act as two NOPs) */ + TILEM_Z80_RESET_UNDOCUMENTED = 8, /* Reset CPU following + undocumented instructions */ + TILEM_Z80_BREAK_EXCEPTIONS = 16, /* Break on hardware exceptions */ + TILEM_Z80_IGNORE_EXCEPTIONS = 32 /* Ignore hardware exceptions */ +}; + +/* Reasons for stopping emulation */ +enum { + TILEM_STOP_TIMEOUT = 0, /* stopped due to timeout */ + TILEM_STOP_BREAKPOINT = 1, /* stopped due to breakpoint */ + TILEM_STOP_INVALID_INST = 2, /* invalid instruction */ + TILEM_STOP_UNDOCUMENTED_INST = 4, /* undocumented instruction */ + TILEM_STOP_EXCEPTION = 8, /* hardware exception */ + TILEM_STOP_LINK_STATE = 16, /* blacklink state change */ + TILEM_STOP_LINK_READ_BYTE = 32, /* graylink finished reading byte */ + TILEM_STOP_LINK_WRITE_BYTE = 64, /* graylink finished writing byte */ + TILEM_STOP_LINK_ERROR = 128 /* graylink encountered error */ +}; + +/* Types of interrupt */ +enum { + TILEM_INTERRUPT_ON_KEY = 1, /* ON key pressed */ + TILEM_INTERRUPT_TIMER1 = 2, /* Main interrupt timer */ + TILEM_INTERRUPT_TIMER2 = 4, /* Alt. interrupt timer (83/83+) */ + TILEM_INTERRUPT_USER_TIMER1 = 8, /* Programmable timers (83+SE) */ + TILEM_INTERRUPT_USER_TIMER2 = 16, + TILEM_INTERRUPT_USER_TIMER3 = 32, + TILEM_INTERRUPT_LINK_ACTIVE = 512, /* Link port state changed */ + TILEM_INTERRUPT_LINK_READ = 1024, /* Link assist read a byte */ + TILEM_INTERRUPT_LINK_IDLE = 2048, /* Link assist is idle */ + TILEM_INTERRUPT_LINK_ERROR = 4096 /* Link assist failed */ +}; + +/* Types of hardware exception */ +enum { + TILEM_EXC_RAM_EXEC = 1, /* Executing at invalid RAM address */ + TILEM_EXC_FLASH_EXEC = 2, /* Executing at invalid Flash address */ + TILEM_EXC_FLASH_WRITE = 4, /* Writing to invalid Flash address */ + TILEM_EXC_INSTRUCTION = 8 /* Invalid instruction */ +}; + +/* Constant hardware timer IDs */ +enum { + TILEM_TIMER_NONE = 0, + TILEM_TIMER_LCD_DELAY, + TILEM_TIMER_FLASH_DELAY, + TILEM_TIMER_LINK_ASSIST, + TILEM_TIMER_USER1, + TILEM_TIMER_USER2, + TILEM_TIMER_USER3, + TILEM_TIMER_HW +}; + +#define TILEM_NUM_SYS_TIMERS (TILEM_TIMER_HW - 1) + +/* Type of a timer callback function. Second arg is the callback data + passed to tilem_z80_add_timer(). */ +typedef void (*TilemZ80TimerFunc)(TilemCalc*, void*); + +/* Type of a breakpoint test function. Second arg is the memory + address (or opcode in the case of TILEM_BREAK_EXECUTE breakpoints.) + Third arg is the callback data passed to + tilem_z80_add_breakpoint(). */ +typedef int (*TilemZ80BreakpointFunc)(TilemCalc*, dword, void*); + +typedef struct _TilemZ80Timer TilemZ80Timer; +typedef struct _TilemZ80Breakpoint TilemZ80Breakpoint; + +typedef struct _TilemZ80 { + TilemZ80Regs r; + unsigned int interrupts; /* Currently active interrupts */ + int clockspeed; /* Current CPU speed (kHz) */ + int halted; + unsigned int exception; + dword clock; + dword lastwrite; + dword lastlcdwrite; + + unsigned int emuflags; + + int ntimers; + TilemZ80Timer* timers; + int timer_cpu; /* Sorted list of timers (CPU-based) */ + int timer_rt; /* Sorted list of timers (realtime) */ + int timer_free; /* List of free timer structs */ + + int nbreakpoints; + TilemZ80Breakpoint* breakpoints; + int breakpoint_mr; /* Memory read breakpoints */ + int breakpoint_mx; /* Memory exec breakpoints */ + int breakpoint_mw; /* Memory write breakpoints */ + int breakpoint_pr; /* Port read breakpoints */ + int breakpoint_pw; /* Port write breakpoints */ + int breakpoint_op; /* Opcode breakpoints */ + int breakpoint_mpr; /* Physical mem read breakpoints */ + int breakpoint_mpx; /* Physical mem exec breakpoints */ + int breakpoint_mpw; /* Physical mem write breakpoints */ + int breakpoint_disabled; /* Disabled breakpoints */ + int breakpoint_free; /* List of free bp structs */ + + int stopping; + dword stop_reason; + dword stop_mask; + int stop_breakpoint; +} TilemZ80; + +/* Reset CPU */ +void tilem_z80_reset(TilemCalc* calc); + +/* Halt simulation */ +void tilem_z80_stop(TilemCalc* calc, dword reason); + +/* Set CPU speed (kHz) */ +void tilem_z80_set_speed(TilemCalc* calc, int speed); + +/* Raise a hardware exception */ +void tilem_z80_exception(TilemCalc* calc, unsigned type); + +/* Add a timer with the given callback function and data. The + callback function will be called after 'count' time units, and + every 'period' time units thereafter. If rt = 0, the time units + are CPU clock cycles; if rt = 1, time units are microseconds. + + Note that if a timer is set in response to a memory read or write, + the length of the delay may be off by as much as 19 clock cycles; + the precise timings for these are not (yet) properly emulated. + Timers set in response to port I/O events will be accurate to the + nearest CPU clock cycle. + + The timer is considered to have "fired" as soon as the specified + amount of time has elapsed. The callback function, however, may + not be called until after the instruction finishes. If multiple + timers fire during the same instruction, the order in which the + callback functions will be called is undefined. + + If you create a timer using this function (either repeating or + non-repeating), you must call tilem_z80_remove_timer() when the + timer is no longer needed. +*/ +int tilem_z80_add_timer(TilemCalc* calc, dword count, dword period, + int rt, TilemZ80TimerFunc func, void* data); + +/* Change settings for an existing timer. Arguments are the same as + above. If count = 0, the timer is disabled. */ +void tilem_z80_set_timer(TilemCalc* calc, int id, dword count, + dword period, int rt); + +/* Change period for an existing timer without affecting the current + interval. */ +void tilem_z80_set_timer_period(TilemCalc* calc, int id, dword period); + +/* Delete a timer. */ +void tilem_z80_remove_timer(TilemCalc* calc, int id); + +/* Check whether a timer is currently running. */ +int tilem_z80_timer_running(TilemCalc* calc, int id) + TILEM_ATTR_PURE; + +/* Get the number of clock ticks from now until the next time the + given timer fires. (This may be negative, if the timer has already + fired during this instruction.) NOTE: If the timer is disabled, + the return value is undefined. */ +int tilem_z80_get_timer_clocks(TilemCalc* calc, int id) + TILEM_ATTR_PURE; + +/* Get the number of microseconds from now until the next time the + given timer fires. */ +int tilem_z80_get_timer_microseconds(TilemCalc* calc, int id) + TILEM_ATTR_PURE; + +/* Add a breakpoint. The breakpoint will be triggered if the address, + ANDed with the given mask, falls between the given start and end + inclusive. If a callback function is specified it acts as an + additional filter, to determine whether the simulation should be + halted. */ +int tilem_z80_add_breakpoint(TilemCalc* calc, int type, + dword start, dword end, dword mask, + TilemZ80BreakpointFunc func, void* data); + +/* Remove the given breakpoint. */ +void tilem_z80_remove_breakpoint(TilemCalc* calc, int id); + +/* Enable the given breakpoint. */ +void tilem_z80_enable_breakpoint(TilemCalc* calc, int id); + +/* Disable the given breakpoint. */ +void tilem_z80_disable_breakpoint(TilemCalc* calc, int id); + +/* Check whether the given breakpoint is currently enabled. */ +int tilem_z80_breakpoint_enabled(TilemCalc* calc, int id); + +/* Get the type of the given breakpoint. */ +int tilem_z80_get_breakpoint_type(TilemCalc* calc, int id); + +/* Get the start address of the given breakpoint. */ +dword tilem_z80_get_breakpoint_address_start(TilemCalc* calc, int id); + +/* Get the start address of the given breakpoint. */ +dword tilem_z80_get_breakpoint_address_end(TilemCalc* calc, int id); + +/* Get the start address of the given breakpoint. */ +dword tilem_z80_get_breakpoint_address_mask(TilemCalc* calc, int id); + +/* Get the callback/filter function associated to the given breakpoint. */ +TilemZ80BreakpointFunc tilem_z80_get_breakpoint_callback(TilemCalc* calc, + int id); + +/* Get the data associated to the given breakpoint. */ +void* tilem_z80_get_breakpoint_data(TilemCalc* calc, int id); + +/* Set the type of the given breakpoint. */ +void tilem_z80_set_breakpoint_type(TilemCalc* calc, int id, int type); + +/* Set the start address of the given breakpoint. */ +void tilem_z80_set_breakpoint_address_start(TilemCalc* calc, int id, + dword start); + +/* Set the start address of the given breakpoint. */ +void tilem_z80_set_breakpoint_address_end(TilemCalc* calc, int id, dword end); + +/* Set the start address of the given breakpoint. */ +void tilem_z80_set_breakpoint_address_mask(TilemCalc* calc, int id, dword mask); + +/* Set the callback/filter function associated to the given breakpoint. */ +void tilem_z80_set_breakpoint_callback(TilemCalc* calc, int id, + TilemZ80BreakpointFunc func); + +/* Set the data associated to the given breakpoint. */ +void tilem_z80_set_breakpoint_data(TilemCalc* calc, int id, void* data); + + +/* Run the simulated CPU for the given number of clock + ticks/microseconds, or until a breakpoint is hit or + tilem_z80_stop() is called. */ +dword tilem_z80_run(TilemCalc* calc, int clocks, int* remaining); +dword tilem_z80_run_time(TilemCalc* calc, int microseconds, int* remaining); + + +/* LCD driver */ + +/* Emulation flags */ +enum { + TILEM_LCD_REQUIRE_DELAY = 1, /* Emulate required delay + between commands */ + TILEM_LCD_REQUIRE_LONG_DELAY = 2 /* Require extra-long delay */ +}; + +typedef struct _TilemLCD { + /* Common settings */ + byte active; /* LCD driver active */ + byte contrast; /* Contrast value (0-63) */ + int rowstride; /* Number of bytes per row */ + unsigned int emuflags; + + /* T6A43 internal driver */ + word addr; /* Memory address */ + + /* T6A04 external driver */ + byte mode; /* I/O mode (0 = 6bit, 1 = 8bit) */ + byte inc; /* Increment mode (4-7) */ + byte nextbyte; /* Output register */ + int x, y; /* Current position */ + int rowshift; /* Starting row for display */ + byte busy; +} TilemLCD; + +/* Reset LCD driver */ +void tilem_lcd_reset(TilemCalc* calc); + +/* Get LCD driver status (port 10 input) */ +byte tilem_lcd_t6a04_status(TilemCalc* calc); + +/* Send command to LCD driver (port 10 output) */ +void tilem_lcd_t6a04_control(TilemCalc* calc, byte val); + +/* Read data from LCD driver (port 11 input) */ +byte tilem_lcd_t6a04_read(TilemCalc* calc); + +/* Write data to LCD driver (port 11 output) */ +void tilem_lcd_t6a04_write(TilemCalc* calc, byte val); + +/* Get screen image (T6A04 style) */ +void tilem_lcd_t6a04_get_data(TilemCalc* calc, byte* data); + +/* Get screen image (T6A43 style) */ +void tilem_lcd_t6a43_get_data(TilemCalc* calc, byte* data); + +/* Callback for TILEM_TIMER_LCD_DELAY */ +void tilem_lcd_delay_timer(TilemCalc* calc, void* data); + + + +/* DBUS link port driver */ + +/* Link port / assist mode flags */ +enum { + TILEM_LINK_MODE_ASSIST = 1, /* Enable link assist */ + TILEM_LINK_MODE_NO_TIMEOUT = 2, /* Assist doesn't time out (xp) */ + TILEM_LINK_MODE_INT_ON_ACTIVE = 4, /* Interrupt on state change */ + TILEM_LINK_MODE_INT_ON_READ = 8, /* Interrupt on asst. read */ + TILEM_LINK_MODE_INT_ON_IDLE = 16, /* Interrupt when asst. idle */ + TILEM_LINK_MODE_INT_ON_ERROR = 32 /* Interrupt on asst. error */ +}; + +/* Link port state flags */ +enum { + TILEM_LINK_ASSIST_READ_BYTE = 1, /* Assisted read finished */ + TILEM_LINK_ASSIST_READ_BUSY = 2, /* Assisted read in progress */ + TILEM_LINK_ASSIST_READ_ERROR = 4, /* Assisted read failed */ + TILEM_LINK_ASSIST_WRITE_BUSY = 8, /* Assisted write in progress */ + TILEM_LINK_ASSIST_WRITE_ERROR = 16 /* Assisted write failed */ +}; + +/* Link emulation mode */ +enum { + TILEM_LINK_EMULATOR_NONE = 0, /* Link port disconnected */ + TILEM_LINK_EMULATOR_BLACK = 1, /* Connected to virtual BlackLink + (exit emulation on state change) */ + TILEM_LINK_EMULATOR_GRAY = 2 /* Connected to virtual GrayLink + (auto send/receive bytes) */ +}; + +typedef struct _TilemLinkport { + byte lines, extlines; /* Link line state for TI/PC + 0 = both lines high + 1 = red wire low + 2 = white wire low + 3 = both wires low */ + + unsigned int mode; /* Mode flags */ + + /* Internal link assist */ + unsigned int assistflags; /* Assist state */ + byte assistin; /* Input buffer (recv from PC) */ + byte assistinbits; /* Input bit count */ + byte assistout; /* Output buffer (send to PC) */ + byte assistoutbits; /* Output bit count */ + byte assistlastbyte; /* Last byte received */ + + /* External link emulator */ + byte linkemu; + byte graylinkin; /* Input buffer (recv from TI) */ + byte graylinkinbits; /* Input bit count */ + byte graylinkout; /* Output buffer (send to TI) */ + byte graylinkoutbits; /* Output bit count */ +} TilemLinkport; + +/* Reset link port */ +void tilem_linkport_reset(TilemCalc* calc); + +/* Read link port lines */ +byte tilem_linkport_get_lines(TilemCalc* calc); + +/* Set link port lines */ +void tilem_linkport_set_lines(TilemCalc* calc, byte lines); + +/* Read from, and clear, link assist input buffer */ +byte tilem_linkport_read_byte(TilemCalc* calc); + +/* Write to link assist output buffer */ +void tilem_linkport_write_byte(TilemCalc* calc, byte data); + +/* Get assist state */ +unsigned int tilem_linkport_get_assist_flags(TilemCalc* calc); + +/* Set link port mode */ +void tilem_linkport_set_mode(TilemCalc* calc, unsigned int mode); + +/* Set line states for virtual BlackLink */ +void tilem_linkport_blacklink_set_lines(TilemCalc* calc, byte lines); + +/* Get line states from virtual BlackLink */ +byte tilem_linkport_blacklink_get_lines(TilemCalc* calc); + +/* Reset GrayLink */ +void tilem_linkport_graylink_reset(TilemCalc* calc); + +/* Check if GrayLink is ready to send data */ +int tilem_linkport_graylink_ready(TilemCalc* calc); + +/* Send a byte via virtual GrayLink */ +int tilem_linkport_graylink_send_byte(TilemCalc* calc, byte value); + +/* Get byte received by virtual GrayLink (-1 = none available) */ +int tilem_linkport_graylink_get_byte(TilemCalc* calc); + +/* Callback for TILEM_TIMER_LINK_ASSIST */ +void tilem_linkport_assist_timer(TilemCalc* calc, void* data); + + +/* Keypad */ + +typedef struct _TilemKeypad { + byte group; + byte onkeydown; + byte onkeyint; + byte keysdown[8]; +} TilemKeypad; + +/* Reset keypad */ +void tilem_keypad_reset(TilemCalc* calc); + +/* Set current group (port 1 output) */ +void tilem_keypad_set_group(TilemCalc* calc, byte group); + +/* Read keys from current group (port 1 input) */ +byte tilem_keypad_read_keys(TilemCalc* calc); + +/* Press a key */ +void tilem_keypad_press_key(TilemCalc* calc, int scancode); + +/* Release a key */ +void tilem_keypad_release_key(TilemCalc* calc, int scancode); + + +/* Flash */ + +/* Emulation flags */ +enum { + TILEM_FLASH_REQUIRE_DELAY = 1 /* Require delay after + program/erase */ +}; + +typedef struct _TilemFlashSector { + dword start; + dword size; + byte protectgroup; +} TilemFlashSector; + +typedef struct _TilemFlash { + byte unlock; + byte state; + unsigned int emuflags; + byte busy; + dword progaddr; + byte progbyte; + byte toggles; + byte overridegroup; +} TilemFlash; + +/* Reset Flash */ +void tilem_flash_reset(TilemCalc* calc); + +/* Read a byte from the Flash chip */ +byte tilem_flash_read_byte(TilemCalc* calc, dword pa); + +/* Erase a Flash sector */ +void tilem_flash_erase_address(TilemCalc* calc, dword pa); + +/* Write a byte to the Flash chip */ +void tilem_flash_write_byte(TilemCalc* calc, dword pa, byte v); + +/* Callback for TILEM_TIMER_FLASH_DELAY */ +void tilem_flash_delay_timer(TilemCalc* calc, void* data); + + +/* MD5 assist */ + +enum { + TILEM_MD5_REG_A = 0, /* initial 'a' value */ + TILEM_MD5_REG_B = 1, /* 'b' value */ + TILEM_MD5_REG_C = 2, /* 'c' value */ + TILEM_MD5_REG_D = 3, /* 'd' value */ + TILEM_MD5_REG_X = 4, /* 'X' (or 'T') value */ + TILEM_MD5_REG_T = 5 /* 'T' (or 'X') value */ +}; + +enum { + TILEM_MD5_FUNC_FF = 0, + TILEM_MD5_FUNC_GG = 1, + TILEM_MD5_FUNC_HH = 2, + TILEM_MD5_FUNC_II = 3 +}; + +typedef struct _TilemMD5Assist { + dword regs[6]; + byte shift; + byte mode; +} TilemMD5Assist; + +/* Reset MD5 assist */ +void tilem_md5_assist_reset(TilemCalc* calc); + +/* Get output value */ +dword tilem_md5_assist_get_value(TilemCalc* calc); + + +/* Programmable timers */ + +#define TILEM_MAX_USER_TIMERS 3 + +enum { + TILEM_USER_TIMER_LOOP = 1, /* loop when counter + reaches 0 */ + TILEM_USER_TIMER_INTERRUPT = 2, /* generate interrupt when + finished */ + TILEM_USER_TIMER_OVERFLOW = 4, /* timer has expired at + least twice since last + mode setting */ + TILEM_USER_TIMER_FINISHED = 256, /* timer has expired at + least once since last + mode setting (port 4 + status bit) */ + TILEM_USER_TIMER_NO_HALT_INT = 512 /* suppress interrupt if + CPU is halted */ +}; + +typedef struct _TilemUserTimer { + byte frequency; + byte loopvalue; + unsigned int status; +} TilemUserTimer; + +/* Reset timers */ +void tilem_user_timers_reset(TilemCalc* calc); + +/* Set frequency control register */ +void tilem_user_timer_set_frequency(TilemCalc* calc, int n, byte value); + +/* Set status flags */ +void tilem_user_timer_set_mode(TilemCalc* calc, int n, byte mode); + +/* Start timer */ +void tilem_user_timer_start(TilemCalc* calc, int n, byte value); + +/* Get timer value */ +byte tilem_user_timer_get_value(TilemCalc* calc, int n); + +/* Callback function */ +void tilem_user_timer_expired(TilemCalc* calc, void* data); + + +/* Calculators */ + +/* Model IDs */ +enum { + TILEM_CALC_TI73 = '7', /* TI-73 / TI-73 Explorer */ + TILEM_CALC_TI76 = 'f', /* TI-76.fr */ + TILEM_CALC_TI81 = '1', /* TI-81 */ + TILEM_CALC_TI82 = '2', /* TI-82 */ + TILEM_CALC_TI83 = '3', /* TI-83 / TI-82 STATS [.fr] */ + TILEM_CALC_TI83P = 'p', /* TI-83 Plus */ + TILEM_CALC_TI83P_SE = 's', /* TI-83 Plus Silver Edition */ + TILEM_CALC_TI84P = '4', /* TI-84 Plus */ + TILEM_CALC_TI84P_SE = 'z', /* TI-84 Plus Silver Edition */ + TILEM_CALC_TI84P_NSPIRE = 'n', /* TI-Nspire 84 Plus emulator */ + TILEM_CALC_TI85 = '5', /* TI-85 */ + TILEM_CALC_TI86 = '6' /* TI-86 */ +}; + +/* Calculator flags */ +enum { + TILEM_CALC_HAS_LINK = 1, /* Has link port */ + TILEM_CALC_HAS_LINK_ASSIST = 2, /* Has hardware link assist */ + TILEM_CALC_HAS_USB = 4, /* Has USB controller */ + TILEM_CALC_HAS_FLASH = 8, /* Has (writable) Flash */ + TILEM_CALC_HAS_T6A04 = 16, /* Has separate LCD driver */ + TILEM_CALC_HAS_MD5_ASSIST = 32 /* Has hardware MD5 assist */ +}; + +/* Calculator hardware description */ +struct _TilemHardware { + char model_id; /* Single character identifying model */ + const char* name; /* Short name (e.g. ti83p) */ + const char* desc; /* Full name (e.g. TI-83 Plus) */ + + unsigned int flags; + + int lcdwidth, lcdheight; /* Size of LCD */ + dword romsize, ramsize; /* Size of ROM and RAM */ + dword lcdmemsize; /* Size of external LCD memory */ + byte rampagemask; /* Bit mask used for RAM page */ + + int nflashsectors; + const TilemFlashSector* flashsectors; + + int nusertimers; + + int nhwregs; /* Number of hardware registers */ + const char** hwregnames; /* Harware register names */ + + int nhwtimers; /* Number of hardware timers */ + const char** hwtimernames; /* Hardware timer names*/ + + const char** keynames; + + /* Reset calculator */ + void (*reset) (TilemCalc*); + + /* Reinitialize after loading state */ + void (*stateloaded) (TilemCalc*, int); + + /* Z80 ports and memory */ + byte (*z80_in) (TilemCalc*, dword); + void (*z80_out) (TilemCalc*, dword, byte); + void (*z80_wrmem) (TilemCalc*, dword, byte); + byte (*z80_rdmem) (TilemCalc*, dword); + byte (*z80_rdmem_m1) (TilemCalc*, dword); + + /* Evaluate a non-standard instruction */ + void (*z80_instr) (TilemCalc*, dword); + + /* Persistent timer callback */ + void (*z80_ptimer) (TilemCalc*, int); + + /* Retrieve LCD contents */ + void (*get_lcd) (TilemCalc*, byte*); + + /* Convert physical <-> logical addresses */ + dword (*mem_ltop) (TilemCalc*, dword); + dword (*mem_ptol) (TilemCalc*, dword); +}; + +/* Current state of the calculator */ +struct _TilemCalc { + TilemHardware hw; + + TilemZ80 z80; + byte* mem; + byte* ram; + byte* lcdmem; + byte mempagemap[4]; + + TilemLCD lcd; + TilemLinkport linkport; + TilemKeypad keypad; + TilemFlash flash; + TilemMD5Assist md5assist; + TilemUserTimer usertimers[TILEM_MAX_USER_TIMERS]; + + byte poweronhalt; /* System power control. If this is + zero, turn off LCD, timers, + etc. when CPU halts */ + + byte battery; /* Battery level (units of 0.1 V) */ + + dword* hwregs; +}; + +/* Get a list of supported hardware models */ +void tilem_get_supported_hardware(const TilemHardware*** models, + int* nmodels); + +/* Create a new calculator. This function returns NULL if + insufficient memory is available. */ +TilemCalc* tilem_calc_new(char id); + +/* Make an exact copy of an existing calculator (including both + internal and external state.) Be careful when using this in + conjunction with custom timer/breakpoint callback functions. This + function returns NULL if insufficient memory is available. */ +TilemCalc* tilem_calc_copy(TilemCalc* calc); + +/* Free a calculator that was previously created by tilem_calc_new() + or tilem_calc_copy(). */ +void tilem_calc_free(TilemCalc* calc); + +/* Reset calculator (essentially, remove and replace batteries.) */ +void tilem_calc_reset(TilemCalc* calc); + +/* Load calculator state from ROM and/or save files. */ +int tilem_calc_load_state(TilemCalc* calc, FILE* romfile, FILE* savfile); + +/* Save calculator state to ROM and/or save files. */ +int tilem_calc_save_state(TilemCalc* calc, FILE* romfile, FILE* savfile); + + +/* LCD image conversion/scaling */ + +/* Scaling algorithms */ +enum { + TILEM_SCALE_FAST = 0, /* Fast scaling (nearest neighbor) - + looks lousy unless the scaling + factor is fairly large */ + TILEM_SCALE_SMOOTH /* Smooth scaling - slower and looks + better at small sizes; note that + this falls back to using the "fast" + algorithm if we are scaling up by + an integer factor */ +}; + +/* Buffer representing a snapshot of the LCD state */ +typedef struct _TilemLCDBuffer { + byte width; /* Width of LCD */ + byte height; /* Height of LCD */ + byte rowstride; /* Offset between rows in buffer */ + byte contrast; /* Contrast value (0-63) */ + dword stamp; /* Timestamp */ + dword tmpbufsize; /* Size of temporary buffer */ + byte *data; /* Image data (rowstride*height bytes) */ + void *tmpbuf; /* Temporary buffer used for scaling */ +} TilemLCDBuffer; + +/* Create new TilemLCDBuffer. */ +TilemLCDBuffer* tilem_lcd_buffer_new(void) + TILEM_ATTR_MALLOC; + +/* Free a TilemLCDBuffer. */ +void tilem_lcd_buffer_free(TilemLCDBuffer *buf); + +/* Convert current LCD memory contents to a TilemLCDBuffer (i.e., a + monochrome snapshot.) */ +void tilem_lcd_get_frame(TilemCalc * restrict calc, + TilemLCDBuffer * restrict buf); + +/* Convert current LCD memory contents to a TilemLCDBuffer (i.e., a + monochrome snapshot.) + Output is only 0 and 1 */ +void tilem_lcd_get_frame1(TilemCalc * restrict calc, + TilemLCDBuffer * restrict buf); + +/* Convert and scale image to an 8-bit indexed image buffer. IMGWIDTH + and IMGHEIGHT are the width and height of the output image, + ROWSTRIDE the number of bytes from the start of one row to the next + (often equal to IMGWIDTH), and SCALETYPE the scaling algorithm to + use. */ +void tilem_draw_lcd_image_indexed(TilemLCDBuffer * restrict frm, + byte * restrict buffer, + int imgwidth, int imgheight, + int rowstride, int scaletype); + +/* Convert and scale image to a 24-bit RGB or 32-bit RGBA image + buffer. IMGWIDTH and IMGHEIGHT are the width and height of the + output image, ROWSTRIDE the number of bytes from the start of one + row to the next (often equal to 3 * IMGWIDTH), PIXBYTES the number + of bytes per pixel (3 or 4), and SCALETYPE the scaling algorithm to + use. PALETTE is an array of 256 color values. */ +void tilem_draw_lcd_image_rgb(TilemLCDBuffer * restrict frm, + byte * restrict buffer, + int imgwidth, int imgheight, int rowstride, + int pixbytes, const dword * restrict palette, + int scaletype); + +/* Calculate a color palette for use with the above functions. + RLIGHT, GLIGHT, BLIGHT are the RGB components (0 to 255) of the + lightest possible color; RDARK, GDARK, BDARK are the RGB components + of the darkest possible color. GAMMA is the gamma value for the + output device (2.2 for most current computer displays and image + file formats.) */ +dword* tilem_color_palette_new(int rlight, int glight, int blight, + int rdark, int gdark, int bdark, + double gamma); + +/* Calculate a color palette, as above, and convert it to a packed + array of bytes (R, G, B) */ +byte* tilem_color_palette_new_packed(int rlight, int glight, int blight, + int rdark, int gdark, int bdark, + double gamma); + + +/* Grayscale LCD simulation */ + +typedef struct _TilemGrayLCD TilemGrayLCD; + +/* Create a new LCD and attach to a calculator. Sampling for + grayscale is done across WINDOWSIZE frames, with samples taken + every SAMPLEINT microseconds. */ +TilemGrayLCD* tilem_gray_lcd_new(TilemCalc *calc, int windowsize, + int sampleint); + +/* Detach and free an LCD. */ +void tilem_gray_lcd_free(TilemGrayLCD *glcd); + +/* Generate a grayscale image for the next frame, based on current + calculator state. This function also updates the frame counter and + internal state; for proper grayscale behavior, this function needs + to be called at regular intervals. */ +void tilem_gray_lcd_get_frame(TilemGrayLCD * restrict glcd, + TilemLCDBuffer * restrict frm); + + +/* Miscellaneous functions */ + +/* Guess calculator type for a ROM file */ +char tilem_guess_rom_type(FILE* romfile); + +/* Get calculator type for a SAV file */ +char tilem_get_sav_type(FILE* savfile); + +/* Check validity of calculator certificate; repair if necessary */ +void tilem_calc_fix_certificate(TilemCalc* calc, byte* cert, + int app_start, int app_end, + unsigned exptab_offset); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tool/tilem-src/emu/tilemint.h b/tool/tilem-src/emu/tilemint.h new file mode 100644 index 0000000..0e95956 --- /dev/null +++ b/tool/tilem-src/emu/tilemint.h @@ -0,0 +1,12 @@ +#ifndef _TILEMINT_H +#define _TILEMINT_H + +#ifdef HAVE_STDINTS1_H +# include +#elif defined(HAVE_STDINTS2_H) +# include +#else +# include +#endif + +#endif diff --git a/tool/tilem-src/emu/timers.c b/tool/tilem-src/emu/timers.c new file mode 100644 index 0000000..967aa73 --- /dev/null +++ b/tool/tilem-src/emu/timers.c @@ -0,0 +1,231 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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" + +void tilem_user_timers_reset(TilemCalc* calc) +{ + int i; + + for (i = 0; i < TILEM_MAX_USER_TIMERS; i++) { + tilem_z80_set_timer(calc, TILEM_TIMER_USER1 + i, 0, 0, 0); + calc->usertimers[i].frequency = 0; + calc->usertimers[i].loopvalue = 0; + calc->usertimers[i].status = 0; + } +} + +static inline dword get_duration(int fvalue, int nticks) +{ + qword t=0; + + if (fvalue & 0x80) { + if (fvalue & 0x20) + return 64 * nticks; + else if (fvalue & 0x10) + return 32 * nticks; + else if (fvalue & 0x08) + return 16 * nticks; + else if (fvalue & 0x04) + return 8 * nticks; + else if (fvalue & 0x02) + return 4 * nticks; + else if (fvalue & 0x01) + return 2 * nticks; + else + return nticks; + } + else if (fvalue & 0x40) { + switch (fvalue & 7) { + case 0: + t = 3000000; + break; + case 1: + t = 33000000; + break; + case 2: + t = 328000000; + break; + case 3: + t = 0xC3530D40; //3277000000; + break; + case 4: + t = 1000000; + break; + case 5: + t = 16000000; + break; + case 6: + t = 256000000; + break; + case 7: + t = 0xF4240000; //4096000000; + break; + } + + return ((t * nticks + 16384) / 32768); + } + + return 0; +} + +static dword get_normal_duration(const TilemUserTimer* tmr) +{ + if (tmr->loopvalue) + return get_duration(tmr->frequency, tmr->loopvalue); + else + return get_duration(tmr->frequency, 256); +} + +static dword get_overflow_duration(const TilemUserTimer* tmr) +{ + return get_duration(tmr->frequency, 256); +} + +void tilem_user_timer_set_frequency(TilemCalc* calc, int n, byte value) +{ + TilemUserTimer* tmr = &calc->usertimers[n]; + + /* writing to frequency control port stops timer; set + loopvalue to the current value of the counter */ + tmr->loopvalue = tilem_user_timer_get_value(calc, n); + tilem_z80_set_timer(calc, TILEM_TIMER_USER1 + n, 0, 0, 0); + tmr->frequency = value; +} + +void tilem_user_timer_set_mode(TilemCalc* calc, int n, byte mode) +{ + TilemUserTimer* tmr = &calc->usertimers[n]; + + /* setting mode clears "finished" and "overflow" flags */ + tmr->status = ((tmr->status & TILEM_USER_TIMER_NO_HALT_INT) + | (mode & 3)); + + calc->z80.interrupts &= ~(TILEM_INTERRUPT_USER_TIMER1 << n); + + /* if timer is currently running, it will not overflow the + next time it expires -> set period to normal */ + if ((mode & TILEM_USER_TIMER_LOOP) || tmr->loopvalue == 0) { + tilem_z80_set_timer_period(calc, TILEM_TIMER_USER1 + n, + get_normal_duration(tmr)); + } + else { + tilem_z80_set_timer_period(calc, TILEM_TIMER_USER1 + n, 0); + } +} + +void tilem_user_timer_start(TilemCalc* calc, int n, byte value) +{ + TilemUserTimer* tmr = &calc->usertimers[n]; + dword count, period; + + tmr->loopvalue = value; + + /* if a valid frequency is set, then writing to value port + starts timer */ + + count = get_normal_duration(tmr); + if (!count) + return; + + if (!value) { + /* input value 0 means loop indefinitely */ + period = get_overflow_duration(tmr); + } + else if (tmr->status & TILEM_USER_TIMER_FINISHED) { + /* timer has already expired once -> it will overflow + the next time it expires (note that this happens + even if the loop flag isn't set) */ + period = get_overflow_duration(tmr); + } + else if (!(tmr->status & TILEM_USER_TIMER_LOOP)) { + /* don't loop */ + period = 0; + } + else { + /* timer hasn't expired yet; second iteration starts + from the same counter value as the first */ + period = count; + } + + tilem_z80_set_timer(calc, TILEM_TIMER_USER1 + n, count, period, + (calc->usertimers[n].frequency & 0x80 ? 0 : 1)); +} + +byte tilem_user_timer_get_value(TilemCalc* calc, int n) +{ + TilemUserTimer* tmr = &calc->usertimers[n]; + dword period; + + if (!tilem_z80_timer_running(calc, TILEM_TIMER_USER1 + n)) + return tmr->loopvalue; + + period = get_overflow_duration(tmr); + + if (tmr->frequency & 0x80) { + dword t = tilem_z80_get_timer_clocks + (calc, TILEM_TIMER_USER1 + n); + return ((t * 256) / period) % 256; + } + else { + qword t = tilem_z80_get_timer_microseconds + (calc, TILEM_TIMER_USER1 + n); + return ((t * 256) / period) % 256; + } +} + +void tilem_user_timer_expired(TilemCalc* calc, void* data) +{ + int n = TILEM_PTR_TO_DWORD(data); + TilemUserTimer* tmr = &calc->usertimers[n]; + + /* input value 0 means loop indefinitely (don't set flags) */ + if (!tmr->loopvalue) { + return; + } + + /* if timer has already finished, set "overflow" flag */ + if (tmr->status & TILEM_USER_TIMER_FINISHED) { + tmr->status |= TILEM_USER_TIMER_OVERFLOW; + } + + /* set "finished" flag */ + tmr->status |= TILEM_USER_TIMER_FINISHED; + + /* generate interrupt if appropriate */ + if ((tmr->status & TILEM_USER_TIMER_INTERRUPT) + && (!(tmr->status & TILEM_USER_TIMER_NO_HALT_INT) + || !calc->z80.halted)) { + calc->z80.interrupts |= (TILEM_INTERRUPT_USER_TIMER1 << n); + } + + if (tmr->status & TILEM_USER_TIMER_LOOP) { + /* timer will overflow the next time it expires + (unless user writes to the mode port before + then) */ + tilem_z80_set_timer_period(calc, TILEM_TIMER_USER1 + n, + get_overflow_duration(tmr)); + } +} diff --git a/tool/tilem-src/emu/x1/x1.h b/tool/tilem-src/emu/x1/x1.h new file mode 100644 index 0000000..eecb19e --- /dev/null +++ b/tool/tilem-src/emu/x1/x1.h @@ -0,0 +1,53 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#ifndef _TILEM_X1_H +#define _TILEM_X1_H + +enum { + HW_VERSION, /* HW version: 0 = unknown + 1 = original HW (1990-1992) + 2 = revised HW (1992-1996) */ + PORT2, /* memory mapping control (new HW) */ + PORT3, /* mask of enabled interrupts */ + PORT4, /* mapping mode, timer control */ + PORT5, /* memory mapping bank A (old HW) */ + PORT6, /* memory mapping bank B (old HW) */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES { "hw_version", "port2", "port3", "port4", "port5", "port6" } + +#define TIMER_INT (TILEM_NUM_SYS_TIMERS + 1) +#define NUM_HW_TIMERS 1 + +#define HW_TIMER_NAMES { "int" } + +void x1_reset(TilemCalc* calc); +byte x1_z80_in(TilemCalc* calc, dword port); +void x1_z80_out(TilemCalc* calc, dword port, byte value); +void x1_z80_ptimer(TilemCalc* calc, int id); +void x1_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte x1_z80_rdmem(TilemCalc* calc, dword addr); +dword x1_mem_ltop(TilemCalc* calc, dword addr); +dword x1_mem_ptol(TilemCalc* calc, dword addr); +void x1_get_lcd(TilemCalc* calc, byte* data); + +#endif diff --git a/tool/tilem-src/emu/x1/x1_init.c b/tool/tilem-src/emu/x1/x1_init.c new file mode 100644 index 0000000..962b4b1 --- /dev/null +++ b/tool/tilem-src/emu/x1/x1_init.c @@ -0,0 +1,50 @@ +/* + * 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 + +#include "x1.h" + +void x1_reset(TilemCalc* calc) +{ + calc->hwregs[PORT3] = 0x08; + calc->hwregs[PORT2] = 0x00; + calc->hwregs[PORT5] = 0x00; + calc->hwregs[PORT6] = 0x00; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x01; + calc->mempagemap[2] = 0x00; + calc->mempagemap[3] = 0x02; + + if (calc->hwregs[HW_VERSION] != 2) + calc->lcd.rowstride = 12; + + tilem_z80_set_speed(calc, 2000); + + /* FIXME: measure actual frequency */ + tilem_z80_set_timer(calc, TIMER_INT, 6000, 6000, 1); +} diff --git a/tool/tilem-src/emu/x1/x1_io.c b/tool/tilem-src/emu/x1/x1_io.c new file mode 100644 index 0000000..b46977d --- /dev/null +++ b/tool/tilem-src/emu/x1/x1_io.c @@ -0,0 +1,237 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x1.h" + +byte x1_z80_in(TilemCalc* calc, dword port) +{ + byte v; + + if (calc->hwregs[HW_VERSION] == 1) + port &= 7; + + switch(port&0x1f) { + case 0x01: + return(tilem_keypad_read_keys(calc)); + + case 0x02: + return(calc->hwregs[PORT2]); + + case 0x03: + v = (calc->keypad.onkeydown ? 0x00: 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + + return(v); + + case 0x05: + return(calc->hwregs[PORT5]); + + case 0x06: + return(calc->hwregs[PORT6]); + + case 0x10: + return(tilem_lcd_t6a04_status(calc)); + + case 0x11: + return(tilem_lcd_t6a04_read(calc)); + } + + tilem_warning(calc, "Input from port %x", port); + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + int pageA, pageB; + + switch (calc->hwregs[HW_VERSION]) { + case 1: + if (calc->hwregs[PORT5] & 0x40) + pageA = 2; + else if (calc->hwregs[PORT5] & 1) + pageA = 1; + else + pageA = 0; + + if (calc->hwregs[PORT6] & 0x40) + pageB = 2; + else if (calc->hwregs[PORT6] & 1) + pageB = 1; + else + pageB = 0; + break; + + case 2: + if (calc->hwregs[PORT2] & 0x40) + pageA = 2; + else if (calc->hwregs[PORT2] & 1) + pageA = 1; + else + pageA = 0; + + if (calc->hwregs[PORT2] & 0x80) + pageB = 2; + else if (calc->hwregs[PORT2] & 8) + pageB = 1; + else + pageB = 0; + break; + + default: + /* unknown HW version - ignore all output values and + use standard mapping */ + pageA = 1; + pageB = 0; + } + + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = 2; +} + +void x1_z80_out(TilemCalc* calc, dword port, byte value) +{ + if (!calc->hwregs[HW_VERSION] && calc->z80.r.pc.d < 0x8000) { + /* detect version */ + if (port == 0x05 || port == 0x06) { + calc->hwregs[HW_VERSION] = 1; + setup_mapping(calc); + } + else if (port == 0x10 || port == 0x11) { + calc->lcd.rowstride = 15; + calc->hwregs[HW_VERSION] = 2; + setup_mapping(calc); + } + } + + if (calc->hwregs[HW_VERSION] == 1) + port &= 7; + + switch(port&0x1f) { + case 0x00: + calc->lcd.addr = ((value & 0x1f) << 8); + calc->z80.lastlcdwrite = calc->z80.clock; + break; + + case 0x01: + tilem_keypad_set_group(calc, value); + break; + + case 0x02: + calc->hwregs[PORT2] = value; + if (calc->hwregs[HW_VERSION] != 2) + calc->lcd.contrast = 16 + (value & 0x1f); + setup_mapping(calc); + break; + + case 0x03: + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) { + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + } + + calc->hwregs[PORT3] = value; + calc->lcd.active = calc->poweronhalt = ((value & 8) >> 3); + break; + + case 0x04: + calc->hwregs[PORT4] = value; + + if (calc->hwregs[HW_VERSION] == 1) { + switch (value & 0x18) { + case 0x00: + calc->lcd.rowstride = 10; + break; + + case 0x08: + calc->lcd.rowstride = 12; + break; + + case 0x10: + calc->lcd.rowstride = 16; + break; + + case 0x18: + calc->lcd.rowstride = 20; + break; + } + calc->z80.lastlcdwrite = calc->z80.clock; + } + break; + + case 0x05: + calc->hwregs[PORT5] = value; + setup_mapping(calc); + break; + + case 0x06: + calc->hwregs[PORT6] = value; + setup_mapping(calc); + break; + + case 0x10: + tilem_lcd_t6a04_control(calc, value); + break; + + case 0x11: + tilem_lcd_t6a04_write(calc, value); + break; + } + + return; +} + +void x1_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + } +} + +void x1_get_lcd(TilemCalc* calc, byte* data) +{ + if (calc->hwregs[HW_VERSION] == 2) + tilem_lcd_t6a04_get_data(calc, data); + else + tilem_lcd_t6a43_get_data(calc, data); +} diff --git a/tool/tilem-src/emu/x1/x1_memory.c b/tool/tilem-src/emu/x1/x1_memory.c new file mode 100644 index 0000000..34c860e --- /dev/null +++ b/tool/tilem-src/emu/x1/x1_memory.c @@ -0,0 +1,71 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x1.h" + +void x1_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + + if (pa >= 0x8000) { + *(calc->mem + 0x8000 + (pa & 0x1fff)) = v; + + if ((((pa - 0x8000 - calc->lcd.addr) >> 6) + < (unsigned) calc->lcd.rowstride) + && calc->hwregs[HW_VERSION] < 2) + calc->z80.lastlcdwrite = calc->z80.clock; + } +} + +byte x1_z80_rdmem(TilemCalc* calc, dword A) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + + if (pa >= 0x8000) + return (*(calc->mem + 0x8000 + (pa & 0x1fff))); + else + return (*(calc->mem + (pa & 0x7fff))); +} + +dword x1_mem_ltop(TilemCalc* calc, dword A) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + + if (pa >= 0x8000) + return (0x8000 + (pa & 0x1fff)); + else + return (pa); +} + +dword x1_mem_ptol(TilemCalc* calc TILEM_ATTR_UNUSED, dword A) +{ + if (A & 0x8000) + return (0xE000 + (A & 0x1fff)); + else + return (A); +} diff --git a/tool/tilem-src/emu/x1/x1_subcore.c b/tool/tilem-src/emu/x1/x1_subcore.c new file mode 100644 index 0000000..2f82856 --- /dev/null +++ b/tool/tilem-src/emu/x1/x1_subcore.c @@ -0,0 +1,56 @@ +/* + * 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 + +#include "x1.h" + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +static const char* keynames[64] = { + "Down", "Left", "Right", "Up", 0, 0, 0, 0, + "Enter", "Add", "Sub", "Mul", "Div", "Power", "Clear", 0, + "Chs", "3", "6", "9", "RParen", "Tan", "Vars", 0, + "DecPnt", "2", "5", "8", "LParen", "Cos", "Prgm", "Mode", + "0", "1", "4", "7", "EE", "Sin", "Matrix", "Graphvar", + "On", "Store", "Ln", "Log", "Square", "Recip", "Math", "Alpha", + "Graph", "Trace", "Zoom", "Range", "YEqu", "2nd", "Ins", "Del", + 0, 0, 0, 0, 0, 0, 0, 0}; + +const TilemHardware hardware_ti81 = { + '1', "ti81", "TI-81", TILEM_CALC_HAS_T6A04, + 96, 64, 0x8000, 0x2000, 15 * 64, 0x40, + 0, NULL, 0, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + keynames, + x1_reset, NULL, + x1_z80_in, x1_z80_out, + x1_z80_wrmem, x1_z80_rdmem, x1_z80_rdmem, NULL, + x1_z80_ptimer, x1_get_lcd, + x1_mem_ltop, x1_mem_ptol }; diff --git a/tool/tilem-src/emu/x2/x2.h b/tool/tilem-src/emu/x2/x2.h new file mode 100644 index 0000000..cfa6093 --- /dev/null +++ b/tool/tilem-src/emu/x2/x2.h @@ -0,0 +1,51 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#ifndef _TILEM_X2_H +#define _TILEM_X2_H + +enum { + HW_VERSION, /* HW version: 0 = unknown + 1 = original HW (1993-2000) + 2 = revised HW (2001-2003) */ + PORT0, /* port 0 (link port control) */ + PORT2, /* port 2 (memory mapping) */ + PORT3, /* mask of enabled interrupts */ + PORT4, /* mapping mode + timer speed */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES { "hw_version", "port0", "port2", "port3", "port4" } + +#define TIMER_INT (TILEM_NUM_SYS_TIMERS + 1) +#define NUM_HW_TIMERS 1 + +#define HW_TIMER_NAMES { "int" } + +void x2_reset(TilemCalc* calc); +byte x2_z80_in(TilemCalc* calc, dword port); +void x2_z80_out(TilemCalc* calc, dword port, byte value); +void x2_z80_ptimer(TilemCalc* calc, int id); +void x2_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte x2_z80_rdmem(TilemCalc* calc, dword addr); +dword x2_mem_ltop(TilemCalc* calc, dword addr); +dword x2_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/x2/x2_init.c b/tool/tilem-src/emu/x2/x2_init.c new file mode 100644 index 0000000..1e83119 --- /dev/null +++ b/tool/tilem-src/emu/x2/x2_init.c @@ -0,0 +1,46 @@ +/* + * 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 + +#include "x2.h" + +void x2_reset(TilemCalc* calc) +{ + calc->hwregs[PORT2] = 0xF8; + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x00; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x00; + calc->mempagemap[2] = 0x09; + calc->mempagemap[3] = 0x08; + + tilem_z80_set_speed(calc, 6000); + + /* FIXME: measure actual frequency */ + tilem_z80_set_timer(calc, TIMER_INT, 1000, 9259, 1); +} diff --git a/tool/tilem-src/emu/x2/x2_io.c b/tool/tilem-src/emu/x2/x2_io.c new file mode 100644 index 0000000..f8e3121 --- /dev/null +++ b/tool/tilem-src/emu/x2/x2_io.c @@ -0,0 +1,207 @@ +/* + * 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 + +#include "x2.h" + +byte x2_z80_in(TilemCalc* calc, dword port) +{ + static const byte battlevel[4] = { 33, 39, 36, 43 }; + byte v, b; + + switch(port&0xff) { + case 0x00: + v = tilem_linkport_get_lines(calc); + + if (calc->hwregs[HW_VERSION] == 1) { + /* HW version 1 uses separate PCR/PDR, as the + TI-85 does. */ + b = (calc->hwregs[PORT0] >> 4) | 0xf0; + return ((calc->hwregs[PORT0] & b) | (v & ~b)); + } + else { + /* HW version 2 uses a TI-83-like interface. + (Do the same if version is unknown, because + most code written for HW version 1 will + work fine with these values.) */ + return (0xc0 | (v * 5)); + } + + case 0x01: + return(tilem_keypad_read_keys(calc)); + + case 0x02: + return(calc->hwregs[PORT2]); + + case 0x03: + v = (calc->keypad.onkeydown ? 0x00: 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + + return(v); + + case 0x10: + return(tilem_lcd_t6a04_status(calc)); + + case 0x11: + return(tilem_lcd_t6a04_read(calc)); + + case 0x14: + /* FIXME: determine value of this port on old + hardware, and the values of bits 1-7. (As on + TI-83, probably mirrors port 0 in both cases.) */ + b = battlevel[calc->hwregs[PORT4] >> 6]; + return(calc->battery >= b ? 1 : 0); + } + + tilem_warning(calc, "Input from port %x", port); + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB; + + /* FIXME: this is all rather hypothetical and untested, but it + makes sense based on how the TI-83 works */ + + if (calc->hwregs[PORT2] & 0x40) { + pageA = (0x08 | (calc->hwregs[PORT2] & 1)); + } + else { + pageA = (calc->hwregs[PORT2] & 7); + } + + if (calc->hwregs[PORT2] & 0x80) { + pageB = (0x08 | ((calc->hwregs[PORT2] >> 3) & 1)); + } + else { + pageB = ((calc->hwregs[PORT2] >> 3) & 7); + } + + if (calc->hwregs[PORT4] & 1) { + calc->mempagemap[1] = (pageA & ~1); + calc->mempagemap[2] = (pageA | 1); + calc->mempagemap[3] = pageB; + } + else { + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = 0x08; + } +} + +void x2_z80_out(TilemCalc* calc, dword port, byte value) +{ + switch(port&0xff) { + case 0x00: + calc->hwregs[PORT0] = value; + + if (calc->hwregs[HW_VERSION] == 1) { + /* HW version 1 */ + value = (((value >> 6) & (value >> 2)) + | ((value >> 4) & ~value)); + } + else if (calc->hwregs[HW_VERSION] == 0) { + /* HW version unknown: auto-detect */ + if ((value & 0xc3) == 0xc0) { + value = ((value >> 2) | (value >> 4)); + } + } + + tilem_linkport_set_lines(calc, value); + break; + + case 0x01: + tilem_keypad_set_group(calc, value); + break; + + case 0x02: + calc->hwregs[PORT2] = value; + setup_mapping(calc); + break; + + case 0x03: + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) { + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + } + + calc->poweronhalt = ((value & 8) >> 3); + calc->hwregs[PORT3] = value; + break; + + case 0x04: + calc->hwregs[PORT4] = value; + + /* Detect hardware version */ + if (calc->z80.r.pc.d < 0x4000) { + if (value & 0x10) + calc->hwregs[HW_VERSION] = 1; + else + calc->hwregs[HW_VERSION] = 2; + } + + /* FIXME: implement changing interrupt frequencies -- + somebody needs to measure them. Also check if bit + 4 works as on 83. */ + + setup_mapping(calc); + break; + + case 0x10: + tilem_lcd_t6a04_control(calc, value); + break; + + case 0x11: + tilem_lcd_t6a04_write(calc, value); + break; + } + + return; +} + +void x2_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + } +} diff --git a/tool/tilem-src/emu/x2/x2_memory.c b/tool/tilem-src/emu/x2/x2_memory.c new file mode 100644 index 0000000..9829f4f --- /dev/null +++ b/tool/tilem-src/emu/x2/x2_memory.c @@ -0,0 +1,70 @@ +/* + * 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 + +#include "x2.h" + +#include + +void x2_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + + if (pa >= 0x28000) + abort(); + + if (pa >= 0x20000) { + *(calc->mem + pa) = v; + } +} + + +byte x2_z80_rdmem(TilemCalc* calc, dword A) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + return (*(calc->mem + pa)); +} + +dword x2_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + return ((page << 14) | (A & 0x3fff)); +} + +dword x2_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + int i; + + for (i = 0; i < 4; i++) { + if (calc->mempagemap[i] == page) { + return ((i << 14) | (A & 0x3fff)); + } + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/x2/x2_subcore.c b/tool/tilem-src/emu/x2/x2_subcore.c new file mode 100644 index 0000000..bdb19fe --- /dev/null +++ b/tool/tilem-src/emu/x2/x2_subcore.c @@ -0,0 +1,57 @@ +/* + * 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 + +#include "x2.h" + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +static const char* keynames[64] = { + "Down", "Left", "Right", "Up", 0, 0, 0, 0, + "Enter", "Add", "Sub", "Mul", "Div", "Power", "Clear", 0, + "Chs", "3", "6", "9", "RParen", "Tan", "Vars", 0, + "DecPnt", "2", "5", "8", "LParen", "Cos", "Prgm", "Stat", + "0", "1", "4", "7", "Comma", "Sin", "Matrix", "Graphvar", + "On", "Store", "Ln", "Log", "Square", "Recip", "Math", "Alpha", + "Graph", "Trace", "Zoom", "Window", "YEqu", "2nd", "Mode", "Del", + 0, 0, 0, 0, 0, 0, 0, 0}; + +const TilemHardware hardware_ti82 = { + '2', "ti82", "TI-82", + (TILEM_CALC_HAS_LINK | TILEM_CALC_HAS_T6A04), + 96, 64, 8 * 0x4000, 0x8000, 15 * 64, 0x40, + 0, NULL, 0, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + keynames, + x2_reset, NULL, + x2_z80_in, x2_z80_out, + x2_z80_wrmem, x2_z80_rdmem, x2_z80_rdmem, NULL, + x2_z80_ptimer, tilem_lcd_t6a04_get_data, + x2_mem_ltop, x2_mem_ptol }; diff --git a/tool/tilem-src/emu/x3/x3.h b/tool/tilem-src/emu/x3/x3.h new file mode 100644 index 0000000..78cbd78 --- /dev/null +++ b/tool/tilem-src/emu/x3/x3.h @@ -0,0 +1,50 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#ifndef _TILEM_X3_H +#define _TILEM_X3_H + +enum { + PORT2, /* port 2 (memory mapping) */ + PORT3, /* mask of enabled interrupts */ + PORT4, /* mapping mode + timer speed */ + ROM_BANK, /* current ROM bank for port 2 mapping */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES { "port2", "port3", "port4", "rom_bank" } + +#define TIMER_INT1 (TILEM_NUM_SYS_TIMERS + 1) +#define TIMER_INT2A (TILEM_NUM_SYS_TIMERS + 2) +#define TIMER_INT2B (TILEM_NUM_SYS_TIMERS + 3) +#define NUM_HW_TIMERS 3 + +#define HW_TIMER_NAMES { "int1", "int2a", "int2b" } + +void x3_reset(TilemCalc* calc); +byte x3_z80_in(TilemCalc* calc, dword port); +void x3_z80_out(TilemCalc* calc, dword port, byte value); +void x3_z80_ptimer(TilemCalc* calc, int id); +void x3_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte x3_z80_rdmem(TilemCalc* calc, dword addr); +dword x3_mem_ltop(TilemCalc* calc, dword addr); +dword x3_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/x3/x3_init.c b/tool/tilem-src/emu/x3/x3_init.c new file mode 100644 index 0000000..84703a8 --- /dev/null +++ b/tool/tilem-src/emu/x3/x3_init.c @@ -0,0 +1,48 @@ +/* + * 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 + +#include "x3.h" + +void x3_reset(TilemCalc* calc) +{ + calc->hwregs[PORT2] = 0xF8; + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x00; + calc->hwregs[ROM_BANK] = 0x00; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x00; + calc->mempagemap[2] = 0x11; + calc->mempagemap[3] = 0x10; + + tilem_z80_set_speed(calc, 6000); + + tilem_z80_set_timer(calc, TIMER_INT1, 1600, 9259, 1); + tilem_z80_set_timer(calc, TIMER_INT2A, 1300, 9259, 1); + tilem_z80_set_timer(calc, TIMER_INT2B, 1000, 9259, 1); +} diff --git a/tool/tilem-src/emu/x3/x3_io.c b/tool/tilem-src/emu/x3/x3_io.c new file mode 100644 index 0000000..9db41c2 --- /dev/null +++ b/tool/tilem-src/emu/x3/x3_io.c @@ -0,0 +1,210 @@ +/* + * 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 + +#include "x3.h" + +byte x3_z80_in(TilemCalc* calc, dword port) +{ + static const byte battlevel[4] = { 33, 39, 36, 43 }; + byte v, b; + + switch(port&0x1f) { + case 0x00: + case 0x04: + case 0x08: + case 0x0C: + v = tilem_linkport_get_lines(calc); + return((calc->hwregs[ROM_BANK] << 1) | (v * 5)); + + case 0x01: + case 0x05: + case 0x09: + case 0x0D: + return(tilem_keypad_read_keys(calc)); + + case 0x02: + case 0x06: + case 0x0A: + case 0x0E: + case 0x16: + case 0x1E: + return(calc->hwregs[PORT2]); + + case 0x03: + case 0x07: + case 0x0B: + case 0x0F: + case 0x17: + case 0x1F: + v = (calc->keypad.onkeydown ? 0x00 : 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + + return(v); + + case 0x10: + case 0x12: + case 0x18: + case 0x1A: + return(tilem_lcd_t6a04_status(calc)); + + case 0x11: + case 0x13: + case 0x19: + case 0x1B: + return(tilem_lcd_t6a04_read(calc)); + + case 0x14: + case 0x1C: + b = battlevel[calc->hwregs[PORT4] >> 6]; + v = tilem_linkport_get_lines(calc); + return((calc->battery >= b ? 1 : 0) + | (calc->hwregs[ROM_BANK] << 1) | (v << 2)); + + case 0x15: + case 0x1D: + return(tilem_keypad_read_keys(calc) & ~1); + } + + tilem_warning(calc, "Input from port %x", port); + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB; + + if (calc->hwregs[PORT2] & 0x40) { + pageA = (0x10 | (calc->hwregs[PORT2] & 1)); + } + else { + pageA = (calc->hwregs[ROM_BANK] | (calc->hwregs[PORT2] & 7)); + } + + if (calc->hwregs[PORT2] & 0x80) { + pageB = (0x10 | ((calc->hwregs[PORT2] >> 3) & 1)); + } + else { + pageB = (calc->hwregs[ROM_BANK] | ((calc->hwregs[PORT2] >> 3) & 7)); + } + + if (calc->hwregs[PORT4] & 1) { + calc->mempagemap[1] = (pageA & ~1); + calc->mempagemap[2] = (pageA | 1); + calc->mempagemap[3] = pageB; + } + else { + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = 0x10; + } +} + +void x3_z80_out(TilemCalc* calc, dword port, byte value) +{ + static const int tmrvalues[8] = { 1667, 1852, 3889, 4321, + 6111, 6790, 8333, 9259 }; + int t; + + switch(port&0x1f) { + case 0x00: + calc->hwregs[ROM_BANK] = ((value & 0x10) >> 1); + tilem_linkport_set_lines(calc, value); + setup_mapping(calc); + break; + + case 0x01: + tilem_keypad_set_group(calc, value); + break; + + case 0x02: + calc->hwregs[PORT2] = value; + setup_mapping(calc); + break; + + case 0x03: + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + + if (!(value & 0x04)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER2; + + calc->poweronhalt = ((value & 8) >> 3); + calc->hwregs[PORT3] = value; + break; + + case 0x04: + calc->hwregs[PORT4] = value; + + t = tmrvalues[((value >> 4) & 1) | (value & 6)]; + tilem_z80_set_timer_period(calc, TIMER_INT1, t); + tilem_z80_set_timer_period(calc, TIMER_INT2A, t); + tilem_z80_set_timer_period(calc, TIMER_INT2B, t); + + setup_mapping(calc); + break; + + case 0x10: + tilem_lcd_t6a04_control(calc, value); + break; + + case 0x11: + tilem_lcd_t6a04_write(calc, value); + break; + } + + return; +} + +void x3_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT1: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + + case TIMER_INT2A: + case TIMER_INT2B: + if (calc->hwregs[PORT3] & 0x04) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER2; + break; + } +} diff --git a/tool/tilem-src/emu/x3/x3_memory.c b/tool/tilem-src/emu/x3/x3_memory.c new file mode 100644 index 0000000..ede1da5 --- /dev/null +++ b/tool/tilem-src/emu/x3/x3_memory.c @@ -0,0 +1,64 @@ +/* + * 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 + +#include "x3.h" + +void x3_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + + if (pa >= 0x40000) { + *(calc->mem + pa) = v; + } +} + +byte x3_z80_rdmem(TilemCalc* calc, dword A) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + return (*(calc->mem + pa)); +} + +dword x3_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + return ((page << 14) | (A & 0x3fff)); +} + +dword x3_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + int i; + + for (i = 0; i < 4; i++) { + if (calc->mempagemap[i] == page) { + return ((i << 14) | (A & 0x3fff)); + } + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/x3/x3_subcore.c b/tool/tilem-src/emu/x3/x3_subcore.c new file mode 100644 index 0000000..a0c95c2 --- /dev/null +++ b/tool/tilem-src/emu/x3/x3_subcore.c @@ -0,0 +1,71 @@ +/* + * 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 + +#include "x3.h" + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +static const char* keynames[64] = { + "Down", "Left", "Right", "Up", 0, 0, 0, 0, + "Enter", "Add", "Sub", "Mul", "Div", "Power", "Clear", 0, + "Chs", "3", "6", "9", "RParen", "Tan", "Vars", 0, + "DecPnt", "2", "5", "8", "LParen", "Cos", "Prgm", "Stat", + "0", "1", "4", "7", "Comma", "Sin", "Matrix", "Graphvar", + "On", "Store", "Ln", "Log", "Square", "Recip", "Math", "Alpha", + "Graph", "Trace", "Zoom", "Window", "YEqu", "2nd", "Mode", "Del", + 0, 0, 0, 0, 0, 0, 0, 0}; + +const TilemHardware hardware_ti83 = { + '3', "ti83", "TI-83 / TI-82 STATS", + (TILEM_CALC_HAS_LINK | TILEM_CALC_HAS_T6A04), + 96, 64, 16 * 0x4000, 0x8000, 15 * 64, 0x40, + 0, NULL, 0, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + keynames, + x3_reset, NULL, + x3_z80_in, x3_z80_out, + x3_z80_wrmem, x3_z80_rdmem, x3_z80_rdmem, NULL, + x3_z80_ptimer, tilem_lcd_t6a04_get_data, + x3_mem_ltop, x3_mem_ptol }; + +const TilemHardware hardware_ti76 = { + 'f', "ti76", "TI-76.fr", + (TILEM_CALC_HAS_LINK | TILEM_CALC_HAS_T6A04), + 96, 64, 16 * 0x4000, 0x8000, 15 * 64, 0x40, + 0, NULL, 0, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + keynames, + x3_reset, NULL, + x3_z80_in, x3_z80_out, + x3_z80_wrmem, x3_z80_rdmem, x3_z80_rdmem, NULL, + x3_z80_ptimer, tilem_lcd_t6a04_get_data, + x3_mem_ltop, x3_mem_ptol }; diff --git a/tool/tilem-src/emu/x4/x4.h b/tool/tilem-src/emu/x4/x4.h new file mode 100644 index 0000000..9ca55dd --- /dev/null +++ b/tool/tilem-src/emu/x4/x4.h @@ -0,0 +1,105 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2009-2011 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 + * . + */ + +#ifndef _TILEM_X4_H +#define _TILEM_X4_H + +enum { + PORT3, /* mask of enabled interrupts */ + PORT4, /* interrupt timer speed */ + PORT5, /* memory mapping bank C */ + PORT6, /* memory mapping bank A */ + PORT7, /* memory mapping bank B */ + PORT8, /* link assist mode flags */ + PORT9, /* unknown (link assist settings?) */ + PORTA, /* unknown (timeout value?) */ + PORTB, /* unknown (timeout value?) */ + PORTC, /* unknown (timeout value?) */ + PORTD, /* unknown */ + PORTE, /* unknown */ + PORTF, /* unknown */ + + PORT20, /* CPU speed control */ + PORT21, /* hardware type / RAM no-exec control */ + PORT22, /* Flash no-exec lower limit */ + PORT23, /* Flash no-exec upper limit */ + PORT25, /* RAM no-exec lower limit */ + PORT26, /* RAM no-exec upper limit */ + PORT27, /* bank C forced-page-0 limit */ + PORT28, /* bank B forced-page-1 limit */ + PORT29, /* LCD port delay (6 MHz) */ + PORT2A, /* LCD port delay (mode 1) */ + PORT2B, /* LCD port delay (mode 2) */ + PORT2C, /* LCD port delay (mode 3) */ + PORT2D, /* unknown */ + PORT2E, /* memory delay */ + PORT2F, /* Duration of LCD wait timer */ + + CLOCK_MODE, /* clock mode */ + CLOCK_INPUT, /* clock input register */ + CLOCK_DIFF, /* clock value minus actual time */ + + RAM_READ_DELAY, + RAM_WRITE_DELAY, + RAM_EXEC_DELAY, + FLASH_READ_DELAY, + FLASH_WRITE_DELAY, + FLASH_EXEC_DELAY, + LCD_PORT_DELAY, + NO_EXEC_RAM_MASK, + NO_EXEC_RAM_LOWER, + NO_EXEC_RAM_UPPER, + + LCD_WAIT, /* LCD wait timer active */ + PROTECTSTATE, /* port protection state */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES \ + { "port3", "port4", "port5", "port6", "port7", "port8", "port9", \ + "portA", "portB", "portC", "portD", "portE", "portF", "port20", \ + "port21", "port22", "port23", "port25", "port26", "port27", \ + "port28", "port29", "port2A", "port2B", "port2C", "port2D", \ + "port2E", "port2F", "clock_mode", "clock_input", "clock_diff", \ + "ram_read_delay", "ram_write_delay", "ram_exec_delay", \ + "flash_read_delay", "flash_write_delay", "flash_exec_delay", \ + "lcd_port_delay", "no_exec_ram_mask", "no_exec_ram_lower", \ + "no_exec_ram_upper", "lcd_wait", "protectstate" } + +#define TIMER_INT1 (TILEM_NUM_SYS_TIMERS + 1) +#define TIMER_INT2A (TILEM_NUM_SYS_TIMERS + 2) +#define TIMER_INT2B (TILEM_NUM_SYS_TIMERS + 3) +#define TIMER_LCD_WAIT (TILEM_NUM_SYS_TIMERS + 4) +#define NUM_HW_TIMERS 4 + +#define HW_TIMER_NAMES { "int1", "int2a", "int2b", "lcd_wait" } + +void x4_reset(TilemCalc* calc); +void x4_stateloaded(TilemCalc* calc, int savtype); +byte x4_z80_in(TilemCalc* calc, dword port); +void x4_z80_out(TilemCalc* calc, dword port, byte value); +void x4_z80_ptimer(TilemCalc* calc, int id); +void x4_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte x4_z80_rdmem(TilemCalc* calc, dword addr); +byte x4_z80_rdmem_m1(TilemCalc* calc, dword addr); +dword x4_mem_ltop(TilemCalc* calc, dword addr); +dword x4_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/x4/x4_init.c b/tool/tilem-src/emu/x4/x4_init.c new file mode 100644 index 0000000..41f7477 --- /dev/null +++ b/tool/tilem-src/emu/x4/x4_init.c @@ -0,0 +1,87 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x4.h" + +void x4_reset(TilemCalc* calc) +{ + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x07; + calc->hwregs[PORT6] = 0x3F; + calc->hwregs[PORT7] = 0x3F; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x3E; + calc->mempagemap[2] = 0x3F; + calc->mempagemap[3] = 0x3F; + + calc->z80.r.pc.d = 0x8000; + + calc->hwregs[PORT8] = 0x80; + + calc->hwregs[PORT20] = 0; + calc->hwregs[PORT21] = 0; + calc->hwregs[PORT22] = 0x08; + calc->hwregs[PORT23] = 0x29; + calc->hwregs[PORT25] = 0x10; + calc->hwregs[PORT26] = 0x20; + calc->hwregs[PORT27] = 0; + calc->hwregs[PORT28] = 0; + calc->hwregs[PORT29] = 0x14; + calc->hwregs[PORT2A] = 0x27; + calc->hwregs[PORT2B] = 0x2F; + calc->hwregs[PORT2C] = 0x3B; + calc->hwregs[PORT2D] = 0x01; + calc->hwregs[PORT2E] = 0x44; + calc->hwregs[PORT2F] = 0x4A; + + calc->hwregs[FLASH_READ_DELAY] = 0; + calc->hwregs[FLASH_WRITE_DELAY] = 0; + calc->hwregs[FLASH_EXEC_DELAY] = 0; + calc->hwregs[RAM_READ_DELAY] = 0; + calc->hwregs[RAM_WRITE_DELAY] = 0; + calc->hwregs[RAM_EXEC_DELAY] = 0; + calc->hwregs[LCD_PORT_DELAY] = 5; + calc->hwregs[NO_EXEC_RAM_MASK] = 0x7C00; + calc->hwregs[NO_EXEC_RAM_LOWER] = 0x4000; + calc->hwregs[NO_EXEC_RAM_UPPER] = 0x8000; + + calc->hwregs[PROTECTSTATE] = 0; + + tilem_z80_set_speed(calc, 6000); + + tilem_z80_set_timer(calc, TIMER_INT1, 1600, 9277, 1); + tilem_z80_set_timer(calc, TIMER_INT2A, 1300, 9277, 1); + tilem_z80_set_timer(calc, TIMER_INT2B, 1000, 9277, 1); +} + +void x4_stateloaded(TilemCalc* calc, int savtype TILEM_ATTR_UNUSED) +{ + tilem_calc_fix_certificate(calc, calc->mem + (0x3E * 0x4000L), + 0x29, 0x0c, 0x1e50); +} diff --git a/tool/tilem-src/emu/x4/x4_io.c b/tool/tilem-src/emu/x4/x4_io.c new file mode 100644 index 0000000..e4f086b --- /dev/null +++ b/tool/tilem-src/emu/x4/x4_io.c @@ -0,0 +1,747 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 +#include + +#include "x4.h" + +static void set_lcd_wait_timer(TilemCalc* calc) +{ + static const int delaytime[8] = { 48, 112, 176, 240, + 304, 368, 432, 496 }; + int i; + + switch (calc->hwregs[PORT20] & 3) { + case 0: + return; + case 1: + i = (calc->hwregs[PORT2F] & 3); + break; + case 2: + i = ((calc->hwregs[PORT2F] >> 2) & 7); + break; + default: + i = ((calc->hwregs[PORT2F] >> 5) & 7); + break; + } + + tilem_z80_set_timer(calc, TIMER_LCD_WAIT, delaytime[i], 0, 0); + calc->hwregs[LCD_WAIT] = 1; +} + +byte x4_z80_in(TilemCalc* calc, dword port) +{ + /* FIXME: measure actual levels */ + static const byte battlevel[4] = { 33, 39, 36, 43 }; + byte v; + unsigned int f; + time_t curtime; + + switch(port&0xff) { + case 0x00: + v = tilem_linkport_get_lines(calc); + v |= (calc->linkport.lines << 4); + return(v); + + case 0x01: + return(tilem_keypad_read_keys(calc)); + + case 0x02: + v = battlevel[calc->hwregs[PORT4] >> 6]; + return ((calc->battery >= v ? 0xe1 : 0xe0) + | (calc->hwregs[LCD_WAIT] ? 0 : 2) + | (calc->flash.unlock << 2)); + + case 0x03: + return(calc->hwregs[PORT3]); + + case 0x04: + v = (calc->keypad.onkeydown ? 0x00 : 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER2) + v |= 0x04; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ACTIVE) + v |= 0x10; + + if (calc->usertimers[0].status & TILEM_USER_TIMER_FINISHED) + v |= 0x20; + if (calc->usertimers[1].status & TILEM_USER_TIMER_FINISHED) + v |= 0x40; + if (calc->usertimers[2].status & TILEM_USER_TIMER_FINISHED) + v |= 0x80; + + return(v); + + case 0x05: + return(calc->hwregs[PORT5] & 0x0f); + + case 0x06: + return(calc->hwregs[PORT6]); + + case 0x07: + return(calc->hwregs[PORT7]); + + case 0x08: + return(calc->hwregs[PORT8]); + + case 0x09: + f = tilem_linkport_get_assist_flags(calc); + + if (f & (TILEM_LINK_ASSIST_READ_BUSY + | TILEM_LINK_ASSIST_WRITE_BUSY)) + v = 0x00; + else + v = 0x20; + + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_READ) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_IDLE) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ERROR) + v |= 0x04; + if (f & TILEM_LINK_ASSIST_READ_BUSY) + v |= 0x08; + if (f & TILEM_LINK_ASSIST_READ_BYTE) + v |= 0x10; + if (f & (TILEM_LINK_ASSIST_READ_ERROR + | TILEM_LINK_ASSIST_WRITE_ERROR)) + v |= 0x40; + if (f & TILEM_LINK_ASSIST_WRITE_BUSY) + v |= 0x80; + + calc->z80.interrupts &= ~TILEM_INTERRUPT_LINK_ERROR; + + return(v); + + case 0x0A: + v = calc->linkport.assistlastbyte; + tilem_linkport_read_byte(calc); + return(v); + + case 0x0E: + return(calc->hwregs[PORTE] & 3); + + case 0x0F: + return(calc->hwregs[PORTF] & 3); + + case 0x10: + case 0x12: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + return(tilem_lcd_t6a04_status(calc)); + + case 0x11: + case 0x13: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + return(tilem_lcd_t6a04_read(calc)); + + case 0x15: + return(0x45); /* ??? */ + + case 0x1C: + return(tilem_md5_assist_get_value(calc)); + + case 0x1D: + return(tilem_md5_assist_get_value(calc) >> 8); + + case 0x1E: + return(tilem_md5_assist_get_value(calc) >> 16); + + case 0x1F: + return(tilem_md5_assist_get_value(calc) >> 24); + + case 0x20: + return(calc->hwregs[PORT20] & 3); + + case 0x21: + return(calc->hwregs[PORT21] & 0x33); + + case 0x22: + return(calc->hwregs[PORT22]); + + case 0x23: + return(calc->hwregs[PORT23]); + + case 0x25: + return(calc->hwregs[PORT25]); + + case 0x26: + return(calc->hwregs[PORT26]); + + case 0x27: + return(calc->hwregs[PORT27]); + + case 0x28: + return(calc->hwregs[PORT28]); + + case 0x29: + return(calc->hwregs[PORT29]); + + case 0x2A: + return(calc->hwregs[PORT2A]); + + case 0x2B: + return(calc->hwregs[PORT2B]); + + case 0x2C: + return(calc->hwregs[PORT2C]); + + case 0x2D: + return(calc->hwregs[PORT2D] & 3); + + case 0x2E: + return(calc->hwregs[PORT2E]); + + case 0x2F: + return(calc->hwregs[PORT2F]); + + case 0x30: + return(calc->usertimers[0].frequency); + case 0x31: + return(calc->usertimers[0].status); + case 0x32: + return(tilem_user_timer_get_value(calc, 0)); + + case 0x33: + return(calc->usertimers[1].frequency); + case 0x34: + return(calc->usertimers[1].status); + case 0x35: + return(tilem_user_timer_get_value(calc, 1)); + + case 0x36: + return(calc->usertimers[2].frequency); + case 0x37: + return(calc->usertimers[2].status); + case 0x38: + return(tilem_user_timer_get_value(calc, 2)); + + case 0x39: + return(0xf0); /* ??? */ + + case 0x40: + return calc->hwregs[CLOCK_MODE]; + + case 0x41: + return calc->hwregs[CLOCK_INPUT]&0xff; + + case 0x42: + return (calc->hwregs[CLOCK_INPUT]>>8)&0xff; + + case 0x43: + return (calc->hwregs[CLOCK_INPUT]>>16)&0xff; + + case 0x44: + return (calc->hwregs[CLOCK_INPUT]>>24)&0xff; + + case 0x45: + case 0x46: + case 0x47: + case 0x48: + if (calc->hwregs[CLOCK_MODE] & 1) { + time(&curtime); + } + else { + curtime = 0; + } + curtime += calc->hwregs[CLOCK_DIFF]; + return (curtime >> ((port - 0x45) * 8)); + + case 0x4C: + return(0x22); + + case 0x4D: + /* USB port - not emulated, calculator should + recognize that the USB cable is + disconnected. + + Thanks go to Dan Englender for these + values. */ + + return(0xA5); + + case 0x55: + return(0x1F); + + case 0x56: + return(0x00); + + case 0x57: + return(0x50); + + case 0x0B: + case 0x0C: + case 0x0D: + case 0x14: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + return(0); + } + + tilem_warning(calc, "Input from port %x", port); + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB, pageC; + + if (calc->hwregs[PORT6] & 0x80) + pageA = (0x40 | (calc->hwregs[PORT6] & 7)); + else + pageA = (calc->hwregs[PORT6] & 0x3f); + + if (calc->hwregs[PORT7] & 0x80) + pageB = (0x40 | (calc->hwregs[PORT7] & 7)); + else + pageB = (calc->hwregs[PORT7] & 0x3f); + + pageC = (0x40 | (calc->hwregs[PORT5] & 7)); + + if (calc->hwregs[PORT4] & 1) { + calc->mempagemap[1] = (pageA & ~1); + calc->mempagemap[2] = (pageA | 1); + calc->mempagemap[3] = pageB; + } + else { + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = pageC; + } +} + +static void setup_clockdelays(TilemCalc* calc) +{ + byte lcdport = calc->hwregs[PORT29 + (calc->hwregs[PORT20] & 3)]; + byte memport = calc->hwregs[PORT2E]; + + if (!(lcdport & 1)) + memport &= ~0x07; + if (!(lcdport & 2)) + memport &= ~0x70; + + calc->hwregs[FLASH_EXEC_DELAY] = (memport & 1); + calc->hwregs[FLASH_READ_DELAY] = ((memport >> 1) & 1); + calc->hwregs[FLASH_WRITE_DELAY] = ((memport >> 2) & 1); + + calc->hwregs[RAM_EXEC_DELAY] = ((memport >> 4) & 1); + calc->hwregs[RAM_READ_DELAY] = ((memport >> 5) & 1); + calc->hwregs[RAM_WRITE_DELAY] = ((memport >> 6) & 1); + + calc->hwregs[LCD_PORT_DELAY] = (lcdport >> 2); +} + +void x4_z80_out(TilemCalc* calc, dword port, byte value) +{ + static const int tmrvalues[4] = { 1953, 4395, 6836, 9277 }; + int t, r; + unsigned int mode; + time_t curtime; + + switch(port&0xff) { + case 0x00: + tilem_linkport_set_lines(calc, value); + break; + + case 0x01: + tilem_keypad_set_group(calc, value); + break; + + case 0x03: + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + + if (!(value & 0x04)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER2; + + if (value & 0x06) { + calc->usertimers[0].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[1].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[2].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + } + else { + calc->usertimers[0].status |= TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[1].status |= TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[2].status |= TILEM_USER_TIMER_NO_HALT_INT; + } + + mode = calc->linkport.mode; + if (value & 0x10) + mode |= TILEM_LINK_MODE_INT_ON_ACTIVE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ACTIVE; + + tilem_linkport_set_mode(calc, mode); + + calc->poweronhalt = ((value & 8) >> 3); + calc->hwregs[PORT3] = value; + break; + + + case 0x04: + calc->hwregs[PORT4] = value; + + t = tmrvalues[(value & 6) >> 1]; + tilem_z80_set_timer_period(calc, TIMER_INT1, t); + tilem_z80_set_timer_period(calc, TIMER_INT2A, t); + tilem_z80_set_timer_period(calc, TIMER_INT2B, t); + + setup_mapping(calc); + break; + + case 0x05: + calc->hwregs[PORT5] = value & 0x0f; + setup_mapping(calc); + break; + + case 0x06: + calc->hwregs[PORT6] = value; + setup_mapping(calc); + break; + + case 0x07: + calc->hwregs[PORT7] = value; + setup_mapping(calc); + break; + + case 0x08: + calc->hwregs[PORT8] = value; + + mode = calc->linkport.mode; + + if (value & 0x01) + mode |= TILEM_LINK_MODE_INT_ON_READ; + else + mode &= ~TILEM_LINK_MODE_INT_ON_READ; + + if (value & 0x02) + mode |= TILEM_LINK_MODE_INT_ON_IDLE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_IDLE; + + if (value & 0x04) + mode |= TILEM_LINK_MODE_INT_ON_ERROR; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ERROR; + + if (value & 0x80) + mode &= ~TILEM_LINK_MODE_ASSIST; + else + mode |= TILEM_LINK_MODE_ASSIST; + + tilem_linkport_set_mode(calc, mode); + break; + + case 0x09: + calc->hwregs[PORT9] = value; + break; + + case 0x0A: + calc->hwregs[PORTA] = value; + break; + + case 0x0B: + calc->hwregs[PORTB] = value; + break; + + case 0x0C: + calc->hwregs[PORTC] = value; + break; + + + case 0x0D: + if (!(calc->hwregs[PORT8] & 0x80)) + tilem_linkport_write_byte(calc, value); + break; + + case 0x0E: + calc->hwregs[PORTE] = value; + break; + + case 0x0F: + calc->hwregs[PORTF] = value; + break; + + case 0x10: + case 0x12: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + tilem_lcd_t6a04_control(calc, value); + break; + + case 0x11: + case 0x13: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + tilem_lcd_t6a04_write(calc, value); + break; + + case 0x14: + if (calc->hwregs[PROTECTSTATE] == 7) { + if (value & 1) + tilem_message(calc, "Flash unlocked"); + else + tilem_message(calc, "Flash locked"); + calc->flash.unlock = value&1; + } + else { + tilem_warning(calc, "Writing to protected port 14"); + } + break; + + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + r = (port & 0xff) - 0x18; + calc->md5assist.regs[r] >>= 8; + calc->md5assist.regs[r] |= (value << 24); + break; + + case 0x1E: + calc->md5assist.shift = value & 0x1f; + break; + + case 0x1F: + calc->md5assist.mode = value & 3; + break; + + case 0x20: + calc->hwregs[PORT20] = value; + + if (value & 3) { + tilem_z80_set_speed(calc, 15000); + } + else { + tilem_z80_set_speed(calc, 6000); + } + + setup_clockdelays(calc); + break; + + case 0x21: + if (calc->flash.unlock) { + calc->hwregs[PORT21] = value; + t = (value >> 4) & 3; + calc->hwregs[NO_EXEC_RAM_MASK] = (0x8000 << t) - 0x400; + calc->flash.overridegroup = value & 3; + } + else { + tilem_warning(calc, "Writing to protected port 21"); + } + break; + + case 0x22: + if (calc->flash.unlock) { + calc->hwregs[PORT22] = value; + } + else { + tilem_warning(calc, "Writing to protected port 22"); + } + break; + + case 0x23: + if (calc->flash.unlock) { + calc->hwregs[PORT23] = value; + } + else { + tilem_warning(calc, "Writing to protected port 23"); + } + break; + + case 0x25: + if (calc->flash.unlock) { + calc->hwregs[PORT25] = value; + calc->hwregs[NO_EXEC_RAM_LOWER] = value * 0x400; + } + else { + tilem_warning(calc, "Writing to protected port 25"); + } + break; + + case 0x26: + if (calc->flash.unlock) { + calc->hwregs[PORT26] = value; + calc->hwregs[NO_EXEC_RAM_UPPER] = value * 0x400; + } + else { + tilem_warning(calc, "Writing to protected port 26"); + } + break; + + case 0x27: + calc->hwregs[PORT27] = value; + break; + + case 0x28: + calc->hwregs[PORT28] = value; + break; + + case 0x29: + calc->hwregs[PORT29] = value; + setup_clockdelays(calc); + break; + + case 0x2A: + calc->hwregs[PORT2A] = value; + setup_clockdelays(calc); + break; + + case 0x2B: + calc->hwregs[PORT2B] = value; + setup_clockdelays(calc); + break; + + case 0x2C: + calc->hwregs[PORT2C] = value; + setup_clockdelays(calc); + break; + + case 0x2D: + calc->hwregs[PORT2D] = value; + break; + + case 0x2E: + calc->hwregs[PORT2E] = value; + setup_clockdelays(calc); + break; + + case 0x2F: + calc->hwregs[PORT2F] = value; + break; + + case 0x30: + tilem_user_timer_set_frequency(calc, 0, value); + break; + case 0x31: + tilem_user_timer_set_mode(calc, 0, value); + break; + case 0x32: + tilem_user_timer_start(calc, 0, value); + break; + + case 0x33: + tilem_user_timer_set_frequency(calc, 1, value); + break; + case 0x34: + tilem_user_timer_set_mode(calc, 1, value); + break; + case 0x35: + tilem_user_timer_start(calc, 1, value); + break; + + case 0x36: + tilem_user_timer_set_frequency(calc, 2, value); + break; + case 0x37: + tilem_user_timer_set_mode(calc, 2, value); + break; + case 0x38: + tilem_user_timer_start(calc, 2, value); + break; + + case 0x40: + time(&curtime); + + if ((calc->hwregs[CLOCK_MODE] & 1) != (value & 1)) { + if (value & 1) + calc->hwregs[CLOCK_DIFF] -= curtime; + else + calc->hwregs[CLOCK_DIFF] += curtime; + } + + if (!(calc->hwregs[CLOCK_MODE] & 2) && (value & 2)) { + calc->hwregs[CLOCK_DIFF] = calc->hwregs[CLOCK_INPUT]; + if (value & 1) + calc->hwregs[CLOCK_DIFF] -= curtime; + } + calc->hwregs[CLOCK_MODE] = value & 3; + break; + + case 0x41: + calc->hwregs[CLOCK_INPUT] &= 0xffffff00; + calc->hwregs[CLOCK_INPUT] |= value; + break; + + case 0x42: + calc->hwregs[CLOCK_INPUT] &= 0xffff00ff; + calc->hwregs[CLOCK_INPUT] |= (value << 8); + break; + + case 0x43: + calc->hwregs[CLOCK_INPUT] &= 0xff00ffff; + calc->hwregs[CLOCK_INPUT] |= (value << 16); + break; + + case 0x44: + calc->hwregs[CLOCK_INPUT] &= 0x00ffffff; + calc->hwregs[CLOCK_INPUT] |= (value << 24); + break; + } + + return; +} + +void x4_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT1: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + + case TIMER_INT2A: + case TIMER_INT2B: + if (calc->hwregs[PORT3] & 0x04) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER2; + break; + + case TIMER_LCD_WAIT: + calc->hwregs[LCD_WAIT] = 0; + break; + } +} diff --git a/tool/tilem-src/emu/x4/x4_memory.c b/tool/tilem-src/emu/x4/x4_memory.c new file mode 100644 index 0000000..f33827e --- /dev/null +++ b/tool/tilem-src/emu/x4/x4_memory.c @@ -0,0 +1,207 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x4.h" + +void x4_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + unsigned long pa; + byte page; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x40; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x41; + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x100000) { + calc->z80.clock += calc->hwregs[FLASH_WRITE_DELAY]; + tilem_flash_write_byte(calc, pa, v); + } + else if (pa < 0x120000) { + calc->z80.clock += calc->hwregs[RAM_WRITE_DELAY]; + *(calc->mem+pa) = v; + } +} + +static inline byte readbyte(TilemCalc* calc, dword pa) +{ + static const byte protectbytes[6] = {0x00,0x00,0xed,0x56,0xf3,0xd3}; + int state = calc->hwregs[PROTECTSTATE]; + byte value; + + if (pa < 0x100000 && (calc->flash.state || calc->flash.busy)) + value = tilem_flash_read_byte(calc, pa); + else + value = *(calc->mem + pa); + + if (pa < 0xB0000 || pa >= 0x100000 + || (pa >= 0xC0000 && pa < 0xF0000)) + calc->hwregs[PROTECTSTATE] = 0; + else if (state == 6) + calc->hwregs[PROTECTSTATE] = 7; + else if (state < 6 && value == protectbytes[state]) + calc->hwregs[PROTECTSTATE] = state + 1; + else + calc->hwregs[PROTECTSTATE] = 0; + + return (value); +} + +byte x4_z80_rdmem(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa; + byte value; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x40; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x41; + } + + if (TILEM_UNLIKELY(page == 0x3E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x100000) + calc->z80.clock += calc->hwregs[FLASH_READ_DELAY]; + else + calc->z80.clock += calc->hwregs[RAM_READ_DELAY]; + + value = readbyte(calc, pa); + return (value); +} + +byte x4_z80_rdmem_m1(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa, m; + byte value; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x40; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x41; + } + + if (TILEM_UNLIKELY(page == 0x3E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x100000) { + calc->z80.clock += calc->hwregs[FLASH_EXEC_DELAY]; + + if (TILEM_UNLIKELY(page >= calc->hwregs[PORT22] + && page <= calc->hwregs[PORT23])) { + tilem_warning(calc, "Executing in restricted Flash area"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + } + else { + calc->z80.clock += calc->hwregs[RAM_EXEC_DELAY]; + + m = pa & calc->hwregs[NO_EXEC_RAM_MASK]; + if (TILEM_UNLIKELY(m < calc->hwregs[NO_EXEC_RAM_LOWER] + || m > calc->hwregs[NO_EXEC_RAM_UPPER])) { + tilem_warning(calc, "Executing in restricted RAM area"); + tilem_z80_exception(calc, TILEM_EXC_RAM_EXEC); + } + } + + value = readbyte(calc, pa); + + if (TILEM_UNLIKELY(value == 0xff && A == 0x0038)) { + tilem_warning(calc, "No OS installed"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + + return (value); +} + +dword x4_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x40; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x41; + } + + return ((page << 14) | (A & 0x3fff)); +} + +dword x4_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + + if (!page) + return (A & 0x3fff); + + if (page == calc->mempagemap[1]) + return (0x4000 | (A & 0x3fff)); + + if ((A & 0x3fff) < 64 * calc->hwregs[PORT28]) { + if (page == 0x41) + return (0x8000 | (A & 0x3fff)); + } + else { + if (page == calc->mempagemap[2]) + return (0x8000 | (A & 0x3fff)); + } + + if ((A & 0x3fff) >= (0x4000 - 64 * calc->hwregs[PORT27])) { + if (page == 0x40) + return (0xC000 | (A & 0x3fff)); + } + else { + if (page == calc->mempagemap[3]) + return (0xC000 | (A & 0x3fff)); + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/x4/x4_subcore.c b/tool/tilem-src/emu/x4/x4_subcore.c new file mode 100644 index 0000000..b393ecf --- /dev/null +++ b/tool/tilem-src/emu/x4/x4_subcore.c @@ -0,0 +1,65 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 + +#include "x4.h" + +static const TilemFlashSector flashsectors[] = { + {0x000000, 0x10000, 0}, {0x010000, 0x10000, 0}, + {0x020000, 0x10000, 0}, {0x030000, 0x10000, 0}, + {0x040000, 0x10000, 0}, {0x050000, 0x10000, 0}, + {0x060000, 0x10000, 0}, {0x070000, 0x10000, 0}, + {0x080000, 0x10000, 0}, {0x090000, 0x10000, 0}, + {0x0A0000, 0x10000, 0}, {0x0B0000, 0x10000, 1}, + {0x0C0000, 0x10000, 0}, {0x0D0000, 0x10000, 0}, + {0x0E0000, 0x10000, 0}, {0x0F0000, 0x08000, 0}, + {0x0F8000, 0x02000, 0}, {0x0FA000, 0x02000, 0}, + {0x0FC000, 0x04000, 1}}; + +#define NUM_FLASH_SECTORS (sizeof(flashsectors) / sizeof(TilemFlashSector)) + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +extern const char* xp_keynames[]; + +TilemHardware hardware_ti84p = { + '4', "ti84p", "TI-84 Plus", + (TILEM_CALC_HAS_LINK | TILEM_CALC_HAS_LINK_ASSIST + | TILEM_CALC_HAS_T6A04 | TILEM_CALC_HAS_FLASH + | TILEM_CALC_HAS_MD5_ASSIST), + 96, 64, 64 * 0x4000, 8 * 0x4000, 16 * 64, 0x80, + NUM_FLASH_SECTORS, flashsectors, 3, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + xp_keynames, + x4_reset, x4_stateloaded, + x4_z80_in, x4_z80_out, + x4_z80_wrmem, x4_z80_rdmem, x4_z80_rdmem_m1, NULL, + x4_z80_ptimer, tilem_lcd_t6a04_get_data, + x4_mem_ltop, x4_mem_ptol }; diff --git a/tool/tilem-src/emu/x5/x5.h b/tool/tilem-src/emu/x5/x5.h new file mode 100644 index 0000000..2af3ed6 --- /dev/null +++ b/tool/tilem-src/emu/x5/x5.h @@ -0,0 +1,49 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#ifndef _TILEM_X5_H +#define _TILEM_X5_H + +enum { + PORT3, /* mask of enabled interrupts */ + PORT4, /* mapping mode, timer control */ + PORT5, /* memory mapping bank A */ + PORT6, /* memory mapping bank B */ + PORT7, /* link port control */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES { "port3", "port4", "port5", "port6", "port7" } + +#define TIMER_INT (TILEM_NUM_SYS_TIMERS + 1) +#define NUM_HW_TIMERS 1 + +#define HW_TIMER_NAMES { "int" } + +void x5_reset(TilemCalc* calc); +byte x5_z80_in(TilemCalc* calc, dword port); +void x5_z80_out(TilemCalc* calc, dword port, byte value); +void x5_z80_ptimer(TilemCalc* calc, int id); +void x5_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte x5_z80_rdmem(TilemCalc* calc, dword addr); +dword x5_mem_ltop(TilemCalc* calc, dword addr); +dword x5_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/x5/x5_init.c b/tool/tilem-src/emu/x5/x5_init.c new file mode 100644 index 0000000..07ca164 --- /dev/null +++ b/tool/tilem-src/emu/x5/x5_init.c @@ -0,0 +1,49 @@ +/* + * 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 + +#include "x5.h" + +void x5_reset(TilemCalc* calc) +{ + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x16; + calc->hwregs[PORT5] = 0x00; + calc->hwregs[PORT6] = 0x40; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x00; + calc->mempagemap[2] = 0x08; + calc->mempagemap[3] = 0x08; + + calc->lcd.addr = 0x3c00; + + tilem_z80_set_speed(calc, 6000); + + /* FIXME: measure actual frequency */ + tilem_z80_set_timer(calc, TIMER_INT, 1000, 5000, 1); +} diff --git a/tool/tilem-src/emu/x5/x5_io.c b/tool/tilem-src/emu/x5/x5_io.c new file mode 100644 index 0000000..cd48f55 --- /dev/null +++ b/tool/tilem-src/emu/x5/x5_io.c @@ -0,0 +1,183 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x5.h" + +byte x5_z80_in(TilemCalc* calc, dword port) +{ + byte v, b; + + switch(port&0xff) { + case 0x01: + return(tilem_keypad_read_keys(calc)); + + case 0x03: + v = (calc->keypad.onkeydown ? 0x00 : 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + + return(v); + + case 0x04: + return(0x01); + + case 0x05: + return(calc->hwregs[PORT5]); + + case 0x06: + return(calc->hwregs[PORT6]); + + case 0x07: + v = tilem_linkport_get_lines(calc); + b = (calc->hwregs[PORT7] >> 4) | 0xf0; + return ((calc->hwregs[PORT7] & b) | (v & ~b)); + } + + tilem_warning(calc, "Input from port %x", port); + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB; + + if (calc->hwregs[PORT5] & 0x40) { + pageA = (0x08 | (calc->hwregs[PORT5] & 1)); + } + else { + pageA = (calc->hwregs[PORT5] & 7); + } + + if (calc->hwregs[PORT6] & 0x40) { + pageB = (0x08 | (calc->hwregs[PORT6] & 1)); + } + else { + pageB = (calc->hwregs[PORT6] & 7); + } + + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = 0x08; +} + +void x5_z80_out(TilemCalc* calc, dword port, byte value) +{ + switch(port&0xff) { + case 0x00: + calc->lcd.addr = ((value & 0x3f) << 8); + calc->z80.lastlcdwrite = calc->z80.clock; + break; + + case 0x01: + tilem_keypad_set_group(calc, value); + break; + + case 0x02: + /* Contrast */ + calc->lcd.contrast = 16 + (value & 0x1f); + break; + + case 0x03: + /* Interrupts / LCD power */ + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) { + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + } + + calc->hwregs[PORT3] = value; + calc->lcd.active = calc->poweronhalt = ((value & 8) >> 3); + break; + + case 0x04: + /* LCD control */ + calc->hwregs[PORT4] = value; + + switch (value & 0x18) { + case 0x00: + calc->lcd.rowstride = 10; + break; + + case 0x08: + calc->lcd.rowstride = 12; + break; + + case 0x10: + calc->lcd.rowstride = 16; + break; + + case 0x18: + calc->lcd.rowstride = 20; + break; + } + calc->z80.lastlcdwrite = calc->z80.clock; + break; + + case 0x05: + calc->hwregs[PORT5] = value; + setup_mapping(calc); + break; + + case 0x06: + calc->hwregs[PORT6] = value; + setup_mapping(calc); + break; + + case 0x07: + /* Link */ + calc->hwregs[PORT7] = value; + + value = (((value >> 6) & (value >> 2)) + | ((value >> 4) & ~value)); + + tilem_linkport_set_lines(calc, value); + break; + } + + return; +} + +void x5_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + } +} diff --git a/tool/tilem-src/emu/x5/x5_memory.c b/tool/tilem-src/emu/x5/x5_memory.c new file mode 100644 index 0000000..050e234 --- /dev/null +++ b/tool/tilem-src/emu/x5/x5_memory.c @@ -0,0 +1,68 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x5.h" + +void x5_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + + if (pa >= 0x20000) { + *(calc->mem + pa) = v; + + if (((pa - 0x20000 - calc->lcd.addr) >> 6) + < (unsigned) calc->lcd.rowstride) + calc->z80.lastlcdwrite = calc->z80.clock; + } +} + +byte x5_z80_rdmem(TilemCalc* calc, dword A) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + return (*(calc->mem + pa)); +} + +dword x5_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + return ((page << 14) | (A & 0x3fff)); +} + +dword x5_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + int i; + + for (i = 0; i < 4; i++) { + if (calc->mempagemap[i] == page) { + return ((i << 14) | (A & 0x3fff)); + } + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/x5/x5_subcore.c b/tool/tilem-src/emu/x5/x5_subcore.c new file mode 100644 index 0000000..d8a5232 --- /dev/null +++ b/tool/tilem-src/emu/x5/x5_subcore.c @@ -0,0 +1,58 @@ +/* + * 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 + +#include "x5.h" + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +static const char* keynames[64] = { + "Down", "Left", "Right", "Up", 0, 0, 0, 0, + "Enter", "Add", "Sub", "Mul", "Div", "Power", "Clear", 0, + "Chs", "3", "6", "9", "RParen", "Tan", "Custom", 0, + "DecPnt", "2", "5", "8", "LParen", "Cos", "Prgm", "Del", + "0", "1", "4", "7", "EE", "Sin", "Stat", "XVar", + "On", "Store", "Comma", "Square", "Ln", "Log", "Graph", "Alpha", + "F5", "F4", "F3", "F2", "F1", "2nd", "Exit", "More", + 0, 0, 0, 0, 0, 0, 0, 0}; + +const TilemHardware hardware_ti85 = { + '5', "ti85", "TI-85", + TILEM_CALC_HAS_LINK, + 128, 64, 8 * 0x4000, 0x8000, 0, 0x40, + 0, NULL, 0, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + keynames, + x5_reset, NULL, + x5_z80_in, x5_z80_out, + x5_z80_wrmem, x5_z80_rdmem, x5_z80_rdmem, NULL, + x5_z80_ptimer, tilem_lcd_t6a43_get_data, + x5_mem_ltop, x5_mem_ptol }; + diff --git a/tool/tilem-src/emu/x6/x6.h b/tool/tilem-src/emu/x6/x6.h new file mode 100644 index 0000000..f55d3a9 --- /dev/null +++ b/tool/tilem-src/emu/x6/x6.h @@ -0,0 +1,49 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#ifndef _TILEM_X6_H +#define _TILEM_X6_H + +enum { + PORT3, /* mask of enabled interrupts */ + PORT4, /* mapping mode, timer control */ + PORT5, /* memory mapping bank A */ + PORT6, /* memory mapping bank B */ + PORT7, /* link port control */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES { "port3", "port4", "port5", "port6", "port7" } + +#define TIMER_INT (TILEM_NUM_SYS_TIMERS + 1) +#define NUM_HW_TIMERS 1 + +#define HW_TIMER_NAMES { "int" } + +void x6_reset(TilemCalc* calc); +byte x6_z80_in(TilemCalc* calc, dword port); +void x6_z80_out(TilemCalc* calc, dword port, byte value); +void x6_z80_ptimer(TilemCalc* calc, int id); +void x6_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte x6_z80_rdmem(TilemCalc* calc, dword addr); +dword x6_mem_ltop(TilemCalc* calc, dword addr); +dword x6_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/x6/x6_init.c b/tool/tilem-src/emu/x6/x6_init.c new file mode 100644 index 0000000..27e6417 --- /dev/null +++ b/tool/tilem-src/emu/x6/x6_init.c @@ -0,0 +1,49 @@ +/* + * 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 + +#include "x6.h" + +void x6_reset(TilemCalc* calc) +{ + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x56; + calc->hwregs[PORT5] = 0x00; + calc->hwregs[PORT6] = 0x00; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x00; + calc->mempagemap[2] = 0x00; + calc->mempagemap[3] = 0x10 + 0x00; + + calc->lcd.addr = 0x3c00; + + tilem_z80_set_speed(calc, 6000); + + /* FIXME: measure actual frequency */ + tilem_z80_set_timer(calc, TIMER_INT, 1000, 5000, 1); +} diff --git a/tool/tilem-src/emu/x6/x6_io.c b/tool/tilem-src/emu/x6/x6_io.c new file mode 100644 index 0000000..92a0d49 --- /dev/null +++ b/tool/tilem-src/emu/x6/x6_io.c @@ -0,0 +1,183 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x6.h" + +byte x6_z80_in(TilemCalc* calc, dword port) +{ + byte v, b; + + switch(port&0xff) { + case 0x01: + return(tilem_keypad_read_keys(calc)); + + case 0x03: + v = (calc->keypad.onkeydown ? 0x00 : 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + + return(v); + + case 0x04: + return(0x01); + + case 0x05: + return(calc->hwregs[PORT5]); + + case 0x06: + return(calc->hwregs[PORT6]); + + case 0x07: + v = tilem_linkport_get_lines(calc); + b = (calc->hwregs[PORT7] >> 4) | 0xf0; + return (calc->hwregs[PORT7] & b) | (v & ~b); + } + + tilem_warning(calc, "Input from port %x", port); + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB; + + if (calc->hwregs[PORT5] & 0x40) { + pageA = (0x10 | (calc->hwregs[PORT5] & 7)); + } + else { + pageA = (calc->hwregs[PORT5] & 0x0f); + } + + if (calc->hwregs[PORT6] & 0x40) { + pageB = (0x10 | (calc->hwregs[PORT6] & 7)); + } + else { + pageB = (calc->hwregs[PORT6] & 0x0f); + } + + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = 0x10; +} + +void x6_z80_out(TilemCalc* calc, dword port, byte value) +{ + switch(port&0xff) { + case 0x00: + calc->lcd.addr = ((value & 0x3f) << 8); + calc->z80.lastlcdwrite = calc->z80.clock; + break; + + case 0x01: + tilem_keypad_set_group(calc, value); + break; + + case 0x02: + /* contrast */ + calc->lcd.contrast = 16 + (value & 0x1f); + break; + + case 0x03: + /* Lcd Power */ + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) { + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + } + + calc->hwregs[PORT3] = value; + calc->lcd.active = calc->poweronhalt = ((value & 8) >> 3); + break; + + case 0x04: + /* LCD control */ + calc->hwregs[PORT4] = value; + + switch (value & 0x18) { + case 0x00: + calc->lcd.rowstride = 10; + break; + + case 0x08: + calc->lcd.rowstride = 12; + break; + + case 0x10: + calc->lcd.rowstride = 16; + break; + + case 0x18: + calc->lcd.rowstride = 20; + break; + } + calc->z80.lastlcdwrite = calc->z80.clock; + break; + + case 0x05: + calc->hwregs[PORT5] = value; + setup_mapping(calc); + break; + + case 0x06: + calc->hwregs[PORT6] = value; + setup_mapping(calc); + break; + + case 0x07: + /* Link */ + calc->hwregs[PORT7] = value; + + value = (((value >> 6) & (value >> 2)) + | ((value >> 4) & ~value)); + + tilem_linkport_set_lines(calc, value); + break; + } + + return; +} + +void x6_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + } +} diff --git a/tool/tilem-src/emu/x6/x6_memory.c b/tool/tilem-src/emu/x6/x6_memory.c new file mode 100644 index 0000000..f771a2f --- /dev/null +++ b/tool/tilem-src/emu/x6/x6_memory.c @@ -0,0 +1,68 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x6.h" + +void x6_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + + if (pa >= 0x40000) { + *(calc->mem + pa) = v; + + if (((pa - 0x40000 - calc->lcd.addr) >> 6) + < (unsigned) calc->lcd.rowstride) + calc->z80.lastlcdwrite = calc->z80.clock; + } +} + +byte x6_z80_rdmem(TilemCalc* calc, dword A) +{ + dword pa = 0x4000 * calc->mempagemap[(A)>>14] + (A & 0x3FFF); + return (*(calc->mem + pa)); +} + +dword x6_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + return ((page << 14) | (A & 0x3fff)); +} + +dword x6_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + int i; + + for (i = 0; i < 4; i++) { + if (calc->mempagemap[i] == page) { + return ((i << 14) | (A & 0x3fff)); + } + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/x6/x6_subcore.c b/tool/tilem-src/emu/x6/x6_subcore.c new file mode 100644 index 0000000..2a69455 --- /dev/null +++ b/tool/tilem-src/emu/x6/x6_subcore.c @@ -0,0 +1,57 @@ +/* + * 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 + +#include "x6.h" + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +static const char* keynames[64] = { + "Down", "Left", "Right", "Up", 0, 0, 0, 0, + "Enter", "Add", "Sub", "Mul", "Div", "Power", "Clear", 0, + "Chs", "3", "6", "9", "RParen", "Tan", "Custom", 0, + "DecPnt", "2", "5", "8", "LParen", "Cos", "Prgm", "Del", + "0", "1", "4", "7", "EE", "Sin", "Table", "XVar", + "On", "Store", "Comma", "Square", "Ln", "Log", "Graph", "Alpha", + "F5", "F4", "F3", "F2", "F1", "2nd", "Exit", "More", + 0, 0, 0, 0, 0, 0, 0, 0}; + +const TilemHardware hardware_ti86 = { + '6', "ti86", "TI-86", + TILEM_CALC_HAS_LINK, + 128, 64, 0x10 * 0x4000, 0x08 * 0x4000, 0, 0x40, + 0, NULL, 0, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + keynames, + x6_reset, NULL, + x6_z80_in, x6_z80_out, + x6_z80_wrmem, x6_z80_rdmem, x6_z80_rdmem, NULL, + x6_z80_ptimer, tilem_lcd_t6a43_get_data, + x6_mem_ltop, x6_mem_ptol }; diff --git a/tool/tilem-src/emu/x7/x7.h b/tool/tilem-src/emu/x7/x7.h new file mode 100644 index 0000000..53543ba --- /dev/null +++ b/tool/tilem-src/emu/x7/x7.h @@ -0,0 +1,56 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#ifndef _TILEM_X7_H +#define _TILEM_X7_H + +enum { + PORT3, /* mask of enabled interrupts */ + PORT4, /* interrupt timer speed */ + PORT6, /* memory mapping bank A */ + PORT7, /* memory mapping bank B */ + NOEXEC, /* no-exec sector map */ + PROTECTSTATE, /* port protection state */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES \ + { "port3", "port4", "port6", "port7", "noexec", \ + "protectstate" } + +#define TIMER_INT1 (TILEM_NUM_SYS_TIMERS + 1) +#define TIMER_INT2A (TILEM_NUM_SYS_TIMERS + 2) +#define TIMER_INT2B (TILEM_NUM_SYS_TIMERS + 3) +#define NUM_HW_TIMERS 3 + +#define HW_TIMER_NAMES { "int1", "int2a", "int2b" } + +void x7_reset(TilemCalc* calc); +void x7_stateloaded(TilemCalc* calc, int savtype); +byte x7_z80_in(TilemCalc* calc, dword port); +void x7_z80_out(TilemCalc* calc, dword port, byte value); +void x7_z80_ptimer(TilemCalc* calc, int id); +void x7_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte x7_z80_rdmem(TilemCalc* calc, dword addr); +byte x7_z80_rdmem_m1(TilemCalc* calc, dword addr); +dword x7_mem_ltop(TilemCalc* calc, dword addr); +dword x7_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/x7/x7_init.c b/tool/tilem-src/emu/x7/x7_init.c new file mode 100644 index 0000000..1d2a62e --- /dev/null +++ b/tool/tilem-src/emu/x7/x7_init.c @@ -0,0 +1,59 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x7.h" + +void x7_reset(TilemCalc* calc) +{ + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x77; + calc->hwregs[PORT6] = 0x1F; + calc->hwregs[PORT7] = 0x1F; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x1E; + calc->mempagemap[2] = 0x1F; + calc->mempagemap[3] = 0x1F; + + calc->z80.r.pc.d = 0x8000; + + calc->hwregs[NOEXEC] = 0; + calc->hwregs[PROTECTSTATE] = 0; + + tilem_z80_set_speed(calc, 6000); + + tilem_z80_set_timer(calc, TIMER_INT1, 1600, 8474, 1); + tilem_z80_set_timer(calc, TIMER_INT2A, 1300, 8474, 1); + tilem_z80_set_timer(calc, TIMER_INT2B, 1000, 8474, 1); +} + +void x7_stateloaded(TilemCalc* calc, int savtype TILEM_ATTR_UNUSED) +{ + tilem_calc_fix_certificate(calc, calc->mem + (0x1E * 0x4000L), + 0x08, 0x13, 0x1f18); +} diff --git a/tool/tilem-src/emu/x7/x7_io.c b/tool/tilem-src/emu/x7/x7_io.c new file mode 100644 index 0000000..aaca352 --- /dev/null +++ b/tool/tilem-src/emu/x7/x7_io.c @@ -0,0 +1,247 @@ +/* + * 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 + +#include "x7.h" + +byte x7_z80_in(TilemCalc* calc, dword port) +{ + /* FIXME: measure actual levels */ + static const byte battlevel[4] = { 33, 39, 36, 43 }; + byte v; + + /* FIXME: confirm that mirror ports are the same as on 83+ + - and figure out what, if anything, port 5 does */ + + switch(port&0x1f) { + case 0x00: + case 0x08: + v = tilem_linkport_get_lines(calc); + v |= (calc->linkport.lines << 4); + return(v); + + case 0x01: + case 0x09: + return(tilem_keypad_read_keys(calc)); + + case 0x02: + case 0x0A: + v = battlevel[calc->hwregs[PORT4] >> 6]; + return(calc->battery >= v ? 0x05 : 0x04); + + case 0x03: + case 0x0B: + return(calc->hwregs[PORT3]); + + case 0x04: + case 0x0C: + v = (calc->keypad.onkeydown ? 0x00 : 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER2) + v |= 0x04; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ACTIVE) + v |= 0x10; + + return(v); + + case 0x06: + case 0x0E: + return(calc->hwregs[PORT6]); + + case 0x07: + case 0x0F: + return(calc->hwregs[PORT7]); + + case 0x10: + case 0x12: + case 0x18: + case 0x1A: + return(tilem_lcd_t6a04_status(calc)); + + case 0x11: + case 0x13: + case 0x19: + case 0x1B: + return(tilem_lcd_t6a04_read(calc)); + } + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB; + + if (calc->hwregs[PORT6] & 0x40) + pageA = (0x20 | (calc->hwregs[PORT6] & 1)); + else + pageA = (calc->hwregs[PORT6] & 0x1f); + + if (calc->hwregs[PORT7] & 0x40) + pageB = (0x20 | (calc->hwregs[PORT7] & 1)); + else + pageB = (calc->hwregs[PORT7] & 0x1f); + + if (calc->hwregs[PORT4] & 1) { + /* FIXME: confirm this behavior (83+-like rather than + 83-like) */ + calc->mempagemap[1] = (pageA & ~1); + calc->mempagemap[2] = pageA; + calc->mempagemap[3] = pageB; + } + else { + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = 0x20; + } +} + +void x7_z80_out(TilemCalc* calc, dword port, byte value) +{ + static const int tmrvalues[4] = { 1786, 4032, 5882, 8474 }; + int t; + unsigned int mode; + + switch(port&0x1f) { + case 0x00: + case 0x08: + tilem_linkport_set_lines(calc, value); + break; + + case 0x01: + case 0x09: + tilem_keypad_set_group(calc, value); + break; + + case 0x03: + case 0x0B: + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + + if (!(value & 0x04)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER2; + + mode = calc->linkport.mode; + if (value & 0x10) + mode |= TILEM_LINK_MODE_INT_ON_ACTIVE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ACTIVE; + + tilem_linkport_set_mode(calc, mode); + + calc->poweronhalt = ((value & 8) >> 3); + calc->hwregs[PORT3] = value; + break; + + case 0x04: + case 0x0C: + calc->hwregs[PORT4] = value; + + t = tmrvalues[(value & 6) >> 1]; + tilem_z80_set_timer_period(calc, TIMER_INT1, t); + tilem_z80_set_timer_period(calc, TIMER_INT2A, t); + tilem_z80_set_timer_period(calc, TIMER_INT2B, t); + + setup_mapping(calc); + break; + + case 0x06: + case 0x0E: + calc->hwregs[PORT6] = value & 0x7f; + setup_mapping(calc); + break; + + case 0x07: + case 0x0F: + calc->hwregs[PORT7] = value & 0x7f; + setup_mapping(calc); + break; + + case 0x10: + case 0x12: + case 0x18: + case 0x1A: + tilem_lcd_t6a04_control(calc, value); + break; + + case 0x11: + case 0x13: + case 0x19: + case 0x1B: + tilem_lcd_t6a04_write(calc, value); + break; + + case 0x14: + case 0x15: + if (calc->hwregs[PROTECTSTATE] == 7) { + if (value & 1) + tilem_message(calc, "Flash unlocked"); + else + tilem_message(calc, "Flash locked"); + calc->flash.unlock = value&1; + } + break; + + case 0x16: + case 0x17: + if (calc->flash.unlock && calc->hwregs[PROTECTSTATE] == 7) { + tilem_message(calc, "No-exec mask set to %x", value); + calc->hwregs[NOEXEC] = ((value & 0x0f) << 2); + } + break; + } + + return; +} + +void x7_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT1: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + + case TIMER_INT2A: + case TIMER_INT2B: + if (calc->hwregs[PORT3] & 0x04) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER2; + break; + } +} diff --git a/tool/tilem-src/emu/x7/x7_memory.c b/tool/tilem-src/emu/x7/x7_memory.c new file mode 100644 index 0000000..d39dc0a --- /dev/null +++ b/tool/tilem-src/emu/x7/x7_memory.c @@ -0,0 +1,132 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "x7.h" + +void x7_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + unsigned long pa; + + pa = (A & 0x3FFF) + 0x4000*calc->mempagemap[(A)>>14]; + + if (pa<0x80000) + tilem_flash_write_byte(calc, pa, v); + + else if (pa < 0x88000) + *(calc->mem+pa) = v; +} + +static inline byte readbyte(TilemCalc* calc, dword pa) +{ + static const byte protectbytes[6] = {0x00,0x00,0xed,0x56,0xf3,0xd3}; + int state = calc->hwregs[PROTECTSTATE]; + byte value; + + if (pa < 0x80000 && (calc->flash.state || calc->flash.busy)) + value = tilem_flash_read_byte(calc, pa); + else + value = *(calc->mem + pa); + + if (pa < 0x70000 || pa >= 0x80000) + calc->hwregs[PROTECTSTATE] = 0; + else if (state == 6) + calc->hwregs[PROTECTSTATE] = 7; + else if (state < 6 && value == protectbytes[state]) + calc->hwregs[PROTECTSTATE] = state + 1; + else + calc->hwregs[PROTECTSTATE] = 0; + + return (value); +} + +byte x7_z80_rdmem(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa; + byte value; + + page = calc->mempagemap[A>>14]; + pa = 0x4000 * page + (A & 0x3FFF); + + if (TILEM_UNLIKELY(page == 0x1E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + value = readbyte(calc, pa); + return(value); +} + +byte x7_z80_rdmem_m1(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa; + byte value; + + page = calc->mempagemap[A>>14]; + pa = 0x4000 * page + (A & 0x3FFF); + + if (TILEM_UNLIKELY(calc->hwregs[NOEXEC] & (1 << (page % 4)))) { + tilem_warning(calc, "Executing in restricted Flash area"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + + if (TILEM_UNLIKELY(page == 0x1E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + tilem_z80_exception(calc, TILEM_EXC_RAM_EXEC); + } + + value = readbyte(calc, pa); + + if (TILEM_UNLIKELY(value == 0xff && A == 0x0038)) { + tilem_warning(calc, "No OS installed"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + + return (value); +} + +dword x7_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + return ((page << 14) | (A & 0x3fff)); +} + +dword x7_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + int i; + + for (i = 0; i < 4; i++) { + if (calc->mempagemap[i] == page) { + return ((i << 14) | (A & 0x3fff)); + } + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/x7/x7_subcore.c b/tool/tilem-src/emu/x7/x7_subcore.c new file mode 100644 index 0000000..69e480a --- /dev/null +++ b/tool/tilem-src/emu/x7/x7_subcore.c @@ -0,0 +1,72 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 + +#include "x7.h" + +static const TilemFlashSector flashsectors[] = { + {0x000000, 0x10000, 0}, + {0x010000, 0x10000, 0}, + {0x020000, 0x10000, 0}, + {0x030000, 0x10000, 0}, + {0x040000, 0x10000, 0}, + {0x050000, 0x10000, 0}, + {0x060000, 0x10000, 0}, + {0x070000, 0x08000, 0}, + {0x078000, 0x02000, 0}, + {0x07A000, 0x02000, 0}, + {0x07C000, 0x04000, 1}}; + +#define NUM_FLASH_SECTORS (sizeof(flashsectors) / sizeof(TilemFlashSector)) + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +static const char* keynames[64] = { + "Down", "Left", "Right", "Up", 0, 0, 0, 0, + "Enter", "Add", "Sub", "Mul", "Div", "Const", "Clear", 0, + "Chs", "3", "6", "9", "RParen", "MixSimp", "AppsMenu", 0, + "DecPnt", "2", "5", "8", "LParen", "FracDec", "Prgm", "StatEd", + "0", "1", "4", "7", "Percent", "FracSlash", "Expon", "Draw", + "On", "Store", "Comma", "VarX", "Simp", "Unit", "Square", "Math", + "Graph", "Trace", "Zoom", "Window", "YEqu", "2nd", "Mode", "Del", + 0, 0, 0, 0, 0, 0, 0, 0}; + +TilemHardware hardware_ti73 = { + '7', "ti73", "TI-73", + (TILEM_CALC_HAS_LINK | TILEM_CALC_HAS_T6A04 | TILEM_CALC_HAS_FLASH), + 96, 64, 32 * 0x4000, 2 * 0x4000, 15 * 64, 0x40, + NUM_FLASH_SECTORS, flashsectors, 0, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + keynames, + x7_reset, x7_stateloaded, + x7_z80_in, x7_z80_out, + x7_z80_wrmem, x7_z80_rdmem, x7_z80_rdmem_m1, NULL, + x7_z80_ptimer, tilem_lcd_t6a04_get_data, + x7_mem_ltop, x7_mem_ptol }; diff --git a/tool/tilem-src/emu/xn/xn.h b/tool/tilem-src/emu/xn/xn.h new file mode 100644 index 0000000..b5e4841 --- /dev/null +++ b/tool/tilem-src/emu/xn/xn.h @@ -0,0 +1,105 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2009-2011 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 + * . + */ + +#ifndef _TILEM_XN_H +#define _TILEM_XN_H + +enum { + PORT3, /* mask of enabled interrupts */ + PORT4, /* interrupt timer speed */ + PORT5, /* memory mapping bank C */ + PORT6, /* memory mapping bank A */ + PORT7, /* memory mapping bank B */ + PORT8, /* link assist mode flags */ + PORT9, /* unknown (link assist settings?) */ + PORTA, /* unknown (timeout value?) */ + PORTB, /* unknown (timeout value?) */ + PORTC, /* unknown (timeout value?) */ + PORTD, /* unknown */ + PORTE, /* unknown */ + PORTF, /* unknown */ + + PORT20, /* CPU speed control */ + PORT21, /* hardware type / RAM no-exec control */ + PORT22, /* Flash no-exec lower limit */ + PORT23, /* Flash no-exec upper limit */ + PORT25, /* unknown */ + PORT26, /* unknown */ + PORT27, /* bank C forced-page-0 limit */ + PORT28, /* bank B forced-page-1 limit */ + PORT29, /* LCD port delay (6 MHz) */ + PORT2A, /* LCD port delay (mode 1) */ + PORT2B, /* LCD port delay (mode 2) */ + PORT2C, /* LCD port delay (mode 3) */ + PORT2D, /* unknown */ + PORT2E, /* memory delay */ + PORT2F, /* Duration of LCD wait timer */ + + CLOCK_MODE, /* clock mode */ + CLOCK_INPUT, /* clock input register */ + CLOCK_DIFF, /* clock value minus actual time */ + + RAM_READ_DELAY, + RAM_WRITE_DELAY, + RAM_EXEC_DELAY, + FLASH_READ_DELAY, + FLASH_WRITE_DELAY, + FLASH_EXEC_DELAY, + LCD_PORT_DELAY, + NO_EXEC_RAM, + + LCD_WAIT, /* LCD wait timer active */ + PROTECTSTATE, /* port protection state */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES \ + { "port3", "port4", "port5", "port6", "port7", "port8", "port9", \ + "portA", "portB", "portC", "portD", "portE", "portF", "port20", \ + "port21", "port22", "port23", "port25", "port26", "port27", \ + "port28", "port29", "port2A", "port2B", "port2C", "port2D", \ + "port2E", "port2F", "clock_mode", "clock_input", "clock_diff", \ + "ram_read_delay", "ram_write_delay", "ram_exec_delay", \ + "flash_read_delay", "flash_write_delay", "flash_exec_delay", \ + "lcd_port_delay", "no_exec_ram", "lcd_wait", "protectstate" } + +#define TIMER_INT1 (TILEM_NUM_SYS_TIMERS + 1) +#define TIMER_INT2A (TILEM_NUM_SYS_TIMERS + 2) +#define TIMER_INT2B (TILEM_NUM_SYS_TIMERS + 3) +#define TIMER_LCD_WAIT (TILEM_NUM_SYS_TIMERS + 4) +#define TIMER_FREEZE_LINK_PORT (TILEM_NUM_SYS_TIMERS + 5) +#define NUM_HW_TIMERS 5 + +#define HW_TIMER_NAMES { "int1", "int2a", "int2b", "lcd_wait", \ + "freeze_link_port" } + +void xn_reset(TilemCalc* calc); +void xn_stateloaded(TilemCalc* calc, int savtype); +byte xn_z80_in(TilemCalc* calc, dword port); +void xn_z80_out(TilemCalc* calc, dword port, byte value); +void xn_z80_instr(TilemCalc* calc, dword opcode); +void xn_z80_ptimer(TilemCalc* calc, int id); +void xn_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte xn_z80_rdmem(TilemCalc* calc, dword addr); +byte xn_z80_rdmem_m1(TilemCalc* calc, dword addr); +dword xn_mem_ltop(TilemCalc* calc, dword addr); +dword xn_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/xn/xn_init.c b/tool/tilem-src/emu/xn/xn_init.c new file mode 100644 index 0000000..84e715e --- /dev/null +++ b/tool/tilem-src/emu/xn/xn_init.c @@ -0,0 +1,88 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "xn.h" + +void xn_reset(TilemCalc* calc) +{ + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x07; + calc->hwregs[PORT6] = 0x7F; + calc->hwregs[PORT7] = 0x7F; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x7E; + calc->mempagemap[2] = 0x7F; + calc->mempagemap[3] = 0x7F; + + calc->z80.r.pc.d = 0x8000; + + calc->hwregs[PORT8] = 0x80; + + calc->hwregs[PORT20] = 0; + calc->hwregs[PORT21] = 0; + calc->hwregs[PORT22] = 0x08; + calc->hwregs[PORT23] = 0x69; + calc->hwregs[PORT25] = 0x10; + calc->hwregs[PORT26] = 0x20; + calc->hwregs[PORT27] = 0; + calc->hwregs[PORT28] = 0; + calc->hwregs[PORT29] = 0x14; + calc->hwregs[PORT2A] = 0x27; + calc->hwregs[PORT2B] = 0x2F; + calc->hwregs[PORT2C] = 0x3B; + calc->hwregs[PORT2D] = 0x01; + calc->hwregs[PORT2E] = 0x44; + calc->hwregs[PORT2F] = 0x4A; + + calc->hwregs[FLASH_READ_DELAY] = 0; + calc->hwregs[FLASH_WRITE_DELAY] = 0; + calc->hwregs[FLASH_EXEC_DELAY] = 0; + calc->hwregs[RAM_READ_DELAY] = 0; + calc->hwregs[RAM_WRITE_DELAY] = 0; + calc->hwregs[RAM_EXEC_DELAY] = 0; + calc->hwregs[LCD_PORT_DELAY] = 5; + calc->hwregs[NO_EXEC_RAM] = 0x5555; + + calc->hwregs[PROTECTSTATE] = 0; + + tilem_z80_set_speed(calc, 6000); + calc->z80.emuflags |= TILEM_Z80_RESET_UNDOCUMENTED; + + calc->lcd.emuflags &= ~TILEM_LCD_REQUIRE_DELAY; + + tilem_z80_set_timer(calc, TIMER_INT1, 1600, 9277, 1); + tilem_z80_set_timer(calc, TIMER_INT2A, 1300, 9277, 1); + tilem_z80_set_timer(calc, TIMER_INT2B, 1000, 9277, 1); +} + +void xn_stateloaded(TilemCalc* calc, int savtype TILEM_ATTR_UNUSED) +{ + tilem_calc_fix_certificate(calc, calc->mem + (0x7E * 0x4000L), + 0x69, 0x0c, 0x1e50); +} diff --git a/tool/tilem-src/emu/xn/xn_io.c b/tool/tilem-src/emu/xn/xn_io.c new file mode 100644 index 0000000..7b4c85e --- /dev/null +++ b/tool/tilem-src/emu/xn/xn_io.c @@ -0,0 +1,881 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 +#include + +#include "xn.h" + +static void set_lcd_wait_timer(TilemCalc* calc) +{ + static const int delaytime[8] = { 48, 112, 176, 240, + 304, 368, 432, 496 }; + int i; + + switch (calc->hwregs[PORT20] & 3) { + case 0: + return; + case 1: + i = (calc->hwregs[PORT2F] & 3); + break; + case 2: + i = ((calc->hwregs[PORT2F] >> 2) & 7); + break; + default: + i = ((calc->hwregs[PORT2F] >> 5) & 7); + break; + } + + tilem_z80_set_timer(calc, TIMER_LCD_WAIT, delaytime[i], 0, 0); + calc->hwregs[LCD_WAIT] = 1; +} + +byte xn_z80_in(TilemCalc* calc, dword port) +{ + /* FIXME: measure actual levels */ + static const byte battlevel[4] = { 33, 39, 36, 43 }; + byte v; + unsigned int f; + time_t curtime; + + switch(port&0xff) { + case 0x00: + if (tilem_z80_timer_running(calc, TIMER_FREEZE_LINK_PORT)) + return(3); + + v = tilem_linkport_get_lines(calc); + v |= (calc->linkport.lines << 4); + return(v); + + case 0x01: + return(tilem_keypad_read_keys(calc)); + + case 0x02: + v = battlevel[calc->hwregs[PORT4] >> 6]; + return ((calc->battery >= v ? 0xe1 : 0xe0) + | (calc->hwregs[LCD_WAIT] ? 0 : 2) + | (calc->flash.unlock << 2)); + + case 0x03: + return(calc->hwregs[PORT3]); + + case 0x04: + v = (calc->keypad.onkeydown ? 0x00 : 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER2) + v |= 0x04; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ACTIVE) + v |= 0x10; + + if (calc->usertimers[0].status & TILEM_USER_TIMER_FINISHED) + v |= 0x20; + if (calc->usertimers[1].status & TILEM_USER_TIMER_FINISHED) + v |= 0x40; + if (calc->usertimers[2].status & TILEM_USER_TIMER_FINISHED) + v |= 0x80; + + return(v); + + case 0x05: + return(calc->hwregs[PORT5] & 0x0f); + + case 0x06: + return(calc->hwregs[PORT6]); + + case 0x07: + return(calc->hwregs[PORT7]); + + case 0x08: + return(calc->hwregs[PORT8]); + + case 0x09: + f = tilem_linkport_get_assist_flags(calc); + + if (f & (TILEM_LINK_ASSIST_READ_BUSY + | TILEM_LINK_ASSIST_WRITE_BUSY)) + v = 0x00; + else + v = 0x20; + + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_READ) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_IDLE) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ERROR) + v |= 0x04; + if (f & TILEM_LINK_ASSIST_READ_BUSY) + v |= 0x08; + if (f & TILEM_LINK_ASSIST_READ_BYTE) + v |= 0x10; + if (f & (TILEM_LINK_ASSIST_READ_ERROR + | TILEM_LINK_ASSIST_WRITE_ERROR)) + v |= 0x40; + if (f & TILEM_LINK_ASSIST_WRITE_BUSY) + v |= 0x80; + + calc->z80.interrupts &= ~TILEM_INTERRUPT_LINK_ERROR; + + return(v); + + case 0x0A: + v = calc->linkport.assistlastbyte; + tilem_linkport_read_byte(calc); + return(v); + + case 0x0E: + return(calc->hwregs[PORTE] & 3); + + case 0x0F: + return(calc->hwregs[PORTF] & 3); + + case 0x10: + case 0x12: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + return(tilem_lcd_t6a04_status(calc)); + + case 0x11: + case 0x13: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + return(tilem_lcd_t6a04_read(calc)); + + case 0x15: + return(0x45); /* ??? */ + + case 0x1C: + return(tilem_md5_assist_get_value(calc)); + + case 0x1D: + return(tilem_md5_assist_get_value(calc) >> 8); + + case 0x1E: + return(tilem_md5_assist_get_value(calc) >> 16); + + case 0x1F: + return(tilem_md5_assist_get_value(calc) >> 24); + + case 0x20: + return(calc->hwregs[PORT20] & 3); + + case 0x21: + return(calc->hwregs[PORT21] & 0x33); + + case 0x22: + return(calc->hwregs[PORT22]); + + case 0x23: + return(calc->hwregs[PORT23]); + + case 0x25: + return(calc->hwregs[PORT25]); + + case 0x26: + return(calc->hwregs[PORT26]); + + case 0x27: + return(calc->hwregs[PORT27]); + + case 0x28: + return(calc->hwregs[PORT28]); + + case 0x29: + return(calc->hwregs[PORT29]); + + case 0x2A: + return(calc->hwregs[PORT2A]); + + case 0x2B: + return(calc->hwregs[PORT2B]); + + case 0x2C: + return(calc->hwregs[PORT2C]); + + case 0x2D: + return(calc->hwregs[PORT2D] & 3); + + case 0x2E: + return(calc->hwregs[PORT2E]); + + case 0x2F: + return(calc->hwregs[PORT2F]); + + case 0x30: + return(calc->usertimers[0].frequency); + case 0x31: + return(calc->usertimers[0].status); + case 0x32: + return(tilem_user_timer_get_value(calc, 0)); + + case 0x33: + return(calc->usertimers[1].frequency); + case 0x34: + return(calc->usertimers[1].status); + case 0x35: + return(tilem_user_timer_get_value(calc, 1)); + + case 0x36: + return(calc->usertimers[2].frequency); + case 0x37: + return(calc->usertimers[2].status); + case 0x38: + return(tilem_user_timer_get_value(calc, 2)); + + case 0x39: + return(0xf0); /* ??? */ + + case 0x40: + return calc->hwregs[CLOCK_MODE]; + + case 0x41: + return calc->hwregs[CLOCK_INPUT]&0xff; + + case 0x42: + return (calc->hwregs[CLOCK_INPUT]>>8)&0xff; + + case 0x43: + return (calc->hwregs[CLOCK_INPUT]>>16)&0xff; + + case 0x44: + return (calc->hwregs[CLOCK_INPUT]>>24)&0xff; + + case 0x45: + case 0x46: + case 0x47: + case 0x48: + if (calc->hwregs[CLOCK_MODE] & 1) { + time(&curtime); + } + else { + curtime = 0; + } + curtime += calc->hwregs[CLOCK_DIFF]; + return (curtime >> ((port - 0x45) * 8)); + + case 0x4C: + return(0x22); + + case 0x4D: + /* USB port - not emulated, calculator should + recognize that the USB cable is + disconnected. + + Thanks go to Dan Englender for these + values. */ + + return(0xA5); + + case 0x55: + return(0x1F); + + case 0x56: + return(0x00); + + case 0x57: + return(0x50); + + case 0x0B: + case 0x0C: + case 0x0D: + case 0x14: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + return(0); + } + + tilem_warning(calc, "Input from port %x", port); + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB, pageC; + + if (calc->hwregs[PORT6] & 0x80) + pageA = (0x80 | (calc->hwregs[PORT6] & 7)); + else + pageA = (calc->hwregs[PORT6] & 0x7f); + + if (calc->hwregs[PORT7] & 0x80) + pageB = (0x80 | (calc->hwregs[PORT7] & 7)); + else + pageB = (calc->hwregs[PORT7] & 0x7f); + + pageC = (0x80 | (calc->hwregs[PORT5] & 7)); + + if (calc->hwregs[PORT4] & 1) { + calc->mempagemap[1] = (pageA & ~1); + calc->mempagemap[2] = (pageA | 1); + calc->mempagemap[3] = pageB; + } + else { + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = pageC; + } +} + +static void setup_clockdelays(TilemCalc* calc) +{ + byte lcdport = calc->hwregs[PORT29 + (calc->hwregs[PORT20] & 3)]; + byte memport = calc->hwregs[PORT2E]; + + if (!(lcdport & 1)) + memport &= ~0x07; + if (!(lcdport & 2)) + memport &= ~0x70; + + calc->hwregs[FLASH_EXEC_DELAY] = (memport & 1); + calc->hwregs[FLASH_READ_DELAY] = ((memport >> 1) & 1); + calc->hwregs[FLASH_WRITE_DELAY] = ((memport >> 2) & 1); + + calc->hwregs[RAM_EXEC_DELAY] = ((memport >> 4) & 1); + calc->hwregs[RAM_READ_DELAY] = ((memport >> 5) & 1); + calc->hwregs[RAM_WRITE_DELAY] = ((memport >> 6) & 1); + + calc->hwregs[LCD_PORT_DELAY] = (lcdport >> 2); +} + +void xn_z80_out(TilemCalc* calc, dword port, byte value) +{ + static const int tmrvalues[4] = { 1953, 4395, 6836, 9277 }; + int t, r; + unsigned int mode; + time_t curtime; + + switch(port&0xff) { + case 0x00: + if (value == 0 + && calc->linkport.lines != 0 + && calc->linkport.extlines == 0) { + /* Kludge to work around TI's broken + RecAByteIO implementation on 2.46+, which + will fail if the sending device is too + fast. */ + tilem_z80_set_timer(calc, TIMER_FREEZE_LINK_PORT, + 100, 0, 0); + } + + tilem_linkport_set_lines(calc, value); + break; + + case 0x01: + tilem_keypad_set_group(calc, value); + break; + + case 0x03: + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + + if (!(value & 0x04)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER2; + + if (value & 0x06) { + calc->usertimers[0].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[1].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[2].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + } + else { + calc->usertimers[0].status |= TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[1].status |= TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[2].status |= TILEM_USER_TIMER_NO_HALT_INT; + } + + mode = calc->linkport.mode; + if (value & 0x10) + mode |= TILEM_LINK_MODE_INT_ON_ACTIVE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ACTIVE; + + tilem_linkport_set_mode(calc, mode); + + calc->poweronhalt = ((value & 8) >> 3); + calc->hwregs[PORT3] = value; + break; + + + case 0x04: + calc->hwregs[PORT4] = value; + + t = tmrvalues[(value & 6) >> 1]; + tilem_z80_set_timer_period(calc, TIMER_INT1, t); + tilem_z80_set_timer_period(calc, TIMER_INT2A, t); + tilem_z80_set_timer_period(calc, TIMER_INT2B, t); + + setup_mapping(calc); + break; + + case 0x05: + calc->hwregs[PORT5] = value & 0x0f; + setup_mapping(calc); + break; + + case 0x06: + calc->hwregs[PORT6] = value; + setup_mapping(calc); + break; + + case 0x07: + calc->hwregs[PORT7] = value; + setup_mapping(calc); + break; + + case 0x08: + calc->hwregs[PORT8] = value; + + mode = calc->linkport.mode; + + if (value & 0x01) + mode |= TILEM_LINK_MODE_INT_ON_READ; + else + mode &= ~TILEM_LINK_MODE_INT_ON_READ; + + if (value & 0x02) + mode |= TILEM_LINK_MODE_INT_ON_IDLE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_IDLE; + + if (value & 0x04) + mode |= TILEM_LINK_MODE_INT_ON_ERROR; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ERROR; + + if (value & 0x80) + mode &= ~TILEM_LINK_MODE_ASSIST; + else + mode |= TILEM_LINK_MODE_ASSIST; + + tilem_linkport_set_mode(calc, mode); + break; + + case 0x09: + calc->hwregs[PORT9] = value; + break; + + case 0x0A: + calc->hwregs[PORTA] = value; + break; + + case 0x0B: + calc->hwregs[PORTB] = value; + break; + + case 0x0C: + calc->hwregs[PORTC] = value; + break; + + + case 0x0D: + if (!(calc->hwregs[PORT8] & 0x80)) + tilem_linkport_write_byte(calc, value); + break; + + case 0x0E: + calc->hwregs[PORTE] = value; + break; + + case 0x0F: + calc->hwregs[PORTF] = value; + break; + + case 0x10: + case 0x12: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + tilem_lcd_t6a04_control(calc, value); + break; + + case 0x11: + case 0x13: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + tilem_lcd_t6a04_write(calc, value); + break; + + case 0x14: + if (calc->hwregs[PROTECTSTATE] == 7) { + /* + if (value & 1) + tilem_message(calc, "Flash unlocked"); + else + tilem_message(calc, "Flash locked"); + */ + calc->flash.unlock = value&1; + } + break; + + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + r = (port & 0xff) - 0x18; + calc->md5assist.regs[r] >>= 8; + calc->md5assist.regs[r] |= (value << 24); + break; + + case 0x1E: + calc->md5assist.shift = value & 0x1f; + break; + + case 0x1F: + calc->md5assist.mode = value & 3; + break; + + case 0x20: + calc->hwregs[PORT20] = value; + + if (value & 3) { + tilem_z80_set_speed(calc, 15000); + } + else { + tilem_z80_set_speed(calc, 6000); + } + + setup_clockdelays(calc); + break; + + case 0x21: + if (calc->flash.unlock && calc->hwregs[PROTECTSTATE] == 7) { + calc->hwregs[PORT21] = value; + + /* FIXME: these restrictions were tested on + 83+ SE; someone should confirm them for + 84+ */ + switch (value & 0x30) { + case 0x00: + /* restrict pp. 0, 2, 4, 6, 8, A, C, E */ + calc->hwregs[NO_EXEC_RAM] = 0x5555; + break; + + case 0x10: + /* restrict pp. 0, 3, 4, 7, 8, B, C, F */ + calc->hwregs[NO_EXEC_RAM] = 0x9999; + break; + + case 0x20: + /* restrict pp. 0, 3-7, 8, B-F */ + calc->hwregs[NO_EXEC_RAM] = 0xF9F9; + break; + + case 0x30: + /* restrict pp. 0, 3-F */ + calc->hwregs[NO_EXEC_RAM] = 0xFFF9; + break; + } + } + break; + + case 0x22: + if (calc->flash.unlock && calc->hwregs[PROTECTSTATE] == 7) { + calc->hwregs[PORT22] = value; + } + break; + + case 0x23: + if (calc->flash.unlock && calc->hwregs[PROTECTSTATE] == 7) { + calc->hwregs[PORT23] = value; + } + break; + + case 0x25: + if (calc->flash.unlock && calc->hwregs[PROTECTSTATE] == 7) { + calc->hwregs[PORT25] = value; + } + break; + + case 0x26: + if (calc->flash.unlock && calc->hwregs[PROTECTSTATE] == 7) { + calc->hwregs[PORT26] = value; + } + break; + + case 0x27: + calc->hwregs[PORT27] = value; + break; + + case 0x28: + calc->hwregs[PORT28] = value; + break; + + case 0x29: + calc->hwregs[PORT29] = value; + setup_clockdelays(calc); + break; + + case 0x2A: + calc->hwregs[PORT2A] = value; + setup_clockdelays(calc); + break; + + case 0x2B: + calc->hwregs[PORT2B] = value; + setup_clockdelays(calc); + break; + + case 0x2C: + calc->hwregs[PORT2C] = value; + setup_clockdelays(calc); + break; + + case 0x2D: + calc->hwregs[PORT2D] = value; + break; + + case 0x2E: + calc->hwregs[PORT2E] = value; + setup_clockdelays(calc); + break; + + case 0x2F: + calc->hwregs[PORT2F] = value; + break; + + case 0x30: + tilem_user_timer_set_frequency(calc, 0, value); + break; + case 0x31: + tilem_user_timer_set_mode(calc, 0, value); + break; + case 0x32: + tilem_user_timer_start(calc, 0, value); + break; + + case 0x33: + tilem_user_timer_set_frequency(calc, 1, value); + break; + case 0x34: + tilem_user_timer_set_mode(calc, 1, value); + break; + case 0x35: + tilem_user_timer_start(calc, 1, value); + break; + + case 0x36: + tilem_user_timer_set_frequency(calc, 2, value); + break; + case 0x37: + tilem_user_timer_set_mode(calc, 2, value); + break; + case 0x38: + tilem_user_timer_start(calc, 2, value); + break; + + case 0x40: + time(&curtime); + + if ((calc->hwregs[CLOCK_MODE] & 1) != (value & 1)) { + if (value & 1) + calc->hwregs[CLOCK_DIFF] -= curtime; + else + calc->hwregs[CLOCK_DIFF] += curtime; + } + + if (!(calc->hwregs[CLOCK_MODE] & 2) && (value & 2)) { + calc->hwregs[CLOCK_DIFF] = calc->hwregs[CLOCK_INPUT]; + if (value & 1) + calc->hwregs[CLOCK_DIFF] -= curtime; + } + calc->hwregs[CLOCK_MODE] = value & 3; + break; + + case 0x41: + calc->hwregs[CLOCK_INPUT] &= 0xffffff00; + calc->hwregs[CLOCK_INPUT] |= value; + break; + + case 0x42: + calc->hwregs[CLOCK_INPUT] &= 0xffff00ff; + calc->hwregs[CLOCK_INPUT] |= (value << 8); + break; + + case 0x43: + calc->hwregs[CLOCK_INPUT] &= 0xff00ffff; + calc->hwregs[CLOCK_INPUT] |= (value << 16); + break; + + case 0x44: + calc->hwregs[CLOCK_INPUT] &= 0x00ffffff; + calc->hwregs[CLOCK_INPUT] |= (value << 24); + break; + } + + return; +} + +void xn_z80_instr(TilemCalc* calc, dword opcode) +{ + dword pa; + byte l, h; + + switch (opcode) { + case 0xeded: + /* emulator control instruction */ + l = xn_z80_rdmem(calc, calc->z80.r.pc.d); + h = xn_z80_rdmem(calc, calc->z80.r.pc.d + 1); + + calc->z80.r.pc.d += 2; + + opcode = (l | (h << 8)); + switch (opcode) { + case 0x1000: /* Power off */ + case 0x1001: /* Power on */ + case 0x1002: /* Prepare for power off */ + break; + + case 0x1003: + /* Disable Nspire keypad */ + tilem_message(calc, "Keypad locked"); + break; + + case 0x1004: + /* Enable Nspire keypad */ + tilem_message(calc, "Keypad unlocked"); + break; + + case 0x1005: + /* ??? */ + break; + + case 0x1008: + /* check if USB data waiting (?) */ + calc->z80.r.af.d |= 0x40; + break; + + case 0x100C: + /* ??? */ + calc->z80.r.af.d &= ~0x40; + break; + + case 0x100D: + /* check if USB link should be used (???) */ + calc->z80.r.af.d &= ~0x40; + break; + + case 0x100E: + case 0x100F: + /* ??? */ + break; + + case 0x101C: + /* disable USB device (???) */ + break; + + case 0x1020: + /* check for something USB-related */ + calc->z80.r.af.d |= 0x40; + break; + + case 0x1024: + /* check if USB data waiting */ + calc->z80.r.af.d |= 0x40; + break; + + case 0x1029: + /* check for something USB-related */ + calc->z80.r.af.d |= 0x40; + break; + + case 0x1027: + /* check for something USB-related */ + calc->z80.r.af.d |= 0x40; + break; + + case 0x102E: + /* ??? */ + break; + + case 0x102F: + /* ??? */ + break; + + default: + tilem_warning(calc, "Unknown control instruction %x", + opcode); + } + break; + + case 0xedee: + /* erase Flash at address HL */ + if (calc->flash.unlock) { + pa = xn_mem_ltop(calc, calc->z80.r.hl.w.l); + if (pa < 0x200000) { + tilem_flash_erase_address(calc, pa); + } + } + break; + + case 0xedef: + /* enable Flash writing for following instruction */ + if (calc->flash.unlock) { + calc->flash.state = 3; + } + break; + + default: + tilem_warning(calc, "Invalid opcode %x", opcode); + tilem_z80_exception(calc, TILEM_EXC_INSTRUCTION); + break; + } +} + +void xn_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT1: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + + case TIMER_INT2A: + case TIMER_INT2B: + if (calc->hwregs[PORT3] & 0x04) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER2; + break; + + case TIMER_LCD_WAIT: + calc->hwregs[LCD_WAIT] = 0; + break; + } +} diff --git a/tool/tilem-src/emu/xn/xn_memory.c b/tool/tilem-src/emu/xn/xn_memory.c new file mode 100644 index 0000000..0956550 --- /dev/null +++ b/tool/tilem-src/emu/xn/xn_memory.c @@ -0,0 +1,204 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "xn.h" + +void xn_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + unsigned long pa; + byte page; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x200000) { + calc->z80.clock += calc->hwregs[FLASH_WRITE_DELAY]; + if (calc->flash.state == 3) { + *(calc->mem+pa) = v; + calc->flash.state = 0; + } + } + else if (pa < 0x220000) { + calc->z80.clock += calc->hwregs[RAM_WRITE_DELAY]; + *(calc->mem+pa) = v; + } +} + +static inline byte readbyte(TilemCalc* calc, dword pa) +{ + static const byte protectbytes[6] = {0x00,0x00,0xed,0x56,0xf3,0xd3}; + int state = calc->hwregs[PROTECTSTATE]; + byte value; + + value = *(calc->mem + pa); + + if (pa < 0x1B0000 || pa >= 0x200000 + || (pa >= 0x1C0000 && pa < 0x1F0000)) + calc->hwregs[PROTECTSTATE] = 0; + else if (state == 6) + calc->hwregs[PROTECTSTATE] = 7; + else if (state < 6 && value == protectbytes[state]) + calc->hwregs[PROTECTSTATE] = state + 1; + else + calc->hwregs[PROTECTSTATE] = 0; + + return (value); +} + +byte xn_z80_rdmem(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa; + byte value; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + if (TILEM_UNLIKELY(page == 0x7E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x200000) + calc->z80.clock += calc->hwregs[FLASH_READ_DELAY]; + else + calc->z80.clock += calc->hwregs[RAM_READ_DELAY]; + + value = readbyte(calc, pa); + return (value); +} + +byte xn_z80_rdmem_m1(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa; + byte value; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + if (TILEM_UNLIKELY((page & 0x80) + && calc->hwregs[NO_EXEC_RAM] & (1 << (page&7)))) { + tilem_warning(calc, "Executing in restricted RAM area"); + tilem_z80_exception(calc, TILEM_EXC_RAM_EXEC); + } + + if (TILEM_UNLIKELY(page >= calc->hwregs[PORT22] + && page <= calc->hwregs[PORT23])) { + tilem_warning(calc, "Executing in restricted Flash area"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + + if (TILEM_UNLIKELY(page == 0x7E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x200000) + calc->z80.clock += calc->hwregs[FLASH_EXEC_DELAY]; + else + calc->z80.clock += calc->hwregs[RAM_EXEC_DELAY]; + + value = readbyte(calc, pa); + + if (TILEM_UNLIKELY(value == 0xff && A == 0x0038)) { + tilem_warning(calc, "No OS installed"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + + return (value); +} + +dword xn_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + return ((page << 14) | (A & 0x3fff)); +} + +dword xn_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + + if (!page) + return (A & 0x3fff); + + if (page == calc->mempagemap[1]) + return (0x4000 | (A & 0x3fff)); + + if ((A & 0x3fff) < 64 * calc->hwregs[PORT28]) { + if (page == 0x81) + return (0x8000 | (A & 0x3fff)); + } + else { + if (page == calc->mempagemap[2]) + return (0x8000 | (A & 0x3fff)); + } + + if ((A & 0x3fff) >= (0x4000 - 64 * calc->hwregs[PORT27])) { + if (page == 0x80) + return (0xC000 | (A & 0x3fff)); + } + else { + if (page == calc->mempagemap[3]) + return (0xC000 | (A & 0x3fff)); + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/xn/xn_subcore.c b/tool/tilem-src/emu/xn/xn_subcore.c new file mode 100644 index 0000000..fb26037 --- /dev/null +++ b/tool/tilem-src/emu/xn/xn_subcore.c @@ -0,0 +1,73 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 + +#include "xn.h" + +static const TilemFlashSector flashsectors[] = { + {0x000000, 0x10000, 0}, {0x010000, 0x10000, 0}, + {0x020000, 0x10000, 0}, {0x030000, 0x10000, 0}, + {0x040000, 0x10000, 0}, {0x050000, 0x10000, 0}, + {0x060000, 0x10000, 0}, {0x070000, 0x10000, 0}, + {0x080000, 0x10000, 0}, {0x090000, 0x10000, 0}, + {0x0A0000, 0x10000, 0}, {0x0B0000, 0x10000, 0}, + {0x0C0000, 0x10000, 0}, {0x0D0000, 0x10000, 0}, + {0x0E0000, 0x10000, 0}, {0x0F0000, 0x10000, 0}, + {0x100000, 0x10000, 0}, {0x110000, 0x10000, 0}, + {0x120000, 0x10000, 0}, {0x130000, 0x10000, 0}, + {0x140000, 0x10000, 0}, {0x150000, 0x10000, 0}, + {0x160000, 0x10000, 0}, {0x170000, 0x10000, 0}, + {0x180000, 0x10000, 0}, {0x190000, 0x10000, 0}, + {0x1A0000, 0x10000, 0}, {0x1B0000, 0x10000, 0}, + {0x1C0000, 0x10000, 0}, {0x1D0000, 0x10000, 0}, + {0x1E0000, 0x10000, 0}, {0x1F0000, 0x08000, 0}, + {0x1F8000, 0x02000, 0}, {0x1FA000, 0x02000, 0}, + {0x1FC000, 0x04000, 0}}; + +#define NUM_FLASH_SECTORS (sizeof(flashsectors) / sizeof(TilemFlashSector)) + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +extern const char* xp_keynames[]; + +TilemHardware hardware_ti84pns = { + 'n', "ti84pns", "TI-Nspire (TI-84 Plus mode)", + (TILEM_CALC_HAS_LINK | TILEM_CALC_HAS_LINK_ASSIST + | TILEM_CALC_HAS_T6A04 | TILEM_CALC_HAS_FLASH + | TILEM_CALC_HAS_MD5_ASSIST), + 96, 64, 128 * 0x4000, 8 * 0x4000, 15 * 64, 0x80, + NUM_FLASH_SECTORS, flashsectors, 3, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + xp_keynames, + xn_reset, xn_stateloaded, + xn_z80_in, xn_z80_out, + xn_z80_wrmem, xn_z80_rdmem, xn_z80_rdmem_m1, xn_z80_instr, + xn_z80_ptimer, tilem_lcd_t6a04_get_data, + xn_mem_ltop, xn_mem_ptol }; diff --git a/tool/tilem-src/emu/xp/xp.h b/tool/tilem-src/emu/xp/xp.h new file mode 100644 index 0000000..3d78152 --- /dev/null +++ b/tool/tilem-src/emu/xp/xp.h @@ -0,0 +1,62 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#ifndef _TILEM_XP_H +#define _TILEM_XP_H + +enum { + PORT3, /* mask of enabled interrupts */ + PORT4, /* interrupt timer speed */ + PORT5, /* no-exec map index */ + PORT6, /* memory mapping bank A */ + PORT7, /* memory mapping bank B */ + NOEXEC0, /* no-exec map */ + NOEXEC1, + NOEXEC2, + NOEXEC3, + NOEXEC4, + PROTECTSTATE, /* port protection state */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES \ + { "port3", "port4", "port5", "port6", "port7", \ + "noexec0", "noexec1", "noexec2", "noexec3", \ + "noexec4", "protectstate" } + +#define TIMER_INT1 (TILEM_NUM_SYS_TIMERS + 1) +#define TIMER_INT2A (TILEM_NUM_SYS_TIMERS + 2) +#define TIMER_INT2B (TILEM_NUM_SYS_TIMERS + 3) +#define NUM_HW_TIMERS 3 + +#define HW_TIMER_NAMES { "int1", "int2a", "int2b" } + +void xp_reset(TilemCalc* calc); +void xp_stateloaded(TilemCalc* calc, int savtype); +byte xp_z80_in(TilemCalc* calc, dword port); +void xp_z80_out(TilemCalc* calc, dword port, byte value); +void xp_z80_ptimer(TilemCalc* calc, int id); +void xp_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte xp_z80_rdmem(TilemCalc* calc, dword addr); +byte xp_z80_rdmem_m1(TilemCalc* calc, dword addr); +dword xp_mem_ltop(TilemCalc* calc, dword addr); +dword xp_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/xp/xp_init.c b/tool/tilem-src/emu/xp/xp_init.c new file mode 100644 index 0000000..0189236 --- /dev/null +++ b/tool/tilem-src/emu/xp/xp_init.c @@ -0,0 +1,67 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "xp.h" + +void xp_reset(TilemCalc* calc) +{ + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x77; + calc->hwregs[PORT6] = 0x1F; + calc->hwregs[PORT7] = 0x1F; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x1E; + calc->mempagemap[2] = 0x1F; + calc->mempagemap[3] = 0x1F; + + calc->z80.r.pc.d = 0x8000; + + calc->hwregs[PORT5] = 0; + calc->hwregs[NOEXEC0] = 0; + calc->hwregs[NOEXEC1] = 0; + calc->hwregs[NOEXEC2] = 0; + calc->hwregs[NOEXEC3] = 0; + calc->hwregs[NOEXEC4] = 0; + + calc->hwregs[PROTECTSTATE] = 0; + + tilem_linkport_set_mode(calc, TILEM_LINK_MODE_NO_TIMEOUT); + + tilem_z80_set_speed(calc, 6000); + + tilem_z80_set_timer(calc, TIMER_INT1, 1600, 8474, 1); + tilem_z80_set_timer(calc, TIMER_INT2A, 1300, 8474, 1); + tilem_z80_set_timer(calc, TIMER_INT2B, 1000, 8474, 1); +} + +void xp_stateloaded(TilemCalc* calc, int savtype TILEM_ATTR_UNUSED) +{ + tilem_calc_fix_certificate(calc, calc->mem + (0x1E * 0x4000L), + 0x15, 0x0c, 0x1f18); +} diff --git a/tool/tilem-src/emu/xp/xp_io.c b/tool/tilem-src/emu/xp/xp_io.c new file mode 100644 index 0000000..d3fe73f --- /dev/null +++ b/tool/tilem-src/emu/xp/xp_io.c @@ -0,0 +1,284 @@ +/* + * 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 + +#include "xp.h" + +byte xp_z80_in(TilemCalc* calc, dword port) +{ + /* FIXME: measure actual levels */ + static const byte battlevel[4] = { 33, 39, 36, 43 }; + byte v; + + switch(port&0x1f) { + case 0x00: + case 0x08: + v = tilem_linkport_get_lines(calc); + v |= (calc->linkport.lines << 4); + if (calc->linkport.mode & TILEM_LINK_MODE_ASSIST) + v |= 0x04; + if (calc->linkport.assistflags & TILEM_LINK_ASSIST_READ_BYTE) + v |= 0x08; + else if (calc->linkport.assistflags + & TILEM_LINK_ASSIST_READ_BUSY) + v |= 0x40; + + return(v); + + case 0x01: + case 0x09: + return(tilem_keypad_read_keys(calc)); + + case 0x02: + case 0x0A: + v = battlevel[calc->hwregs[PORT4] >> 6]; + return((calc->battery >= v ? 3 : 2) + | (calc->flash.unlock << 2) + | (calc->hwregs[PORT5] << 3)); + + case 0x03: + case 0x0B: + return(calc->hwregs[PORT3]); + + case 0x04: + case 0x0C: + v = (calc->keypad.onkeydown ? 0x00 : 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER2) + v |= 0x04; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ACTIVE) + v |= 0x10; + + return(v); + + case 0x05: + case 0x0D: + return(tilem_linkport_read_byte(calc)); + + case 0x06: + case 0x0E: + return(calc->hwregs[PORT6]); + + case 0x07: + case 0x0F: + return(calc->hwregs[PORT7]); + + case 0x10: + case 0x12: + case 0x18: + case 0x1A: + return(tilem_lcd_t6a04_status(calc)); + + case 0x11: + case 0x13: + case 0x19: + case 0x1B: + return(tilem_lcd_t6a04_read(calc)); + } + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB; + + if (calc->hwregs[PORT6] & 0x40) + pageA = (0x20 | (calc->hwregs[PORT6] & 1)); + else + pageA = (calc->hwregs[PORT6] & 0x1f); + + if (calc->hwregs[PORT7] & 0x40) + pageB = (0x20 | (calc->hwregs[PORT7] & 1)); + else + pageB = (calc->hwregs[PORT7] & 0x1f); + + if (calc->hwregs[PORT4] & 1) { + calc->mempagemap[1] = (pageA & ~1); + calc->mempagemap[2] = pageA; + calc->mempagemap[3] = pageB; + } + else { + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = 0x20; + } +} + +void xp_z80_out(TilemCalc* calc, dword port, byte value) +{ + static const int tmrvalues[4] = { 1786, 4032, 5882, 8474 }; + int t; + unsigned int mode; + + switch(port&0x1f) { + case 0x00: + case 0x08: + mode = calc->linkport.mode; + if (value & 4) + mode |= TILEM_LINK_MODE_ASSIST; + else + mode &= ~TILEM_LINK_MODE_ASSIST; + + tilem_linkport_set_mode(calc, mode); + tilem_linkport_set_lines(calc, value); + break; + + case 0x01: + case 0x09: + tilem_keypad_set_group(calc, value); + break; + + case 0x03: + case 0x0B: + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + + if (!(value & 0x04)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER2; + + mode = calc->linkport.mode; + if (value & 0x10) + mode |= TILEM_LINK_MODE_INT_ON_ACTIVE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ACTIVE; + + tilem_linkport_set_mode(calc, mode); + + calc->poweronhalt = ((value & 8) >> 3); + calc->hwregs[PORT3] = value; + break; + + case 0x04: + case 0x0C: + calc->hwregs[PORT4] = value; + + t = tmrvalues[(value & 6) >> 1]; + tilem_z80_set_timer_period(calc, TIMER_INT1, t); + tilem_z80_set_timer_period(calc, TIMER_INT2A, t); + tilem_z80_set_timer_period(calc, TIMER_INT2B, t); + + setup_mapping(calc); + break; + + case 0x05: + case 0x0D: + calc->hwregs[PORT5] = value & 0x07; + break; + + case 0x06: + case 0x0E: + calc->hwregs[PORT6] = value & 0x7f; + setup_mapping(calc); + break; + + case 0x07: + case 0x0F: + calc->hwregs[PORT7] = value & 0x7f; + setup_mapping(calc); + break; + + case 0x10: + case 0x12: + case 0x18: + case 0x1A: + tilem_lcd_t6a04_control(calc, value); + break; + + case 0x11: + case 0x13: + case 0x19: + case 0x1B: + tilem_lcd_t6a04_write(calc, value); + break; + + case 0x14: + case 0x15: + if (calc->hwregs[PROTECTSTATE] == 7) { + if (value & 1) + tilem_message(calc, "Flash unlocked"); + else + tilem_message(calc, "Flash locked"); + calc->flash.unlock = value&1; + } + break; + + case 0x16: + case 0x17: + if (calc->flash.unlock && calc->hwregs[PROTECTSTATE] == 7) { + switch(calc->hwregs[PORT5]) { + case 0: + tilem_message(calc, "No-exec mask for 08-0F set to %x", value); + calc->hwregs[NOEXEC1] = value; + break; + case 1: + tilem_message(calc, "No-exec mask for 10-17 set to %x", value); + calc->hwregs[NOEXEC2] = value; + break; + case 2: + tilem_message(calc, "No-exec mask for 18-1B set to %x", value); + calc->hwregs[NOEXEC3] = value & 0x0f; + break; + case 7: + tilem_message(calc, "No-exec mask for RAM set to %x", value); + calc->hwregs[NOEXEC4] = (value & 1) | ((value>>4) & 2); + break; + } + } + break; + } + + return; +} + +void xp_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT1: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + + case TIMER_INT2A: + case TIMER_INT2B: + if (calc->hwregs[PORT3] & 0x04) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER2; + break; + } +} diff --git a/tool/tilem-src/emu/xp/xp_memory.c b/tool/tilem-src/emu/xp/xp_memory.c new file mode 100644 index 0000000..ad717ad --- /dev/null +++ b/tool/tilem-src/emu/xp/xp_memory.c @@ -0,0 +1,135 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "xp.h" + +void xp_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + unsigned long pa; + + pa = (A & 0x3FFF) + 0x4000*calc->mempagemap[(A)>>14]; + + if (pa < 0x80000) + tilem_flash_write_byte(calc, pa, v); + + else if (pa < 0x88000) + *(calc->mem+pa) = v; +} + +static inline byte readbyte(TilemCalc* calc, dword pa) +{ + static const byte protectbytes[6] = {0x00,0x00,0xed,0x56,0xf3,0xd3}; + int state = calc->hwregs[PROTECTSTATE]; + byte value; + + if (pa < 0x80000 && (calc->flash.state || calc->flash.busy)) + value = tilem_flash_read_byte(calc, pa); + else + value = *(calc->mem + pa); + + if (pa < 0x70000 || pa >= 0x80000) + calc->hwregs[PROTECTSTATE] = 0; + else if (state == 6) + calc->hwregs[PROTECTSTATE] = 7; + else if (state < 6 && value == protectbytes[state]) + calc->hwregs[PROTECTSTATE] = state + 1; + else + calc->hwregs[PROTECTSTATE] = 0; + + return (value); +} + +byte xp_z80_rdmem(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa; + byte value; + + page = calc->mempagemap[A>>14]; + pa = 0x4000 * page + (A & 0x3FFF); + + if (TILEM_UNLIKELY(page == 0x1E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + value = readbyte(calc, pa); + return (value); +} + +byte xp_z80_rdmem_m1(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa; + byte value; + + page = calc->mempagemap[A>>14]; + pa = 0x4000 * page + (A & 0x3FFF); + + if (TILEM_UNLIKELY(calc->hwregs[NOEXEC0+page/8] & (1<<(page%8)))) { + tilem_warning(calc, "Executing in restricted %s area", + page>0x1f?"RAM":"Flash"); + tilem_z80_exception(calc, (page > 0x1f + ? TILEM_EXC_RAM_EXEC + : TILEM_EXC_FLASH_EXEC)); + } + + if (TILEM_UNLIKELY(page == 0x1E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + value = readbyte(calc, pa); + + if (TILEM_UNLIKELY(value == 0xff && A == 0x0038)) { + tilem_warning(calc, "No OS installed"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + + return (value); +} + +dword xp_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + return ((page << 14) | (A & 0x3fff)); +} + +dword xp_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + int i; + + for (i = 0; i < 4; i++) { + if (calc->mempagemap[i] == page) { + return ((i << 14) | (A & 0x3fff)); + } + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/xp/xp_subcore.c b/tool/tilem-src/emu/xp/xp_subcore.c new file mode 100644 index 0000000..3c6ed28 --- /dev/null +++ b/tool/tilem-src/emu/xp/xp_subcore.c @@ -0,0 +1,73 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 + +#include "xp.h" + +static const TilemFlashSector flashsectors[] = { + {0x000000, 0x10000, 0}, + {0x010000, 0x10000, 0}, + {0x020000, 0x10000, 0}, + {0x030000, 0x10000, 0}, + {0x040000, 0x10000, 0}, + {0x050000, 0x10000, 0}, + {0x060000, 0x10000, 0}, + {0x070000, 0x08000, 0}, + {0x078000, 0x02000, 0}, + {0x07A000, 0x02000, 0}, + {0x07C000, 0x04000, 1}}; + +#define NUM_FLASH_SECTORS (sizeof(flashsectors) / sizeof(TilemFlashSector)) + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +const char* xp_keynames[64] = { + "Down", "Left", "Right", "Up", 0, 0, 0, 0, + "Enter", "Add", "Sub", "Mul", "Div", "Power", "Clear", 0, + "Chs", "3", "6", "9", "RParen", "Tan", "Vars", 0, + "DecPnt", "2", "5", "8", "LParen", "Cos", "Prgm", "Stat", + "0", "1", "4", "7", "Comma", "Sin", "Apps", "Graphvar", + "On", "Store", "Ln", "Log", "Square", "Recip", "Math", "Alpha", + "Graph", "Trace", "Zoom", "Window", "YEqu", "2nd", "Mode", "Del", + 0, 0, 0, 0, 0, 0, 0, 0}; + +TilemHardware hardware_ti83p = { + 'p', "ti83p", "TI-83 Plus", + (TILEM_CALC_HAS_LINK | TILEM_CALC_HAS_LINK_ASSIST + | TILEM_CALC_HAS_T6A04 | TILEM_CALC_HAS_FLASH), + 96, 64, 32 * 0x4000, 2 * 0x4000, 15 * 64, 0x40, + NUM_FLASH_SECTORS, flashsectors, 0, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + xp_keynames, + xp_reset, xp_stateloaded, + xp_z80_in, xp_z80_out, + xp_z80_wrmem, xp_z80_rdmem, xp_z80_rdmem_m1, NULL, + xp_z80_ptimer, tilem_lcd_t6a04_get_data, + xp_mem_ltop, xp_mem_ptol }; diff --git a/tool/tilem-src/emu/xs/xs.h b/tool/tilem-src/emu/xs/xs.h new file mode 100644 index 0000000..6e3e602 --- /dev/null +++ b/tool/tilem-src/emu/xs/xs.h @@ -0,0 +1,101 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2009-2011 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 + * . + */ + +#ifndef _TILEM_XS_H +#define _TILEM_XS_H + +enum { + PORT3, /* mask of enabled interrupts */ + PORT4, /* interrupt timer speed */ + PORT5, /* memory mapping bank C */ + PORT6, /* memory mapping bank A */ + PORT7, /* memory mapping bank B */ + PORT8, /* link assist mode flags */ + PORT9, /* unknown (link assist settings?) */ + PORTA, /* unknown (timeout value?) */ + PORTB, /* unknown (timeout value?) */ + PORTC, /* unknown (timeout value?) */ + PORTD, /* unknown */ + PORTE, /* unknown */ + PORTF, /* unknown */ + + PORT20, /* CPU speed control */ + PORT21, /* hardware type / RAM no-exec control */ + PORT22, /* Flash no-exec lower limit */ + PORT23, /* Flash no-exec upper limit */ + PORT25, /* RAM no-exec lower limit */ + PORT26, /* RAM no-exec upper limit */ + PORT27, /* bank C forced-page-0 limit */ + PORT28, /* bank B forced-page-1 limit */ + PORT29, /* LCD port delay (6 MHz) */ + PORT2A, /* LCD port delay (mode 1) */ + PORT2B, /* LCD port delay (mode 2) */ + PORT2C, /* LCD port delay (mode 3) */ + PORT2D, /* unknown */ + PORT2E, /* memory delay */ + PORT2F, /* Duration of LCD wait timer */ + + RAM_READ_DELAY, + RAM_WRITE_DELAY, + RAM_EXEC_DELAY, + FLASH_READ_DELAY, + FLASH_WRITE_DELAY, + FLASH_EXEC_DELAY, + LCD_PORT_DELAY, + NO_EXEC_RAM_MASK, + NO_EXEC_RAM_LOWER, + NO_EXEC_RAM_UPPER, + + LCD_WAIT, /* LCD wait timer active */ + PROTECTSTATE, /* port protection state */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES \ + { "port3", "port4", "port5", "port6", "port7", "port8", "port9", \ + "portA", "portB", "portC", "portD", "portE", "portF", "port20", \ + "port21", "port22", "port23", "port25", "port26", "port27", \ + "port28", "port29", "port2A", "port2B", "port2C", "port2D", \ + "port2E", "port2F", "ram_read_delay", "ram_write_delay", \ + "ram_exec_delay", "flash_read_delay", "flash_write_delay", \ + "flash_exec_delay", "lcd_port_delay", "no_exec_ram_mask", \ + "no_exec_ram_lower", "no_exec_ram_upper", \ + "lcd_wait", "protectstate" } + +#define TIMER_INT1 (TILEM_NUM_SYS_TIMERS + 1) +#define TIMER_INT2A (TILEM_NUM_SYS_TIMERS + 2) +#define TIMER_INT2B (TILEM_NUM_SYS_TIMERS + 3) +#define TIMER_LCD_WAIT (TILEM_NUM_SYS_TIMERS + 4) +#define NUM_HW_TIMERS 4 + +#define HW_TIMER_NAMES { "int1", "int2a", "int2b", "lcd_wait" } + +void xs_reset(TilemCalc* calc); +void xs_stateloaded(TilemCalc* calc, int savtype); +byte xs_z80_in(TilemCalc* calc, dword port); +void xs_z80_out(TilemCalc* calc, dword port, byte value); +void xs_z80_ptimer(TilemCalc* calc, int id); +void xs_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte xs_z80_rdmem(TilemCalc* calc, dword addr); +byte xs_z80_rdmem_m1(TilemCalc* calc, dword addr); +dword xs_mem_ltop(TilemCalc* calc, dword addr); +dword xs_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/xs/xs_init.c b/tool/tilem-src/emu/xs/xs_init.c new file mode 100644 index 0000000..1e05164 --- /dev/null +++ b/tool/tilem-src/emu/xs/xs_init.c @@ -0,0 +1,91 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 + +#include "xs.h" + +void xp_fix_cert(TilemCalc* calc, byte *cert); + +void xs_reset(TilemCalc* calc) +{ + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x77; + calc->hwregs[PORT6] = 0x7F; + calc->hwregs[PORT7] = 0x7F; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x7E; + calc->mempagemap[2] = 0x7F; + calc->mempagemap[3] = 0x7F; + + calc->z80.r.pc.d = 0x8000; + + calc->hwregs[PORT8] = 0x80; + + calc->hwregs[PORT20] = 0; + calc->hwregs[PORT21] = 1; + calc->hwregs[PORT22] = 0x08; + calc->hwregs[PORT23] = 0x69; + calc->hwregs[PORT25] = 0x10; + calc->hwregs[PORT26] = 0x20; + calc->hwregs[PORT27] = 0; + calc->hwregs[PORT28] = 0; + calc->hwregs[PORT29] = 0x14; + calc->hwregs[PORT2A] = 0x27; + calc->hwregs[PORT2B] = 0x2F; + calc->hwregs[PORT2C] = 0x3B; + calc->hwregs[PORT2D] = 0x01; + calc->hwregs[PORT2E] = 0x44; + calc->hwregs[PORT2F] = 0x4A; + + calc->hwregs[FLASH_READ_DELAY] = 0; + calc->hwregs[FLASH_WRITE_DELAY] = 0; + calc->hwregs[FLASH_EXEC_DELAY] = 0; + calc->hwregs[RAM_READ_DELAY] = 0; + calc->hwregs[RAM_WRITE_DELAY] = 0; + calc->hwregs[RAM_EXEC_DELAY] = 0; + calc->hwregs[LCD_PORT_DELAY] = 5; + calc->hwregs[NO_EXEC_RAM_MASK] = 0x7C00; + calc->hwregs[NO_EXEC_RAM_LOWER] = 0x4000; + calc->hwregs[NO_EXEC_RAM_UPPER] = 0x8000; + + calc->hwregs[PROTECTSTATE] = 0; + + calc->flash.overridegroup = 1; + + tilem_z80_set_speed(calc, 6000); + + tilem_z80_set_timer(calc, TIMER_INT1, 1600, 9277, 1); + tilem_z80_set_timer(calc, TIMER_INT2A, 1300, 9277, 1); + tilem_z80_set_timer(calc, TIMER_INT2B, 1000, 9277, 1); +} + +void xs_stateloaded(TilemCalc* calc, int savtype TILEM_ATTR_UNUSED) +{ + tilem_calc_fix_certificate(calc, calc->mem + (0x7E * 0x4000L), + 0x69, 0x0c, 0x1e50); +} diff --git a/tool/tilem-src/emu/xs/xs_io.c b/tool/tilem-src/emu/xs/xs_io.c new file mode 100644 index 0000000..ef6f27f --- /dev/null +++ b/tool/tilem-src/emu/xs/xs_io.c @@ -0,0 +1,658 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 + +#include "xs.h" + +static void set_lcd_wait_timer(TilemCalc* calc) +{ + static const int delaytime[8] = { 48, 112, 176, 240, + 304, 368, 432, 496 }; + int i; + + switch (calc->hwregs[PORT20] & 3) { + case 0: + return; + case 1: + i = (calc->hwregs[PORT2F] & 3); + break; + case 2: + i = ((calc->hwregs[PORT2F] >> 2) & 7); + break; + default: + i = ((calc->hwregs[PORT2F] >> 5) & 7); + break; + } + + tilem_z80_set_timer(calc, TIMER_LCD_WAIT, delaytime[i], 0, 0); + calc->hwregs[LCD_WAIT] = 1; +} + +byte xs_z80_in(TilemCalc* calc, dword port) +{ + /* FIXME: measure actual levels */ + static const byte battlevel[4] = { 33, 39, 36, 43 }; + byte v; + unsigned int f; + + switch(port&0xff) { + case 0x00: + v = tilem_linkport_get_lines(calc); + v |= (calc->linkport.lines << 4); + return(v); + + case 0x01: + return(tilem_keypad_read_keys(calc)); + + case 0x02: + v = battlevel[calc->hwregs[PORT4] >> 6]; + return((calc->battery >= v ? 0xc1 : 0xc0) + | (calc->hwregs[LCD_WAIT] ? 0 : 2) + | (calc->flash.unlock << 2)); + + case 0x03: + return(calc->hwregs[PORT3]); + + case 0x04: + v = (calc->keypad.onkeydown ? 0x00 : 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER2) + v |= 0x04; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ACTIVE) + v |= 0x10; + + if (calc->usertimers[0].status & TILEM_USER_TIMER_FINISHED) + v |= 0x20; + if (calc->usertimers[1].status & TILEM_USER_TIMER_FINISHED) + v |= 0x40; + if (calc->usertimers[2].status & TILEM_USER_TIMER_FINISHED) + v |= 0x80; + + return(v); + + case 0x05: + return(calc->hwregs[PORT5] & 0x0f); + + case 0x06: + return(calc->hwregs[PORT6]); + + case 0x07: + return(calc->hwregs[PORT7]); + + case 0x08: + return(calc->hwregs[PORT8]); + + case 0x09: + f = tilem_linkport_get_assist_flags(calc); + + if (f & (TILEM_LINK_ASSIST_READ_BUSY + | TILEM_LINK_ASSIST_WRITE_BUSY)) + v = 0x00; + else + v = 0x20; + + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_READ) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_IDLE) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ERROR) + v |= 0x04; + if (f & TILEM_LINK_ASSIST_READ_BUSY) + v |= 0x08; + if (f & TILEM_LINK_ASSIST_READ_BYTE) + v |= 0x10; + if (f & (TILEM_LINK_ASSIST_READ_ERROR + | TILEM_LINK_ASSIST_WRITE_ERROR)) + v |= 0x40; + if (f & TILEM_LINK_ASSIST_WRITE_BUSY) + v |= 0x80; + + calc->z80.interrupts &= ~TILEM_INTERRUPT_LINK_ERROR; + + return(v); + + case 0x0A: + v = calc->linkport.assistlastbyte; + tilem_linkport_read_byte(calc); + return(v); + + case 0x0E: + return(calc->hwregs[PORTE] & 3); + + case 0x0F: + return(calc->hwregs[PORTF] & 3); + + case 0x10: + case 0x12: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + return(tilem_lcd_t6a04_status(calc)); + + case 0x11: + case 0x13: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + return(tilem_lcd_t6a04_read(calc)); + + case 0x15: + return(0x33); /* ??? */ + + case 0x1C: + return(tilem_md5_assist_get_value(calc)); + + case 0x1D: + return(tilem_md5_assist_get_value(calc) >> 8); + + case 0x1E: + return(tilem_md5_assist_get_value(calc) >> 16); + + case 0x1F: + return(tilem_md5_assist_get_value(calc) >> 24); + + case 0x20: + return(calc->hwregs[PORT20] & 3); + + case 0x21: + return(calc->hwregs[PORT21] & 0x33); + + case 0x22: + return(calc->hwregs[PORT22]); + + case 0x23: + return(calc->hwregs[PORT23]); + + case 0x25: + return(calc->hwregs[PORT25]); + + case 0x26: + return(calc->hwregs[PORT26]); + + case 0x27: + return(calc->hwregs[PORT27]); + + case 0x28: + return(calc->hwregs[PORT28]); + + case 0x29: + return(calc->hwregs[PORT29]); + + case 0x2A: + return(calc->hwregs[PORT2A]); + + case 0x2B: + return(calc->hwregs[PORT2B]); + + case 0x2C: + return(calc->hwregs[PORT2C]); + + case 0x2D: + return(calc->hwregs[PORT2D] & 3); + + case 0x2E: + return(calc->hwregs[PORT2E]); + + case 0x2F: + return(calc->hwregs[PORT2F]); + + case 0x30: + return(calc->usertimers[0].frequency); + case 0x31: + return(calc->usertimers[0].status); + case 0x32: + return(tilem_user_timer_get_value(calc, 0)); + + case 0x33: + return(calc->usertimers[1].frequency); + case 0x34: + return(calc->usertimers[1].status); + case 0x35: + return(tilem_user_timer_get_value(calc, 1)); + + case 0x36: + return(calc->usertimers[2].frequency); + case 0x37: + return(calc->usertimers[2].status); + case 0x38: + return(tilem_user_timer_get_value(calc, 2)); + + case 0x0B: + case 0x0C: + case 0x0D: + case 0x14: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + return(0); + } + + return(0xff); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB, pageC; + + if (calc->hwregs[PORT6] & 0x80) + pageA = (0x80 | (calc->hwregs[PORT6] & 7)); + else + pageA = (calc->hwregs[PORT6] & 0x7f); + + if (calc->hwregs[PORT7] & 0x80) + pageB = (0x80 | (calc->hwregs[PORT7] & 7)); + else + pageB = (calc->hwregs[PORT7] & 0x7f); + + pageC = (0x80 | (calc->hwregs[PORT5] & 7)); + + if (calc->hwregs[PORT4] & 1) { + calc->mempagemap[1] = (pageA & ~1); + calc->mempagemap[2] = (pageA | 1); + calc->mempagemap[3] = pageB; + } + else { + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = pageC; + } +} + +static void setup_clockdelays(TilemCalc* calc) +{ + byte lcdport = calc->hwregs[PORT29 + (calc->hwregs[PORT20] & 3)]; + byte memport = calc->hwregs[PORT2E]; + + if (!(lcdport & 1)) + memport &= ~0x07; + if (!(lcdport & 2)) + memport &= ~0x70; + + calc->hwregs[FLASH_EXEC_DELAY] = (memport & 1); + calc->hwregs[FLASH_READ_DELAY] = ((memport >> 1) & 1); + calc->hwregs[FLASH_WRITE_DELAY] = ((memport >> 2) & 1); + + calc->hwregs[RAM_EXEC_DELAY] = ((memport >> 4) & 1); + calc->hwregs[RAM_READ_DELAY] = ((memport >> 5) & 1); + calc->hwregs[RAM_WRITE_DELAY] = ((memport >> 6) & 1); + + calc->hwregs[LCD_PORT_DELAY] = (lcdport >> 2); +} + +void xs_z80_out(TilemCalc* calc, dword port, byte value) +{ + static const int tmrvalues[4] = { 1953, 4395, 6836, 9277 }; + int t, r; + unsigned int mode; + + switch(port&0xff) { + case 0x00: + tilem_linkport_set_lines(calc, value); + break; + + case 0x01: + tilem_keypad_set_group(calc, value); + break; + + case 0x03: + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + + if (!(value & 0x04)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER2; + + if (value & 0x06) { + calc->usertimers[0].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[1].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[2].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + } + else { + calc->usertimers[0].status |= TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[1].status |= TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[2].status |= TILEM_USER_TIMER_NO_HALT_INT; + } + + mode = calc->linkport.mode; + if (value & 0x10) + mode |= TILEM_LINK_MODE_INT_ON_ACTIVE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ACTIVE; + + tilem_linkport_set_mode(calc, mode); + + calc->poweronhalt = ((value & 8) >> 3); + calc->hwregs[PORT3] = value; + break; + + case 0x04: + calc->hwregs[PORT4] = value; + + t = tmrvalues[(value & 6) >> 1]; + tilem_z80_set_timer_period(calc, TIMER_INT1, t); + tilem_z80_set_timer_period(calc, TIMER_INT2A, t); + tilem_z80_set_timer_period(calc, TIMER_INT2B, t); + + setup_mapping(calc); + break; + + case 0x05: + calc->hwregs[PORT5] = value & 0x0f; + setup_mapping(calc); + break; + + case 0x06: + calc->hwregs[PORT6] = value; + setup_mapping(calc); + break; + + case 0x07: + calc->hwregs[PORT7] = value; + setup_mapping(calc); + break; + + case 0x08: + calc->hwregs[PORT8] = value; + + mode = calc->linkport.mode; + + if (value & 0x01) + mode |= TILEM_LINK_MODE_INT_ON_READ; + else + mode &= ~TILEM_LINK_MODE_INT_ON_READ; + + if (value & 0x02) + mode |= TILEM_LINK_MODE_INT_ON_IDLE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_IDLE; + + if (value & 0x04) + mode |= TILEM_LINK_MODE_INT_ON_ERROR; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ERROR; + + if (value & 0x80) + mode &= ~TILEM_LINK_MODE_ASSIST; + else + mode |= TILEM_LINK_MODE_ASSIST; + + tilem_linkport_set_mode(calc, mode); + break; + + case 0x09: + calc->hwregs[PORT9] = value; + break; + + case 0x0A: + calc->hwregs[PORTA] = value; + break; + + case 0x0B: + calc->hwregs[PORTB] = value; + break; + + case 0x0C: + calc->hwregs[PORTC] = value; + break; + + case 0x0D: + if (!(calc->hwregs[PORT8] & 0x80)) + tilem_linkport_write_byte(calc, value); + break; + + case 0x0E: + calc->hwregs[PORTE] = value; + break; + + case 0x0F: + calc->hwregs[PORTF] = value; + break; + + case 0x10: + case 0x12: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + tilem_lcd_t6a04_control(calc, value); + break; + + case 0x11: + case 0x13: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + tilem_lcd_t6a04_write(calc, value); + break; + + case 0x14: + if (calc->hwregs[PROTECTSTATE] == 7) { + if (value & 1) + tilem_message(calc, "Flash unlocked"); + else + tilem_message(calc, "Flash locked"); + calc->flash.unlock = value&1; + } + else { + tilem_warning(calc, "Writing to protected port 14"); + } + break; + + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + r = (port & 0xff) - 0x18; + calc->md5assist.regs[r] >>= 8; + calc->md5assist.regs[r] |= (value << 24); + break; + + case 0x1E: + calc->md5assist.shift = value & 0x1f; + break; + + case 0x1F: + calc->md5assist.mode = value & 3; + break; + + case 0x20: + calc->hwregs[PORT20] = value; + + if (value & 3) { + tilem_z80_set_speed(calc, 15000); + } + else { + tilem_z80_set_speed(calc, 6000); + } + + setup_clockdelays(calc); + break; + + case 0x21: + if (calc->flash.unlock) { + calc->hwregs[PORT21] = value; + t = (value >> 4) & 3; + calc->hwregs[NO_EXEC_RAM_MASK] = (0x8000 << t) - 0x400; + calc->flash.overridegroup = value & 3; + } + else { + tilem_warning(calc, "Writing to protected port 21"); + } + break; + + case 0x22: + if (calc->flash.unlock) { + calc->hwregs[PORT22] = value; + } + else { + tilem_warning(calc, "Writing to protected port 22"); + } + break; + + case 0x23: + if (calc->flash.unlock) { + calc->hwregs[PORT23] = value; + } + else { + tilem_warning(calc, "Writing to protected port 23"); + } + break; + + case 0x25: + if (calc->flash.unlock) { + calc->hwregs[PORT25] = value; + calc->hwregs[NO_EXEC_RAM_LOWER] = value * 0x400; + } + else { + tilem_warning(calc, "Writing to protected port 25"); + } + break; + + case 0x26: + if (calc->flash.unlock) { + calc->hwregs[PORT26] = value; + calc->hwregs[NO_EXEC_RAM_UPPER] = value * 0x400; + } + else { + tilem_warning(calc, "Writing to protected port 26"); + } + break; + + case 0x27: + calc->hwregs[PORT27] = value; + break; + + case 0x28: + calc->hwregs[PORT28] = value; + break; + + case 0x29: + calc->hwregs[PORT29] = value; + setup_clockdelays(calc); + break; + + case 0x2A: + calc->hwregs[PORT2A] = value; + setup_clockdelays(calc); + break; + + case 0x2B: + calc->hwregs[PORT2B] = value; + setup_clockdelays(calc); + break; + + case 0x2C: + calc->hwregs[PORT2C] = value; + setup_clockdelays(calc); + break; + + case 0x2D: + calc->hwregs[PORT2D] = value; + setup_clockdelays(calc); + break; + + case 0x2E: + calc->hwregs[PORT2E] = value; + setup_clockdelays(calc); + break; + + case 0x2F: + calc->hwregs[PORT2F] = value; + break; + + case 0x30: + tilem_user_timer_set_frequency(calc, 0, value); + break; + case 0x31: + tilem_user_timer_set_mode(calc, 0, value); + break; + case 0x32: + tilem_user_timer_start(calc, 0, value); + break; + + case 0x33: + tilem_user_timer_set_frequency(calc, 1, value); + break; + case 0x34: + tilem_user_timer_set_mode(calc, 1, value); + break; + case 0x35: + tilem_user_timer_start(calc, 1, value); + break; + + case 0x36: + tilem_user_timer_set_frequency(calc, 2, value); + break; + case 0x37: + tilem_user_timer_set_mode(calc, 2, value); + break; + case 0x38: + tilem_user_timer_start(calc, 2, value); + break; + } + + return; +} + +void xs_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT1: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + + case TIMER_INT2A: + case TIMER_INT2B: + if (calc->hwregs[PORT3] & 0x04) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER2; + break; + + case TIMER_LCD_WAIT: + calc->hwregs[LCD_WAIT] = 0; + break; + } +} diff --git a/tool/tilem-src/emu/xs/xs_memory.c b/tool/tilem-src/emu/xs/xs_memory.c new file mode 100644 index 0000000..c007e1a --- /dev/null +++ b/tool/tilem-src/emu/xs/xs_memory.c @@ -0,0 +1,213 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "xs.h" + +/* FIXME: what effect, if any, do ports 27 and 28 have in memory + mapping mode 1? */ + +void xs_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + unsigned long pa; + byte page; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa<0x200000) { + calc->z80.clock += calc->hwregs[FLASH_WRITE_DELAY]; + tilem_flash_write_byte(calc, pa, v); + } + else if (pa < 0x220000) { + calc->z80.clock += calc->hwregs[RAM_WRITE_DELAY]; + *(calc->mem+pa) = v; + } +} + +static inline byte readbyte(TilemCalc* calc, dword pa) +{ + static const byte protectbytes[6] = {0x00,0x00,0xed,0x56,0xf3,0xd3}; + int state = calc->hwregs[PROTECTSTATE]; + byte value; + + if (pa < 0x200000 && (calc->flash.state || calc->flash.busy)) + value = tilem_flash_read_byte(calc, pa); + else + value = *(calc->mem + pa); + + if (pa < 0x1F0000 || pa >= 0x200000) + calc->hwregs[PROTECTSTATE] = 0; + else if (state == 6) + calc->hwregs[PROTECTSTATE] = 7; + else if (state < 6 && value == protectbytes[state]) + calc->hwregs[PROTECTSTATE] = state + 1; + else + calc->hwregs[PROTECTSTATE] = 0; + + return (value); +} + +byte xs_z80_rdmem(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa; + byte value; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + if (TILEM_UNLIKELY(page == 0x7E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x200000) + calc->z80.clock += calc->hwregs[FLASH_READ_DELAY]; + else + calc->z80.clock += calc->hwregs[RAM_READ_DELAY]; + + value = readbyte(calc, pa); + return (value); +} + +byte xs_z80_rdmem_m1(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa, m; + byte value; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + if (TILEM_UNLIKELY(page == 0x7E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x200000) { + calc->z80.clock += calc->hwregs[FLASH_EXEC_DELAY]; + + if (TILEM_UNLIKELY(page >= calc->hwregs[PORT22] + && page <= calc->hwregs[PORT23])) { + tilem_warning(calc, "Executing in restricted Flash area"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + } + else { + calc->z80.clock += calc->hwregs[RAM_EXEC_DELAY]; + + /* Note: this isn't quite strict enough; when port 21 + is set to 30h, the "ghost" RAM pages 88-8F are + treated as distinct pages for restriction purposes. + This detail probably isn't worth emulating. */ + m = pa & calc->hwregs[NO_EXEC_RAM_MASK]; + if (TILEM_UNLIKELY(m < calc->hwregs[NO_EXEC_RAM_LOWER] + || m > calc->hwregs[NO_EXEC_RAM_UPPER])) { + tilem_warning(calc, "Executing in restricted RAM area"); + tilem_z80_exception(calc, TILEM_EXC_RAM_EXEC); + } + } + + value = readbyte(calc, pa); + + if (TILEM_UNLIKELY(value == 0xff && A == 0x0038)) { + tilem_warning(calc, "No OS installed"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + + return (value); +} + +dword xs_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + return ((page << 14) | (A & 0x3fff)); +} + +dword xs_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + + if (!page) + return (A & 0x3fff); + + if (page == calc->mempagemap[1]) + return (0x4000 | (A & 0x3fff)); + + if ((A & 0x3fff) < 64 * calc->hwregs[PORT28]) { + if (page == 0x81) + return (0x8000 | (A & 0x3fff)); + } + else { + if (page == calc->mempagemap[2]) + return (0x8000 | (A & 0x3fff)); + } + + if ((A & 0x3fff) >= (0x4000 - 64 * calc->hwregs[PORT27])) { + if (page == 0x80) + return (0xC000 | (A & 0x3fff)); + } + else { + if (page == calc->mempagemap[3]) + return (0xC000 | (A & 0x3fff)); + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/xs/xs_subcore.c b/tool/tilem-src/emu/xs/xs_subcore.c new file mode 100644 index 0000000..121be93 --- /dev/null +++ b/tool/tilem-src/emu/xs/xs_subcore.c @@ -0,0 +1,73 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 + +#include "xs.h" + +static const TilemFlashSector flashsectors[] = { + {0x000000, 0x10000, 0}, {0x010000, 0x10000, 0}, + {0x020000, 0x10000, 0}, {0x030000, 0x10000, 0}, + {0x040000, 0x10000, 0}, {0x050000, 0x10000, 0}, + {0x060000, 0x10000, 0}, {0x070000, 0x10000, 0}, + {0x080000, 0x10000, 0}, {0x090000, 0x10000, 0}, + {0x0A0000, 0x10000, 0}, {0x0B0000, 0x10000, 0}, + {0x0C0000, 0x10000, 0}, {0x0D0000, 0x10000, 0}, + {0x0E0000, 0x10000, 0}, {0x0F0000, 0x10000, 0}, + {0x100000, 0x10000, 0}, {0x110000, 0x10000, 0}, + {0x120000, 0x10000, 0}, {0x130000, 0x10000, 0}, + {0x140000, 0x10000, 0}, {0x150000, 0x10000, 0}, + {0x160000, 0x10000, 0}, {0x170000, 0x10000, 0}, + {0x180000, 0x10000, 0}, {0x190000, 0x10000, 0}, + {0x1A0000, 0x10000, 0}, {0x1B0000, 0x10000, 0}, + {0x1C0000, 0x10000, 0}, {0x1D0000, 0x10000, 0}, + {0x1E0000, 0x10000, 0}, {0x1F0000, 0x08000, 0}, + {0x1F8000, 0x02000, 0}, {0x1FA000, 0x02000, 0}, + {0x1FC000, 0x04000, 2}}; + +#define NUM_FLASH_SECTORS (sizeof(flashsectors) / sizeof(TilemFlashSector)) + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +extern const char* xp_keynames[]; + +TilemHardware hardware_ti83pse = { + 's', "ti83pse", "TI-83 Plus Silver Edition", + (TILEM_CALC_HAS_LINK | TILEM_CALC_HAS_LINK_ASSIST + | TILEM_CALC_HAS_T6A04 | TILEM_CALC_HAS_FLASH + | TILEM_CALC_HAS_MD5_ASSIST), + 96, 64, 128 * 0x4000, 8 * 0x4000, 16 * 64, 0x80, + NUM_FLASH_SECTORS, flashsectors, 3, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + xp_keynames, + xs_reset, xs_stateloaded, + xs_z80_in, xs_z80_out, + xs_z80_wrmem, xs_z80_rdmem, xs_z80_rdmem_m1, NULL, + xs_z80_ptimer, tilem_lcd_t6a04_get_data, + xs_mem_ltop, xs_mem_ptol }; diff --git a/tool/tilem-src/emu/xz/xz.h b/tool/tilem-src/emu/xz/xz.h new file mode 100644 index 0000000..6f6d440 --- /dev/null +++ b/tool/tilem-src/emu/xz/xz.h @@ -0,0 +1,105 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2009-2011 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 + * . + */ + +#ifndef _TILEM_XZ_H +#define _TILEM_XZ_H + +enum { + PORT3, /* mask of enabled interrupts */ + PORT4, /* interrupt timer speed */ + PORT5, /* memory mapping bank C */ + PORT6, /* memory mapping bank A */ + PORT7, /* memory mapping bank B */ + PORT8, /* link assist mode flags */ + PORT9, /* unknown (link assist settings?) */ + PORTA, /* unknown (timeout value?) */ + PORTB, /* unknown (timeout value?) */ + PORTC, /* unknown (timeout value?) */ + PORTD, /* unknown */ + PORTE, /* unknown */ + PORTF, /* unknown */ + + PORT20, /* CPU speed control */ + PORT21, /* hardware type / RAM no-exec control */ + PORT22, /* Flash no-exec lower limit */ + PORT23, /* Flash no-exec upper limit */ + PORT25, /* RAM no-exec lower limit */ + PORT26, /* RAM no-exec upper limit */ + PORT27, /* bank C forced-page-0 limit */ + PORT28, /* bank B forced-page-1 limit */ + PORT29, /* LCD port delay (6 MHz) */ + PORT2A, /* LCD port delay (mode 1) */ + PORT2B, /* LCD port delay (mode 2) */ + PORT2C, /* LCD port delay (mode 3) */ + PORT2D, /* unknown */ + PORT2E, /* memory delay */ + PORT2F, /* Duration of LCD wait timer */ + + CLOCK_MODE, /* clock mode */ + CLOCK_INPUT, /* clock input register */ + CLOCK_DIFF, /* clock value minus actual time */ + + RAM_READ_DELAY, + RAM_WRITE_DELAY, + RAM_EXEC_DELAY, + FLASH_READ_DELAY, + FLASH_WRITE_DELAY, + FLASH_EXEC_DELAY, + LCD_PORT_DELAY, + NO_EXEC_RAM_MASK, + NO_EXEC_RAM_LOWER, + NO_EXEC_RAM_UPPER, + + LCD_WAIT, /* LCD wait timer active */ + PROTECTSTATE, /* port protection state */ + NUM_HW_REGS +}; + +#define HW_REG_NAMES \ + { "port3", "port4", "port5", "port6", "port7", "port8", "port9", \ + "portA", "portB", "portC", "portD", "portE", "portF", "port20", \ + "port21", "port22", "port23", "port25", "port26", "port27", \ + "port28", "port29", "port2A", "port2B", "port2C", "port2D", \ + "port2E", "port2F", "clock_mode", "clock_input", "clock_diff", \ + "ram_read_delay", "ram_write_delay", "ram_exec_delay", \ + "flash_read_delay", "flash_write_delay", "flash_exec_delay", \ + "lcd_port_delay", "no_exec_ram_mask", "no_exec_ram_lower", \ + "no_exec_ram_upper", "lcd_wait", "protectstate" } + +#define TIMER_INT1 (TILEM_NUM_SYS_TIMERS + 1) +#define TIMER_INT2A (TILEM_NUM_SYS_TIMERS + 2) +#define TIMER_INT2B (TILEM_NUM_SYS_TIMERS + 3) +#define TIMER_LCD_WAIT (TILEM_NUM_SYS_TIMERS + 4) +#define NUM_HW_TIMERS 4 + +#define HW_TIMER_NAMES { "int1", "int2a", "int2b", "lcd_wait" } + +void xz_reset(TilemCalc* calc); +void xz_stateloaded(TilemCalc* calc, int savtype); +byte xz_z80_in(TilemCalc* calc, dword port); +void xz_z80_out(TilemCalc* calc, dword port, byte value); +void xz_z80_ptimer(TilemCalc* calc, int id); +void xz_z80_wrmem(TilemCalc* calc, dword addr, byte value); +byte xz_z80_rdmem(TilemCalc* calc, dword addr); +byte xz_z80_rdmem_m1(TilemCalc* calc, dword addr); +dword xz_mem_ltop(TilemCalc* calc, dword addr); +dword xz_mem_ptol(TilemCalc* calc, dword addr); + +#endif diff --git a/tool/tilem-src/emu/xz/xz_init.c b/tool/tilem-src/emu/xz/xz_init.c new file mode 100644 index 0000000..eace5a7 --- /dev/null +++ b/tool/tilem-src/emu/xz/xz_init.c @@ -0,0 +1,89 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 + +#include "xz.h" + +void xz_reset(TilemCalc* calc) +{ + calc->hwregs[PORT3] = 0x0B; + calc->hwregs[PORT4] = 0x07; + calc->hwregs[PORT6] = 0x7F; + calc->hwregs[PORT7] = 0x7F; + + calc->mempagemap[0] = 0x00; + calc->mempagemap[1] = 0x7E; + calc->mempagemap[2] = 0x7F; + calc->mempagemap[3] = 0x7F; + + calc->z80.r.pc.d = 0x8000; + + calc->hwregs[PORT8] = 0x80; + + calc->hwregs[PORT20] = 0; + calc->hwregs[PORT21] = 1; + calc->hwregs[PORT22] = 0x08; + calc->hwregs[PORT23] = 0x69; + calc->hwregs[PORT25] = 0x10; + calc->hwregs[PORT26] = 0x20; + calc->hwregs[PORT27] = 0; + calc->hwregs[PORT28] = 0; + calc->hwregs[PORT29] = 0x14; + calc->hwregs[PORT2A] = 0x27; + calc->hwregs[PORT2B] = 0x2F; + calc->hwregs[PORT2C] = 0x3B; + calc->hwregs[PORT2D] = 0x01; + calc->hwregs[PORT2E] = 0x44; + calc->hwregs[PORT2F] = 0x4A; + + calc->hwregs[FLASH_READ_DELAY] = 0; + calc->hwregs[FLASH_WRITE_DELAY] = 0; + calc->hwregs[FLASH_EXEC_DELAY] = 0; + calc->hwregs[RAM_READ_DELAY] = 0; + calc->hwregs[RAM_WRITE_DELAY] = 0; + calc->hwregs[RAM_EXEC_DELAY] = 0; + calc->hwregs[LCD_PORT_DELAY] = 5; + calc->hwregs[NO_EXEC_RAM_MASK] = 0x7C00; + calc->hwregs[NO_EXEC_RAM_LOWER] = 0x4000; + calc->hwregs[NO_EXEC_RAM_UPPER] = 0x8000; + + calc->hwregs[PROTECTSTATE] = 0; + + calc->flash.overridegroup = 1; + + tilem_z80_set_speed(calc, 6000); + + tilem_z80_set_timer(calc, TIMER_INT1, 1600, 9277, 1); + tilem_z80_set_timer(calc, TIMER_INT2A, 1300, 9277, 1); + tilem_z80_set_timer(calc, TIMER_INT2B, 1000, 9277, 1); +} + +void xz_stateloaded(TilemCalc* calc, int savtype TILEM_ATTR_UNUSED) +{ + tilem_calc_fix_certificate(calc, calc->mem + (0x7E * 0x4000L), + 0x69, 0x0c, 0x1e50); +} diff --git a/tool/tilem-src/emu/xz/xz_io.c b/tool/tilem-src/emu/xz/xz_io.c new file mode 100644 index 0000000..557e878 --- /dev/null +++ b/tool/tilem-src/emu/xz/xz_io.c @@ -0,0 +1,747 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 +#include + +#include "xz.h" + +static void set_lcd_wait_timer(TilemCalc* calc) +{ + static const int delaytime[8] = { 48, 112, 176, 240, + 304, 368, 432, 496 }; + int i; + + switch (calc->hwregs[PORT20] & 3) { + case 0: + return; + case 1: + i = (calc->hwregs[PORT2F] & 3); + break; + case 2: + i = ((calc->hwregs[PORT2F] >> 2) & 7); + break; + default: + i = ((calc->hwregs[PORT2F] >> 5) & 7); + break; + } + + tilem_z80_set_timer(calc, TIMER_LCD_WAIT, delaytime[i], 0, 0); + calc->hwregs[LCD_WAIT] = 1; +} + +byte xz_z80_in(TilemCalc* calc, dword port) +{ + /* FIXME: measure actual levels */ + static const byte battlevel[4] = { 33, 39, 36, 43 }; + byte v; + unsigned int f; + time_t curtime; + + switch(port&0xff) { + case 0x00: + v = tilem_linkport_get_lines(calc); + v |= (calc->linkport.lines << 4); + return(v); + + case 0x01: + return(tilem_keypad_read_keys(calc)); + + case 0x02: + v = battlevel[calc->hwregs[PORT4] >> 6]; + return ((calc->battery >= v ? 0xe1 : 0xe0) + | (calc->hwregs[LCD_WAIT] ? 0 : 2) + | (calc->flash.unlock << 2)); + + case 0x03: + return(calc->hwregs[PORT3]); + + case 0x04: + v = (calc->keypad.onkeydown ? 0x00 : 0x08); + + if (calc->z80.interrupts & TILEM_INTERRUPT_ON_KEY) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER1) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_TIMER2) + v |= 0x04; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ACTIVE) + v |= 0x10; + + if (calc->usertimers[0].status & TILEM_USER_TIMER_FINISHED) + v |= 0x20; + if (calc->usertimers[1].status & TILEM_USER_TIMER_FINISHED) + v |= 0x40; + if (calc->usertimers[2].status & TILEM_USER_TIMER_FINISHED) + v |= 0x80; + + return(v); + + case 0x05: + return(calc->hwregs[PORT5] & 0x0f); + + case 0x06: + return(calc->hwregs[PORT6]); + + case 0x07: + return(calc->hwregs[PORT7]); + + case 0x08: + return(calc->hwregs[PORT8]); + + case 0x09: + f = tilem_linkport_get_assist_flags(calc); + + if (f & (TILEM_LINK_ASSIST_READ_BUSY + | TILEM_LINK_ASSIST_WRITE_BUSY)) + v = 0x00; + else + v = 0x20; + + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_READ) + v |= 0x01; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_IDLE) + v |= 0x02; + if (calc->z80.interrupts & TILEM_INTERRUPT_LINK_ERROR) + v |= 0x04; + if (f & TILEM_LINK_ASSIST_READ_BUSY) + v |= 0x08; + if (f & TILEM_LINK_ASSIST_READ_BYTE) + v |= 0x10; + if (f & (TILEM_LINK_ASSIST_READ_ERROR + | TILEM_LINK_ASSIST_WRITE_ERROR)) + v |= 0x40; + if (f & TILEM_LINK_ASSIST_WRITE_BUSY) + v |= 0x80; + + calc->z80.interrupts &= ~TILEM_INTERRUPT_LINK_ERROR; + + return(v); + + case 0x0A: + v = calc->linkport.assistlastbyte; + tilem_linkport_read_byte(calc); + return(v); + + case 0x0E: + return(calc->hwregs[PORTE] & 3); + + case 0x0F: + return(calc->hwregs[PORTF] & 3); + + case 0x10: + case 0x12: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + return(tilem_lcd_t6a04_status(calc)); + + case 0x11: + case 0x13: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + return(tilem_lcd_t6a04_read(calc)); + + case 0x15: + return(0x45); /* ??? */ + + case 0x1C: + return(tilem_md5_assist_get_value(calc)); + + case 0x1D: + return(tilem_md5_assist_get_value(calc) >> 8); + + case 0x1E: + return(tilem_md5_assist_get_value(calc) >> 16); + + case 0x1F: + return(tilem_md5_assist_get_value(calc) >> 24); + + case 0x20: + return(calc->hwregs[PORT20] & 3); + + case 0x21: + return(calc->hwregs[PORT21] & 0x33); + + case 0x22: + return(calc->hwregs[PORT22]); + + case 0x23: + return(calc->hwregs[PORT23]); + + case 0x25: + return(calc->hwregs[PORT25]); + + case 0x26: + return(calc->hwregs[PORT26]); + + case 0x27: + return(calc->hwregs[PORT27]); + + case 0x28: + return(calc->hwregs[PORT28]); + + case 0x29: + return(calc->hwregs[PORT29]); + + case 0x2A: + return(calc->hwregs[PORT2A]); + + case 0x2B: + return(calc->hwregs[PORT2B]); + + case 0x2C: + return(calc->hwregs[PORT2C]); + + case 0x2D: + return(calc->hwregs[PORT2D] & 3); + + case 0x2E: + return(calc->hwregs[PORT2E]); + + case 0x2F: + return(calc->hwregs[PORT2F]); + + case 0x30: + return(calc->usertimers[0].frequency); + case 0x31: + return(calc->usertimers[0].status); + case 0x32: + return(tilem_user_timer_get_value(calc, 0)); + + case 0x33: + return(calc->usertimers[1].frequency); + case 0x34: + return(calc->usertimers[1].status); + case 0x35: + return(tilem_user_timer_get_value(calc, 1)); + + case 0x36: + return(calc->usertimers[2].frequency); + case 0x37: + return(calc->usertimers[2].status); + case 0x38: + return(tilem_user_timer_get_value(calc, 2)); + + case 0x39: + return(0xf0); /* ??? */ + + case 0x40: + return calc->hwregs[CLOCK_MODE]; + + case 0x41: + return calc->hwregs[CLOCK_INPUT]&0xff; + + case 0x42: + return (calc->hwregs[CLOCK_INPUT]>>8)&0xff; + + case 0x43: + return (calc->hwregs[CLOCK_INPUT]>>16)&0xff; + + case 0x44: + return (calc->hwregs[CLOCK_INPUT]>>24)&0xff; + + case 0x45: + case 0x46: + case 0x47: + case 0x48: + if (calc->hwregs[CLOCK_MODE] & 1) { + time(&curtime); + } + else { + curtime = 0; + } + curtime += calc->hwregs[CLOCK_DIFF]; + return (curtime >> ((port - 0x45) * 8)); + + case 0x4C: + return(0x22); + + case 0x4D: + /* USB port - not emulated, calculator should + recognize that the USB cable is + disconnected. + + Thanks go to Dan Englender for these + values. */ + + return(0xA5); + + case 0x55: + return(0x1F); + + case 0x56: + return(0x00); + + case 0x57: + return(0x50); + + case 0x0B: + case 0x0C: + case 0x0D: + case 0x14: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + return(0); + } + + tilem_warning(calc, "Input from port %x", port); + return(0x00); +} + + +static void setup_mapping(TilemCalc* calc) +{ + unsigned int pageA, pageB, pageC; + + if (calc->hwregs[PORT6] & 0x80) + pageA = (0x80 | (calc->hwregs[PORT6] & 7)); + else + pageA = (calc->hwregs[PORT6] & 0x7f); + + if (calc->hwregs[PORT7] & 0x80) + pageB = (0x80 | (calc->hwregs[PORT7] & 7)); + else + pageB = (calc->hwregs[PORT7] & 0x7f); + + pageC = (0x80 | (calc->hwregs[PORT5] & 7)); + + if (calc->hwregs[PORT4] & 1) { + calc->mempagemap[1] = (pageA & ~1); + calc->mempagemap[2] = (pageA | 1); + calc->mempagemap[3] = pageB; + } + else { + calc->mempagemap[1] = pageA; + calc->mempagemap[2] = pageB; + calc->mempagemap[3] = pageC; + } +} + +static void setup_clockdelays(TilemCalc* calc) +{ + byte lcdport = calc->hwregs[PORT29 + (calc->hwregs[PORT20] & 3)]; + byte memport = calc->hwregs[PORT2E]; + + if (!(lcdport & 1)) + memport &= ~0x07; + if (!(lcdport & 2)) + memport &= ~0x70; + + calc->hwregs[FLASH_EXEC_DELAY] = (memport & 1); + calc->hwregs[FLASH_READ_DELAY] = ((memport >> 1) & 1); + calc->hwregs[FLASH_WRITE_DELAY] = ((memport >> 2) & 1); + + calc->hwregs[RAM_EXEC_DELAY] = ((memport >> 4) & 1); + calc->hwregs[RAM_READ_DELAY] = ((memport >> 5) & 1); + calc->hwregs[RAM_WRITE_DELAY] = ((memport >> 6) & 1); + + calc->hwregs[LCD_PORT_DELAY] = (lcdport >> 2); +} + +void xz_z80_out(TilemCalc* calc, dword port, byte value) +{ + static const int tmrvalues[4] = { 1953, 4395, 6836, 9277 }; + int t, r; + unsigned int mode; + time_t curtime; + + switch(port&0xff) { + case 0x00: + tilem_linkport_set_lines(calc, value); + break; + + case 0x01: + tilem_keypad_set_group(calc, value); + break; + + case 0x03: + if (value & 0x01) { + calc->keypad.onkeyint = 1; + } + else { + calc->z80.interrupts &= ~TILEM_INTERRUPT_ON_KEY; + calc->keypad.onkeyint = 0; + } + + if (!(value & 0x02)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER1; + + if (!(value & 0x04)) + calc->z80.interrupts &= ~TILEM_INTERRUPT_TIMER2; + + if (value & 0x06) { + calc->usertimers[0].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[1].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[2].status &= ~TILEM_USER_TIMER_NO_HALT_INT; + } + else { + calc->usertimers[0].status |= TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[1].status |= TILEM_USER_TIMER_NO_HALT_INT; + calc->usertimers[2].status |= TILEM_USER_TIMER_NO_HALT_INT; + } + + mode = calc->linkport.mode; + if (value & 0x10) + mode |= TILEM_LINK_MODE_INT_ON_ACTIVE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ACTIVE; + + tilem_linkport_set_mode(calc, mode); + + calc->poweronhalt = ((value & 8) >> 3); + calc->hwregs[PORT3] = value; + break; + + + case 0x04: + calc->hwregs[PORT4] = value; + + t = tmrvalues[(value & 6) >> 1]; + tilem_z80_set_timer_period(calc, TIMER_INT1, t); + tilem_z80_set_timer_period(calc, TIMER_INT2A, t); + tilem_z80_set_timer_period(calc, TIMER_INT2B, t); + + setup_mapping(calc); + break; + + case 0x05: + calc->hwregs[PORT5] = value & 0x0f; + setup_mapping(calc); + break; + + case 0x06: + calc->hwregs[PORT6] = value; + setup_mapping(calc); + break; + + case 0x07: + calc->hwregs[PORT7] = value; + setup_mapping(calc); + break; + + case 0x08: + calc->hwregs[PORT8] = value; + + mode = calc->linkport.mode; + + if (value & 0x01) + mode |= TILEM_LINK_MODE_INT_ON_READ; + else + mode &= ~TILEM_LINK_MODE_INT_ON_READ; + + if (value & 0x02) + mode |= TILEM_LINK_MODE_INT_ON_IDLE; + else + mode &= ~TILEM_LINK_MODE_INT_ON_IDLE; + + if (value & 0x04) + mode |= TILEM_LINK_MODE_INT_ON_ERROR; + else + mode &= ~TILEM_LINK_MODE_INT_ON_ERROR; + + if (value & 0x80) + mode &= ~TILEM_LINK_MODE_ASSIST; + else + mode |= TILEM_LINK_MODE_ASSIST; + + tilem_linkport_set_mode(calc, mode); + break; + + case 0x09: + calc->hwregs[PORT9] = value; + break; + + case 0x0A: + calc->hwregs[PORTA] = value; + break; + + case 0x0B: + calc->hwregs[PORTB] = value; + break; + + case 0x0C: + calc->hwregs[PORTC] = value; + break; + + + case 0x0D: + if (!(calc->hwregs[PORT8] & 0x80)) + tilem_linkport_write_byte(calc, value); + break; + + case 0x0E: + calc->hwregs[PORTE] = value; + break; + + case 0x0F: + calc->hwregs[PORTF] = value; + break; + + case 0x10: + case 0x12: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + tilem_lcd_t6a04_control(calc, value); + break; + + case 0x11: + case 0x13: + calc->z80.clock += calc->hwregs[LCD_PORT_DELAY]; + set_lcd_wait_timer(calc); + tilem_lcd_t6a04_write(calc, value); + break; + + case 0x14: + if (calc->hwregs[PROTECTSTATE] == 7) { + if (value & 1) + tilem_message(calc, "Flash unlocked"); + else + tilem_message(calc, "Flash locked"); + calc->flash.unlock = value&1; + } + else { + tilem_warning(calc, "Writing to protected port 14"); + } + break; + + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + r = (port & 0xff) - 0x18; + calc->md5assist.regs[r] >>= 8; + calc->md5assist.regs[r] |= (value << 24); + break; + + case 0x1E: + calc->md5assist.shift = value & 0x1f; + break; + + case 0x1F: + calc->md5assist.mode = value & 3; + break; + + case 0x20: + calc->hwregs[PORT20] = value; + + if (value & 3) { + tilem_z80_set_speed(calc, 15000); + } + else { + tilem_z80_set_speed(calc, 6000); + } + + setup_clockdelays(calc); + break; + + case 0x21: + if (calc->flash.unlock) { + calc->hwregs[PORT21] = value; + t = (value >> 4) & 3; + calc->hwregs[NO_EXEC_RAM_MASK] = (0x8000 << t) - 0x400; + calc->flash.overridegroup = value & 3; + } + else { + tilem_warning(calc, "Writing to protected port 21"); + } + break; + + case 0x22: + if (calc->flash.unlock) { + calc->hwregs[PORT22] = value; + } + else { + tilem_warning(calc, "Writing to protected port 22"); + } + break; + + case 0x23: + if (calc->flash.unlock) { + calc->hwregs[PORT23] = value; + } + else { + tilem_warning(calc, "Writing to protected port 23"); + } + break; + + case 0x25: + if (calc->flash.unlock) { + calc->hwregs[PORT25] = value; + calc->hwregs[NO_EXEC_RAM_LOWER] = value * 0x400; + } + else { + tilem_warning(calc, "Writing to protected port 25"); + } + break; + + case 0x26: + if (calc->flash.unlock) { + calc->hwregs[PORT26] = value; + calc->hwregs[NO_EXEC_RAM_UPPER] = value * 0x400; + } + else { + tilem_warning(calc, "Writing to protected port 26"); + } + break; + + case 0x27: + calc->hwregs[PORT27] = value; + break; + + case 0x28: + calc->hwregs[PORT28] = value; + break; + + case 0x29: + calc->hwregs[PORT29] = value; + setup_clockdelays(calc); + break; + + case 0x2A: + calc->hwregs[PORT2A] = value; + setup_clockdelays(calc); + break; + + case 0x2B: + calc->hwregs[PORT2B] = value; + setup_clockdelays(calc); + break; + + case 0x2C: + calc->hwregs[PORT2C] = value; + setup_clockdelays(calc); + break; + + case 0x2D: + calc->hwregs[PORT2D] = value; + break; + + case 0x2E: + calc->hwregs[PORT2E] = value; + setup_clockdelays(calc); + break; + + case 0x2F: + calc->hwregs[PORT2F] = value; + break; + + case 0x30: + tilem_user_timer_set_frequency(calc, 0, value); + break; + case 0x31: + tilem_user_timer_set_mode(calc, 0, value); + break; + case 0x32: + tilem_user_timer_start(calc, 0, value); + break; + + case 0x33: + tilem_user_timer_set_frequency(calc, 1, value); + break; + case 0x34: + tilem_user_timer_set_mode(calc, 1, value); + break; + case 0x35: + tilem_user_timer_start(calc, 1, value); + break; + + case 0x36: + tilem_user_timer_set_frequency(calc, 2, value); + break; + case 0x37: + tilem_user_timer_set_mode(calc, 2, value); + break; + case 0x38: + tilem_user_timer_start(calc, 2, value); + break; + + case 0x40: + time(&curtime); + + if ((calc->hwregs[CLOCK_MODE] & 1) != (value & 1)) { + if (value & 1) + calc->hwregs[CLOCK_DIFF] -= curtime; + else + calc->hwregs[CLOCK_DIFF] += curtime; + } + + if (!(calc->hwregs[CLOCK_MODE] & 2) && (value & 2)) { + calc->hwregs[CLOCK_DIFF] = calc->hwregs[CLOCK_INPUT]; + if (value & 1) + calc->hwregs[CLOCK_DIFF] -= curtime; + } + calc->hwregs[CLOCK_MODE] = value & 3; + break; + + case 0x41: + calc->hwregs[CLOCK_INPUT] &= 0xffffff00; + calc->hwregs[CLOCK_INPUT] |= value; + break; + + case 0x42: + calc->hwregs[CLOCK_INPUT] &= 0xffff00ff; + calc->hwregs[CLOCK_INPUT] |= (value << 8); + break; + + case 0x43: + calc->hwregs[CLOCK_INPUT] &= 0xff00ffff; + calc->hwregs[CLOCK_INPUT] |= (value << 16); + break; + + case 0x44: + calc->hwregs[CLOCK_INPUT] &= 0x00ffffff; + calc->hwregs[CLOCK_INPUT] |= (value << 24); + break; + } + + return; +} + +void xz_z80_ptimer(TilemCalc* calc, int id) +{ + switch (id) { + case TIMER_INT1: + if (calc->hwregs[PORT3] & 0x02) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER1; + break; + + case TIMER_INT2A: + case TIMER_INT2B: + if (calc->hwregs[PORT3] & 0x04) + calc->z80.interrupts |= TILEM_INTERRUPT_TIMER2; + break; + + case TIMER_LCD_WAIT: + calc->hwregs[LCD_WAIT] = 0; + break; + } +} diff --git a/tool/tilem-src/emu/xz/xz_memory.c b/tool/tilem-src/emu/xz/xz_memory.c new file mode 100644 index 0000000..a8f083c --- /dev/null +++ b/tool/tilem-src/emu/xz/xz_memory.c @@ -0,0 +1,207 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2011 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 + +#include "xz.h" + +void xz_z80_wrmem(TilemCalc* calc, dword A, byte v) +{ + unsigned long pa; + byte page; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x200000) { + calc->z80.clock += calc->hwregs[FLASH_WRITE_DELAY]; + tilem_flash_write_byte(calc, pa, v); + } + else if (pa < 0x220000) { + calc->z80.clock += calc->hwregs[RAM_WRITE_DELAY]; + *(calc->mem+pa) = v; + } +} + +static inline byte readbyte(TilemCalc* calc, dword pa) +{ + static const byte protectbytes[6] = {0x00,0x00,0xed,0x56,0xf3,0xd3}; + int state = calc->hwregs[PROTECTSTATE]; + byte value; + + if (pa < 0x200000 && (calc->flash.state || calc->flash.busy)) + value = tilem_flash_read_byte(calc, pa); + else + value = *(calc->mem + pa); + + if (pa < 0x1B0000 || pa >= 0x200000 + || (pa >= 0x1C0000 && pa < 0x1F0000)) + calc->hwregs[PROTECTSTATE] = 0; + else if (state == 6) + calc->hwregs[PROTECTSTATE] = 7; + else if (state < 6 && value == protectbytes[state]) + calc->hwregs[PROTECTSTATE] = state + 1; + else + calc->hwregs[PROTECTSTATE] = 0; + + return (value); +} + +byte xz_z80_rdmem(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa; + byte value; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + if (TILEM_UNLIKELY(page == 0x7E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x200000) + calc->z80.clock += calc->hwregs[FLASH_READ_DELAY]; + else + calc->z80.clock += calc->hwregs[RAM_READ_DELAY]; + + value = readbyte(calc, pa); + return (value); +} + +byte xz_z80_rdmem_m1(TilemCalc* calc, dword A) +{ + byte page; + unsigned long pa, m; + byte value; + + page = calc->mempagemap[A>>14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + if (TILEM_UNLIKELY(page == 0x7E && !calc->flash.unlock)) { + tilem_warning(calc, "Reading from read-protected sector"); + return (0xff); + } + + pa = (A & 0x3FFF) + 0x4000L*page; + + if (pa < 0x200000) { + calc->z80.clock += calc->hwregs[FLASH_EXEC_DELAY]; + + if (TILEM_UNLIKELY(page >= calc->hwregs[PORT22] + && page <= calc->hwregs[PORT23])) { + tilem_warning(calc, "Executing in restricted Flash area"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + } + else { + calc->z80.clock += calc->hwregs[RAM_EXEC_DELAY]; + + m = pa & calc->hwregs[NO_EXEC_RAM_MASK]; + if (TILEM_UNLIKELY(m < calc->hwregs[NO_EXEC_RAM_LOWER] + || m > calc->hwregs[NO_EXEC_RAM_UPPER])) { + tilem_warning(calc, "Executing in restricted RAM area"); + tilem_z80_exception(calc, TILEM_EXC_RAM_EXEC); + } + } + + value = readbyte(calc, pa); + + if (TILEM_UNLIKELY(value == 0xff && A == 0x0038)) { + tilem_warning(calc, "No OS installed"); + tilem_z80_exception(calc, TILEM_EXC_FLASH_EXEC); + } + + return (value); +} + +dword xz_mem_ltop(TilemCalc* calc, dword A) +{ + byte page = calc->mempagemap[A >> 14]; + + if (A & 0x8000) { + if (A > (0xFFFF - 64 * calc->hwregs[PORT27])) + page = 0x80; + else if (A < (0x8000 + 64 * calc->hwregs[PORT28])) + page = 0x81; + } + + return ((page << 14) | (A & 0x3fff)); +} + +dword xz_mem_ptol(TilemCalc* calc, dword A) +{ + byte page = A >> 14; + + if (!page) + return (A & 0x3fff); + + if (page == calc->mempagemap[1]) + return (0x4000 | (A & 0x3fff)); + + if ((A & 0x3fff) < 64 * calc->hwregs[PORT28]) { + if (page == 0x81) + return (0x8000 | (A & 0x3fff)); + } + else { + if (page == calc->mempagemap[2]) + return (0x8000 | (A & 0x3fff)); + } + + if ((A & 0x3fff) >= (0x4000 - 64 * calc->hwregs[PORT27])) { + if (page == 0x80) + return (0xC000 | (A & 0x3fff)); + } + else { + if (page == calc->mempagemap[3]) + return (0xC000 | (A & 0x3fff)); + } + + return (0xffffffff); +} diff --git a/tool/tilem-src/emu/xz/xz_subcore.c b/tool/tilem-src/emu/xz/xz_subcore.c new file mode 100644 index 0000000..3692447 --- /dev/null +++ b/tool/tilem-src/emu/xz/xz_subcore.c @@ -0,0 +1,73 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2001 Solignac Julien + * Copyright (C) 2004-2012 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 + +#include "xz.h" + +static const TilemFlashSector flashsectors[] = { + {0x000000, 0x10000, 0}, {0x010000, 0x10000, 0}, + {0x020000, 0x10000, 0}, {0x030000, 0x10000, 0}, + {0x040000, 0x10000, 0}, {0x050000, 0x10000, 0}, + {0x060000, 0x10000, 0}, {0x070000, 0x10000, 0}, + {0x080000, 0x10000, 0}, {0x090000, 0x10000, 0}, + {0x0A0000, 0x10000, 0}, {0x0B0000, 0x10000, 0}, + {0x0C0000, 0x10000, 0}, {0x0D0000, 0x10000, 0}, + {0x0E0000, 0x10000, 0}, {0x0F0000, 0x10000, 0}, + {0x100000, 0x10000, 0}, {0x110000, 0x10000, 0}, + {0x120000, 0x10000, 0}, {0x130000, 0x10000, 0}, + {0x140000, 0x10000, 0}, {0x150000, 0x10000, 0}, + {0x160000, 0x10000, 0}, {0x170000, 0x10000, 0}, + {0x180000, 0x10000, 0}, {0x190000, 0x10000, 0}, + {0x1A0000, 0x10000, 0}, {0x1B0000, 0x10000, 2}, + {0x1C0000, 0x10000, 0}, {0x1D0000, 0x10000, 0}, + {0x1E0000, 0x10000, 0}, {0x1F0000, 0x08000, 0}, + {0x1F8000, 0x02000, 0}, {0x1FA000, 0x02000, 0}, + {0x1FC000, 0x04000, 2}}; + +#define NUM_FLASH_SECTORS (sizeof(flashsectors) / sizeof(TilemFlashSector)) + +static const char* hwregnames[NUM_HW_REGS] = HW_REG_NAMES; + +static const char* hwtimernames[NUM_HW_TIMERS] = HW_TIMER_NAMES; + +extern const char* xp_keynames[]; + +TilemHardware hardware_ti84pse = { + 'z', "ti84pse", "TI-84 Plus Silver Edition", + (TILEM_CALC_HAS_LINK | TILEM_CALC_HAS_LINK_ASSIST + | TILEM_CALC_HAS_T6A04 | TILEM_CALC_HAS_FLASH + | TILEM_CALC_HAS_MD5_ASSIST), + 96, 64, 128 * 0x4000, 8 * 0x4000, 16 * 64, 0x80, + NUM_FLASH_SECTORS, flashsectors, 3, + NUM_HW_REGS, hwregnames, + NUM_HW_TIMERS, hwtimernames, + xp_keynames, + xz_reset, xz_stateloaded, + xz_z80_in, xz_z80_out, + xz_z80_wrmem, xz_z80_rdmem, xz_z80_rdmem_m1, NULL, + xz_z80_ptimer, tilem_lcd_t6a04_get_data, + xz_mem_ltop, xz_mem_ptol }; diff --git a/tool/tilem-src/emu/z80.c b/tool/tilem-src/emu/z80.c new file mode 100644 index 0000000..51399ba --- /dev/null +++ b/tool/tilem-src/emu/z80.c @@ -0,0 +1,990 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2009-2011 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 +#include "tilem.h" +#include "z80.h" + +/* Timer manipulation */ + +/* +static void dumptimers(TilemZ80* z80) +{ + int tmr; + int t; + + printf("*** RT:"); + for (tmr = z80->timer_rt; tmr; tmr = z80->timers[tmr].next) { + t = z80->timers[tmr].count - z80->clock; + printf(" %d:%d", tmr, t); + } + printf("\n*** CPU:"); + for (tmr = z80->timer_cpu; tmr; tmr = z80->timers[tmr].next) { + t = z80->timers[tmr].count - z80->clock; + printf(" %d:%d", tmr, t); + } + printf("\n*** Free:"); + for (tmr = z80->timer_free; tmr; tmr = z80->timers[tmr].next) { + printf(" %d", tmr); + } + printf("\n"); +} +*/ + +static inline void timer_free(TilemZ80* z80, int tmr) +{ + z80->timers[tmr].callback = NULL; + z80->timers[tmr].callbackdata = NULL; + z80->timers[tmr].next = z80->timer_free; + z80->timers[tmr].prev = 0; + z80->timer_free = tmr; +} + +static inline int timer_alloc(TilemZ80* z80) +{ + int tmr, i; + + if (z80->timer_free) { + tmr = z80->timer_free; + z80->timer_free = z80->timers[tmr].next; + z80->timers[tmr].next = 0; + return tmr; + } + + i = z80->ntimers; + z80->ntimers = i * 2 + 1; + z80->timers = tilem_renew(TilemZ80Timer, z80->timers, z80->ntimers); + while (i < z80->ntimers) { + timer_free(z80, i); + i++; + } + + tmr = z80->timer_free; + z80->timer_free = z80->timers[tmr].next; + z80->timers[tmr].next = 0; + return tmr; +} + +static inline int timer_earlier(TilemZ80* z80, int tmr1, int tmr2) +{ + dword count1, count2; + + count1 = z80->timers[tmr1].count + 10000 - z80->clock; + count2 = z80->timers[tmr2].count + 10000 - z80->clock; + + return (count1 < count2); +} + +static inline void timer_insert(TilemZ80* z80, int* list, int tmr) +{ + int prev, next; + + if (!*list || timer_earlier(z80, tmr, *list)) { + z80->timers[tmr].prev = 0; + z80->timers[tmr].next = *list; + z80->timers[*list].prev = tmr; + *list = tmr; + return; + } + + prev = *list; + next = z80->timers[prev].next; + + while (next && timer_earlier(z80, next, tmr)) { + prev = next; + next = z80->timers[prev].next; + } + + z80->timers[prev].next = tmr; + z80->timers[next].prev = tmr; + z80->timers[tmr].prev = prev; + z80->timers[tmr].next = next; +} + +static inline void timer_set(TilemZ80* z80, int tmr, dword count, + dword period, int rt, dword extra) +{ + dword clocks; + qword kclocks; + + if (!count) { + /* leave timer disabled */ + z80->timers[tmr].prev = 0; + z80->timers[tmr].next = 0; + } + else if (rt) { + kclocks = z80->clockspeed; + kclocks *= count; + clocks = (kclocks + 500) / 1000 - extra; + z80->timers[tmr].count = z80->clock + clocks; + z80->timers[tmr].period = period; + timer_insert(z80, &z80->timer_rt, tmr); + } + else { + clocks = count - extra; + z80->timers[tmr].count = z80->clock + clocks; + z80->timers[tmr].period = period; + timer_insert(z80, &z80->timer_cpu, tmr); + } +} + +static inline void timer_unset(TilemZ80* z80, int tmr) +{ + int prev, next; + + if (tmr == z80->timer_cpu) + z80->timer_cpu = z80->timers[tmr].next; + if (tmr == z80->timer_rt) + z80->timer_rt = z80->timers[tmr].next; + + prev = z80->timers[tmr].prev; + next = z80->timers[tmr].next; + z80->timers[prev].next = next; + z80->timers[next].prev = prev; + z80->timers[tmr].prev = 0; + z80->timers[tmr].next = 0; +} + + +/* Breakpoint manipulation */ + +static inline void bp_free(TilemZ80* z80, int bp) +{ + z80->breakpoints[bp].type = 0; + z80->breakpoints[bp].testfunc = NULL; + z80->breakpoints[bp].testdata = NULL; + z80->breakpoints[bp].next = z80->breakpoint_free; + z80->breakpoints[bp].prev = 0; + z80->breakpoint_free = bp; +} + +static inline int bp_alloc(TilemZ80* z80) +{ + int bp, i; + + if (z80->breakpoint_free) { + bp = z80->breakpoint_free; + z80->breakpoint_free = z80->breakpoints[bp].next; + return bp; + } + + i = z80->nbreakpoints; + z80->nbreakpoints = i * 2 + 2; + z80->breakpoints = tilem_renew(TilemZ80Breakpoint, z80->breakpoints, + z80->nbreakpoints); + while (i < z80->nbreakpoints) { + bp_free(z80, i); + i++; + } + + bp = z80->breakpoint_free; + z80->breakpoint_free = z80->breakpoints[bp].next; + return bp; +} + +static int* bp_head(TilemCalc *calc, int type) +{ + if (type & TILEM_BREAK_DISABLED) + return &calc->z80.breakpoint_disabled; + + switch (type & TILEM_BREAK_TYPE_MASK) { + case TILEM_BREAK_MEM_READ: + return (type & TILEM_BREAK_PHYSICAL + ? &calc->z80.breakpoint_mpr + : &calc->z80.breakpoint_mr); + + case TILEM_BREAK_MEM_EXEC: + return (type & TILEM_BREAK_PHYSICAL + ? &calc->z80.breakpoint_mpx + : &calc->z80.breakpoint_mx); + + case TILEM_BREAK_MEM_WRITE: + return (type & TILEM_BREAK_PHYSICAL + ? &calc->z80.breakpoint_mpw + : &calc->z80.breakpoint_mw); + + case TILEM_BREAK_PORT_READ: + return &calc->z80.breakpoint_pr; + + case TILEM_BREAK_PORT_WRITE: + return &calc->z80.breakpoint_pw; + + case TILEM_BREAK_EXECUTE: + return &calc->z80.breakpoint_op; + + default: + tilem_internal(calc, "invalid bp type"); + return 0; + } +} + +static int bp_add(TilemCalc *calc, int bp, int type) +{ + int *head = bp_head(calc, type); + + if (!head) { + bp_free(&calc->z80, bp); + return 0; + } + + calc->z80.breakpoints[bp].next = *head; + calc->z80.breakpoints[*head].prev = *head ? bp : 0; + *head = bp; + + return bp; +} + +static void bp_rem(TilemCalc *calc, int bp, int type) +{ + int prev, next; + int *head = bp_head(calc, type); + + prev = calc->z80.breakpoints[bp].prev; + next = calc->z80.breakpoints[bp].next; + + if (bp == *head) + *head = next; + + calc->z80.breakpoints[prev].next = prev ? next : 0; + calc->z80.breakpoints[next].prev = next ? prev : 0; +} + +static void invoke_ptimer(TilemCalc* calc, void* data) +{ + (*calc->hw.z80_ptimer)(calc, TILEM_PTR_TO_DWORD(data)); +} + +/* Z80 API */ + +void tilem_z80_reset(TilemCalc* calc) +{ + int i; + + AF = BC = DE = HL = AF2 = BC2 = DE2 = HL2 = 0xffff; + IX = IY = IR = SP = WZ = WZ2 = 0xffff; + PC = 0; + Rh = 0x80; + IFF1 = IFF2 = IM = 0; + calc->z80.interrupts = 0; + calc->z80.halted = 0; + + /* Set up hardware timers */ + if (!calc->z80.ntimers) { + calc->z80.ntimers = (calc->hw.nhwtimers + + TILEM_NUM_SYS_TIMERS + 1); + tilem_free(calc->z80.timers); + calc->z80.timers = tilem_new(TilemZ80Timer, calc->z80.ntimers); + + for (i = 1; i < calc->z80.ntimers; i++) { + calc->z80.timers[i].next = 0; + calc->z80.timers[i].prev = 0; + calc->z80.timers[i].count = 0; + calc->z80.timers[i].period = 0; + calc->z80.timers[i].callback = &invoke_ptimer; + calc->z80.timers[i].callbackdata = TILEM_DWORD_TO_PTR(i); + } + + calc->z80.timers[TILEM_TIMER_LCD_DELAY].callback + = &tilem_lcd_delay_timer; + calc->z80.timers[TILEM_TIMER_FLASH_DELAY].callback + = tilem_flash_delay_timer; + calc->z80.timers[TILEM_TIMER_LINK_ASSIST].callback + = tilem_linkport_assist_timer; + + for (i = 0; i < TILEM_MAX_USER_TIMERS; i++) { + calc->z80.timers[TILEM_TIMER_USER1 + i].callback + = tilem_user_timer_expired; + calc->z80.timers[TILEM_TIMER_USER1 + i].callbackdata + = TILEM_DWORD_TO_PTR(i); + } + } +} + +void tilem_z80_stop(TilemCalc* calc, dword reason) +{ + if (!(reason & calc->z80.stop_mask)) { + calc->z80.stop_reason |= reason; + calc->z80.stopping = 1; + } +} + +void tilem_z80_exception(TilemCalc* calc, unsigned type) +{ + calc->z80.exception |= type; +} + +void tilem_z80_set_speed(TilemCalc* calc, int speed) +{ + int tmr; + qword t; + int oldspeed = calc->z80.clockspeed; + + if (oldspeed == speed) + return; + + for (tmr = calc->z80.timer_rt; tmr; tmr = calc->z80.timers[tmr].next) { + if ((calc->z80.clock - calc->z80.timers[tmr].count) < 10000) + continue; + + t = calc->z80.timers[tmr].count - calc->z80.clock; + t = (t * speed + oldspeed / 2) / oldspeed; + calc->z80.timers[tmr].count = calc->z80.clock + t; + } + + calc->z80.clockspeed = speed; +} + +int tilem_z80_add_timer(TilemCalc* calc, dword count, dword period, + int rt, TilemZ80TimerFunc func, void* data) +{ + int id; + + id = timer_alloc(&calc->z80); + calc->z80.timers[id].callback = func; + calc->z80.timers[id].callbackdata = data; + timer_set(&calc->z80, id, count, period, rt, 0); + return id; +} + +void tilem_z80_set_timer(TilemCalc* calc, int id, dword count, + dword period, int rt) +{ + if (id < 1 || id > calc->z80.ntimers + || !calc->z80.timers[id].callback) { + tilem_internal(calc, "setting invalid timer %d", id); + return; + } + timer_unset(&calc->z80, id); + timer_set(&calc->z80, id, count, period, rt, 0); +} + +void tilem_z80_set_timer_period(TilemCalc* calc, int id, dword period) +{ + if (id < 1 || id > calc->z80.ntimers + || !calc->z80.timers[id].callback) { + tilem_internal(calc, "setting invalid timer %d", id); + return; + } + + calc->z80.timers[id].period = period; +} + +void tilem_z80_remove_timer(TilemCalc* calc, int id) +{ + if (id <= calc->hw.nhwtimers + TILEM_NUM_SYS_TIMERS + || id > calc->z80.ntimers || !calc->z80.timers[id].callback) { + tilem_internal(calc, "removing invalid timer %d", id); + return; + } + timer_unset(&calc->z80, id); + timer_free(&calc->z80, id); +} + +int tilem_z80_timer_running(TilemCalc* calc, int id) +{ + if (id < 1 || id > calc->z80.ntimers + || !calc->z80.timers[id].callback) { + tilem_internal(calc, "querying invalid timer %d", id); + return 0; + } + + if (id == calc->z80.timer_rt || id == calc->z80.timer_cpu) + return 1; + if (calc->z80.timers[id].prev) + return 1; + return 0; +} + +int tilem_z80_get_timer_clocks(TilemCalc* calc, int id) +{ + if (id < 1 || id > calc->z80.ntimers + || !calc->z80.timers[id].callback) { + tilem_internal(calc, "querying invalid timer %d", id); + return 0; + } + return (calc->z80.timers[id].count - calc->z80.clock); +} + +int tilem_z80_get_timer_microseconds(TilemCalc* calc, int id) +{ + int n = tilem_z80_get_timer_clocks(calc, id); + + if (n < 0) { + n = ((((qword) -n * 1000) + (calc->z80.clockspeed / 2)) + / calc->z80.clockspeed); + return -n; + } + else { + n = ((((qword) n * 1000) + (calc->z80.clockspeed / 2)) + / calc->z80.clockspeed); + return n; + } +} + +int tilem_z80_add_breakpoint(TilemCalc* calc, int type, + dword start, dword end, dword mask, + TilemZ80BreakpointFunc func, + void* data) +{ + int bp; + + bp = bp_alloc(&calc->z80); + + calc->z80.breakpoints[bp].type = type; + calc->z80.breakpoints[bp].start = start; + calc->z80.breakpoints[bp].end = end; + calc->z80.breakpoints[bp].mask = mask; + calc->z80.breakpoints[bp].testfunc = func; + calc->z80.breakpoints[bp].testdata = data; + calc->z80.breakpoints[bp].prev = 0; + + return bp_add(calc, bp, type); +} + +void tilem_z80_remove_breakpoint(TilemCalc* calc, int id) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to remove invalid breakpoint %d", id); + return; + } + + bp_rem(calc, id, calc->z80.breakpoints[id].type); + + bp_free(&calc->z80, id); +} + +void tilem_z80_enable_breakpoint(TilemCalc* calc, int id) +{ + int type = tilem_z80_get_breakpoint_type(calc, id); + tilem_z80_set_breakpoint_type(calc, id, type & ~TILEM_BREAK_DISABLED); +} + +void tilem_z80_disable_breakpoint(TilemCalc* calc, int id) +{ + int type = tilem_z80_get_breakpoint_type(calc, id); + tilem_z80_set_breakpoint_type(calc, id, type | TILEM_BREAK_DISABLED); +} + +int tilem_z80_breakpoint_enabled(TilemCalc* calc, int id) +{ + int type = tilem_z80_get_breakpoint_type(calc, id); + return !(type & TILEM_BREAK_DISABLED); +} + +int tilem_z80_get_breakpoint_type(TilemCalc* calc, int id) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to access invalid breakpoint %d", id); + return -1; + } + + return calc->z80.breakpoints[id].type; +} + +dword tilem_z80_get_breakpoint_address_start(TilemCalc* calc, int id) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to access invalid breakpoint %d", id); + return -1; + } + + return calc->z80.breakpoints[id].start; +} + +dword tilem_z80_get_breakpoint_address_end(TilemCalc* calc, int id) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to access invalid breakpoint %d", id); + return -1; + } + + return calc->z80.breakpoints[id].end; +} + +dword tilem_z80_get_breakpoint_address_mask(TilemCalc* calc, int id) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to access invalid breakpoint %d", id); + return -1; + } + + return calc->z80.breakpoints[id].mask; +} + +TilemZ80BreakpointFunc tilem_z80_get_breakpoint_callback(TilemCalc* calc, int id) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to access invalid breakpoint %d", id); + return 0; + } + + return calc->z80.breakpoints[id].testfunc; +} + +void* tilem_z80_get_breakpoint_data(TilemCalc* calc, int id) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to access invalid breakpoint %d", id); + return 0; + } + + return calc->z80.breakpoints[id].testdata; +} + +void tilem_z80_set_breakpoint_type(TilemCalc* calc, int id, int type) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to modify invalid breakpoint %d", id); + return; + } + + if (type == calc->z80.breakpoints[id].type) + return; + + bp_rem(calc, id, calc->z80.breakpoints[id].type); + + calc->z80.breakpoints[id].type = type; + + bp_add(calc, id, type); +} + +void tilem_z80_set_breakpoint_address_start(TilemCalc* calc, int id, dword start) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to modify invalid breakpoint %d", id); + return; + } + + calc->z80.breakpoints[id].start = start; +} + +void tilem_z80_set_breakpoint_address_end(TilemCalc* calc, int id, dword end) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to modify invalid breakpoint %d", id); + return; + } + + calc->z80.breakpoints[id].end = end; +} + +void tilem_z80_set_breakpoint_address_mask(TilemCalc* calc, int id, dword mask) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to modify invalid breakpoint %d", id); + return; + } + + calc->z80.breakpoints[id].mask = mask; +} + +void tilem_z80_set_breakpoint_callback(TilemCalc* calc, int id, + TilemZ80BreakpointFunc func) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to modify invalid breakpoint %d", id); + return; + } + + calc->z80.breakpoints[id].testfunc = func; +} + +void tilem_z80_set_breakpoint_data(TilemCalc* calc, int id, void* data) +{ + if (id < 1 || id > calc->z80.nbreakpoints + || !calc->z80.breakpoints[id].type) { + tilem_internal(calc, + "attempt to modify invalid breakpoint %d", id); + return; + } + + calc->z80.breakpoints[id].testdata = data; +} + + +static inline void check_timers(TilemCalc* calc) +{ + int tmr; + dword t; + TilemZ80TimerFunc callback; + void* callbackdata; + + while (calc->z80.timer_cpu) { + tmr = calc->z80.timer_cpu; + t = calc->z80.clock - calc->z80.timers[tmr].count; + if (t >= 10000) + break; + + callback = calc->z80.timers[tmr].callback; + callbackdata = calc->z80.timers[tmr].callbackdata; + + timer_unset(&calc->z80, tmr); + timer_set(&calc->z80, tmr, calc->z80.timers[tmr].period, + calc->z80.timers[tmr].period, 0, t); + + (*callback)(calc, callbackdata); + } + + while (calc->z80.timer_rt) { + tmr = calc->z80.timer_rt; + t = calc->z80.clock - calc->z80.timers[tmr].count; + if (t >= 10000) + break; + + callback = calc->z80.timers[tmr].callback; + callbackdata = calc->z80.timers[tmr].callbackdata; + + timer_unset(&calc->z80, tmr); + timer_set(&calc->z80, tmr, calc->z80.timers[tmr].period, + calc->z80.timers[tmr].period, 1, t); + + (*callback)(calc, callbackdata); + } +} + +static inline void check_breakpoints(TilemCalc* calc, int list, dword addr) +{ + dword masked; + int bp; + TilemZ80BreakpointFunc testfunc; + void* testdata; + + for (bp = list; bp; bp = calc->z80.breakpoints[bp].next) { + masked = addr & calc->z80.breakpoints[bp].mask; + if (masked < calc->z80.breakpoints[bp].start + || masked > calc->z80.breakpoints[bp].end) + continue; + + testfunc = calc->z80.breakpoints[bp].testfunc; + testdata = calc->z80.breakpoints[bp].testdata; + + if (testfunc && !(*testfunc)(calc, addr, testdata)) + continue; + + calc->z80.stop_breakpoint = bp; + tilem_z80_stop(calc, TILEM_STOP_BREAKPOINT); + } +} + +static inline void check_mem_breakpoints(TilemCalc* calc, int list_l, + int list_p, dword addr) +{ + check_breakpoints(calc, list_l, addr); + + if (list_p) { + addr = (*calc->hw.mem_ltop)(calc, addr); + check_breakpoints(calc, list_p, addr); + } +} + +static inline byte z80_readb_m1(TilemCalc* calc, dword addr) +{ + byte b; + addr &= 0xffff; + b = (*calc->hw.z80_rdmem_m1)(calc, addr); + check_mem_breakpoints(calc, calc->z80.breakpoint_mx, + calc->z80.breakpoint_mpx, addr); + Rl++; + return b; +} + +static inline byte z80_readb(TilemCalc* calc, dword addr) +{ + byte b; + addr &= 0xffff; + b = (*calc->hw.z80_rdmem)(calc, addr); + check_mem_breakpoints(calc, calc->z80.breakpoint_mr, + calc->z80.breakpoint_mpr, addr); + return b; +} + +static inline dword z80_readw(TilemCalc* calc, dword addr) +{ + dword v; + addr &= 0xffff; + v = (*calc->hw.z80_rdmem)(calc, addr); + check_mem_breakpoints(calc, calc->z80.breakpoint_mr, + calc->z80.breakpoint_mpr, addr); + addr = (addr + 1) & 0xffff; + v |= (*calc->hw.z80_rdmem)(calc, addr) << 8; + check_mem_breakpoints(calc, calc->z80.breakpoint_mr, + calc->z80.breakpoint_mpr, addr); + return v; +} + +static inline byte z80_input(TilemCalc* calc, dword addr) +{ + byte b; + addr &= 0xffff; + check_timers(calc); + b = (*calc->hw.z80_in)(calc, addr); + check_breakpoints(calc, calc->z80.breakpoint_pr, addr); + return b; +} + +static inline void z80_writeb(TilemCalc* calc, dword addr, byte value) +{ + addr &= 0xffff; + (*calc->hw.z80_wrmem)(calc, addr, value); + check_mem_breakpoints(calc, calc->z80.breakpoint_mw, + calc->z80.breakpoint_mpw, addr); + calc->z80.lastwrite = calc->z80.clock; +} + +static inline void z80_writew(TilemCalc* calc, dword addr, word value) +{ + addr &= 0xffff; + (*calc->hw.z80_wrmem)(calc, addr, value); + check_mem_breakpoints(calc, calc->z80.breakpoint_mw, + calc->z80.breakpoint_mpw, addr); + addr = (addr + 1) & 0xffff; + value >>= 8; + (*calc->hw.z80_wrmem)(calc, addr, value); + check_mem_breakpoints(calc, calc->z80.breakpoint_mw, + calc->z80.breakpoint_mpw, addr); + calc->z80.lastwrite = calc->z80.clock; +} + +static inline void z80_output(TilemCalc* calc, dword addr, byte value) +{ + addr &= 0xffff; + check_timers(calc); + (*calc->hw.z80_out)(calc, addr, value); + check_breakpoints(calc, calc->z80.breakpoint_pw, addr); +} + +#define readb_m1(aaa) z80_readb_m1(calc, aaa) +#define readb(aaa) z80_readb(calc, aaa) +#define readw(aaa) z80_readw(calc, aaa) +#define input(aaa) z80_input(calc, aaa) +#define writeb(aaa, vvv) z80_writeb(calc, aaa, vvv) +#define writew(aaa, vvv) z80_writew(calc, aaa, vvv) +#define output(aaa, vvv) z80_output(calc, aaa, vvv) +#define delay(nnn) calc->z80.clock += (nnn) + +#include "z80cmds.h" + +static dword z80_execute_opcode(TilemCalc* calc, byte op) +{ + byte tmp1; + word tmp2; + int offs; +#ifdef DISABLE_Z80_WZ_REGISTER + TilemZ80Reg temp_wz, temp_wz2; +#endif + + opcode_main: +#include "z80main.h" + return op; + + opcode_cb: +#include "z80cb.h" + return op | 0xcb00; + + opcode_ed: +#include "z80ed.h" + return op | 0xed00; + +#define PREFIX_DD + opcode_dd: +#include "z80ddfd.h" + return op | 0xdd00; + opcode_ddcb: +#include "z80cb.h" + return op | 0xddcb0000; +#undef PREFIX_DD + +#define PREFIX_FD + opcode_fd: +#include "z80ddfd.h" + return op | 0xfd00; + opcode_fdcb: +#include "z80cb.h" + return op | 0xfdcb0000; +#undef PREFIX_FD +} + +static void z80_execute(TilemCalc* calc) +{ + TilemZ80* z80 = &calc->z80; + byte busbyte; + dword op; + dword t1, t2; + + z80->stopping = 0; + z80->stop_reason = 0; + z80->stop_breakpoint = 0; + + if (!z80->timer_cpu && !z80->timer_rt) { + tilem_internal(calc, "No timers set"); + return; + } + + while (!z80->stopping) { + z80->exception = 0; + op = (*calc->hw.z80_rdmem_m1)(calc, PC); + PC++; + Rl++; + op = z80_execute_opcode(calc, op); + check_breakpoints(calc, z80->breakpoint_op, op); + check_timers(calc); + + if (z80->interrupts && IFF1 && op != 0xfb + && op != 0xddfb && op != 0xfdfb) { + IFF1 = IFF2 = 0; + Rl++; + z80->halted = 0; + + /* Depending on the calculator, this value + varies somewhat randomly from one interrupt + to the next (making IM 2 rather difficult + to use, and IM 0 essentially worthless.) + Most likely, there is nothing connected to + the data bus at interrupt time. I seem to + remember somebody (sigma, perhaps?) + experimenting with this on the TI-83+ and + finding it usually 3F, 7F, BF, or FF. Or + maybe I'm completely wrong. In any case it + is unwise for programs to depend on this + value! */ + + busbyte = rand() & 0xff; + + switch (IM) { + case 0: + delay(2); + z80_execute_opcode(calc, busbyte); + break; + + case 1: + push(PC); + PC = 0x0038; + delay(13); + break; + + case 2: + /* FIXME: does accepting an IM 2 + interrupt affect WZ? It seems very + likely. */ + push(PC); + PC = readw((IR & 0xff00) | busbyte); + delay(19); + } + check_mem_breakpoints(calc, z80->breakpoint_mx, z80->breakpoint_mpx, PC); + check_timers(calc); + } + else if (op != 0x76) { + check_mem_breakpoints(calc, z80->breakpoint_mx, z80->breakpoint_mpx, PC); + } + else { + z80->halted = 1; + PC--; + if (z80->stopping) + break; + + /* CPU halted: fast-forward to next timer event */ + if (z80->timer_cpu && z80->timer_rt) { + t1 = (z80->timers[z80->timer_cpu].count + - z80->clock); + t2 = (z80->timers[z80->timer_rt].count + - z80->clock); + if (t1 > t2) + t1 = t2; + } + else if (z80->timer_cpu) { + t1 = (z80->timers[z80->timer_cpu].count + - z80->clock); + } + else if (z80->timer_rt) { + t1 = (z80->timers[z80->timer_rt].count + - z80->clock); + } + else { + tilem_internal(calc, "No timers set"); + return; + } + + z80->clock += t1 & ~3; + Rl += t1 / 4; + check_timers(calc); + } + + if (TILEM_UNLIKELY(z80->exception)) { + if (z80->emuflags & TILEM_Z80_BREAK_EXCEPTIONS) + tilem_z80_stop(calc, TILEM_STOP_EXCEPTION); + if (!(z80->emuflags & TILEM_Z80_IGNORE_EXCEPTIONS)) + tilem_calc_reset(calc); + } + } +} + +static void tmr_stop(TilemCalc* calc, void* data TILEM_ATTR_UNUSED) +{ + tilem_z80_stop(calc, TILEM_STOP_TIMEOUT); +} + +dword tilem_z80_run(TilemCalc* calc, int clocks, int* remaining) +{ + int tmr = tilem_z80_add_timer(calc, clocks, 0, 0, &tmr_stop, 0); + z80_execute(calc); + if (remaining) + *remaining = tilem_z80_get_timer_clocks(calc, tmr); + tilem_z80_remove_timer(calc, tmr); + return calc->z80.stop_reason; +} + +dword tilem_z80_run_time(TilemCalc* calc, int microseconds, int* remaining) +{ + int tmr = tilem_z80_add_timer(calc, microseconds, 0, 1, &tmr_stop, 0); + z80_execute(calc); + if (remaining) + *remaining = tilem_z80_get_timer_microseconds(calc, tmr); + tilem_z80_remove_timer(calc, tmr); + return calc->z80.stop_reason; +} diff --git a/tool/tilem-src/emu/z80.h b/tool/tilem-src/emu/z80.h new file mode 100644 index 0000000..f48c483 --- /dev/null +++ b/tool/tilem-src/emu/z80.h @@ -0,0 +1,110 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#ifndef _TILEM_Z80_H +#define _TILEM_Z80_H + +/* Internal Z80 data structures */ + +struct _TilemZ80Timer { + int next, prev; + dword count; + dword period; + TilemZ80TimerFunc callback; + void* callbackdata; +}; + +struct _TilemZ80Breakpoint { + int next, prev; + int type; + dword start; + dword end; + dword mask; + TilemZ80BreakpointFunc testfunc; + void* testdata; +}; + +/* Useful definitions */ + +#define AF (calc->z80.r.af.d) +#define BC (calc->z80.r.bc.d) +#define DE (calc->z80.r.de.d) +#define HL (calc->z80.r.hl.d) +#define AF2 (calc->z80.r.af2.d) +#define BC2 (calc->z80.r.bc2.d) +#define DE2 (calc->z80.r.de2.d) +#define HL2 (calc->z80.r.hl2.d) +#define IX (calc->z80.r.ix.d) +#define IY (calc->z80.r.iy.d) +#define SP (calc->z80.r.sp.d) +#define PC (calc->z80.r.pc.d) +#define IR (calc->z80.r.ir.d) + +#define A (calc->z80.r.af.b.h) +#define F (calc->z80.r.af.b.l) +#define B (calc->z80.r.bc.b.h) +#define C (calc->z80.r.bc.b.l) +#define D (calc->z80.r.de.b.h) +#define E (calc->z80.r.de.b.l) +#define H (calc->z80.r.hl.b.h) +#define L (calc->z80.r.hl.b.l) +#define IXh (calc->z80.r.ix.b.h) +#define IXl (calc->z80.r.ix.b.l) +#define IYh (calc->z80.r.iy.b.h) +#define IYl (calc->z80.r.iy.b.l) +#define I (calc->z80.r.ir.b.h) +#define Rh (calc->z80.r.r7) +#define Rl (calc->z80.r.ir.b.l) +#define R ((Rl & 0x7f) | Rh) + +#ifdef DISABLE_Z80_WZ_REGISTER +# define WZ temp_wz.d +# define WZ2 temp_wz2.d +# define W temp_wz.b.h +# define Z temp_wz.b.l +#else +# define WZ (calc->z80.r.wz.d) +# define WZ2 (calc->z80.r.wz2.d) +# define W (calc->z80.r.wz.b.h) +# define Z (calc->z80.r.wz.b.l) +#endif + +#define IFF1 (calc->z80.r.iff1) +#define IFF2 (calc->z80.r.iff2) +#define IM (calc->z80.r.im) + +#define FLAG_S 0x80 +#define FLAG_Z 0x40 +#define FLAG_Y 0x20 +#define FLAG_H 0x10 +#define FLAG_X 0x08 +#define FLAG_P 0x04 +#define FLAG_V FLAG_P +#define FLAG_N 0x02 +#define FLAG_C 0x01 + +#define BCw (calc->z80.r.bc.w.l) +#define DEw (calc->z80.r.de.w.l) +#define HLw (calc->z80.r.hl.w.l) +#define SPw (calc->z80.r.sp.w.l) +#define IXw (calc->z80.r.ix.w.l) +#define IYw (calc->z80.r.iy.w.l) + +#endif diff --git a/tool/tilem-src/emu/z80cb.h b/tool/tilem-src/emu/z80cb.h new file mode 100644 index 0000000..ef276ee --- /dev/null +++ b/tool/tilem-src/emu/z80cb.h @@ -0,0 +1,426 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +#if defined(PREFIX_DD) || defined(PREFIX_FD) + +# define CBINST(fnc, reg) do { \ + UNDOCUMENTED(16); \ + tmp1 = readb(WZ); \ + fnc(tmp1); \ + writeb(WZ, tmp1); \ + (reg) = tmp1; \ + delay(19); \ + } while (0) + +# define CBINST_HL(fnc) do { \ + tmp1 = readb(WZ); \ + fnc(tmp1); \ + writeb(WZ, tmp1); \ + delay(19); \ + } while (0) + +# define CBINST_UNDOC(fnc, reg) CBINST(fnc, reg) + +# define CBINST_UNDOC_HL(fnc) do { \ + UNDOCUMENTED(12); \ + CBINST_HL(fnc); \ + } while (0) + +# define CB_BIT(b, reg) do { \ + UNDOCUMENTED(12); \ + CB_BIT_HL(b); \ + } while (0) + +# define CB_BIT_HL(b) do { \ + tmp1 = readb(WZ) & (1 << b); \ + F = ((tmp1 & FLAG_S) /* S */ \ + | (W & FLAG_XY) /* X/Y */ \ + | (tmp1 ? 0 : FLAG_ZP) /* Z/P */ \ + | (FLAG_H) /* H */ \ + | (F & FLAG_C)); \ + delay(16); \ + } while (0) + +# define CB_RES(b, reg) do { \ + UNDOCUMENTED(16); \ + tmp1 = readb(WZ) & ~(1 << b); \ + writeb(WZ, tmp1); \ + (reg) = tmp1; \ + delay(19); \ + } while (0) + +# define CB_RES_HL(b) do { \ + tmp1 = readb(WZ) & ~(1 << b); \ + writeb(WZ, tmp1); \ + delay(19); \ + } while (0) + +# define CB_SET(b, reg) do { \ + UNDOCUMENTED(16); \ + tmp1 = readb(WZ) | (1 << b); \ + writeb(WZ, tmp1); \ + (reg) = tmp1; \ + delay(19); \ + } while (0) + +# define CB_SET_HL(b) do { \ + tmp1 = readb(WZ) | (1 << b); \ + writeb(WZ, tmp1); \ + delay(19); \ + } while (0) + +#else /* ! PREFIX_DD, ! PREFIX_FD */ + +# define CBINST(fnc, reg) do { \ + fnc(reg); \ + delay(8); \ + } while (0) + +# define CBINST_HL(fnc) do { \ + tmp1 = readb(HL); \ + fnc(tmp1); \ + writeb(HL, tmp1); \ + delay(15); \ + } while (0) + +# define CBINST_UNDOC(fnc, reg) do { \ + UNDOCUMENTED(8); \ + CBINST(fnc, reg); \ + } while (0) + +# define CBINST_UNDOC_HL(fnc) do { \ + UNDOCUMENTED(8); \ + CBINST_HL(fnc); \ + } while (0) + +# define CB_BIT(b, reg) do { \ + tmp1 = (reg) & (1 << b); \ + F = ((tmp1 & FLAG_SXY) /* S/X/Y */ \ + | (tmp1 ? 0 : FLAG_ZP) /* Z/P */ \ + | (FLAG_H) /* H */ \ + | (F & FLAG_C)); \ + delay(8); \ + } while (0) + +# define CB_BIT_HL(b) do { \ + tmp1 = readb(HL) & (1 << b); \ + F = ((tmp1 & FLAG_S) /* S */ \ + | (W & FLAG_XY) /* X/Y */ \ + | (tmp1 ? 0 : FLAG_ZP) /* Z/P */ \ + | (FLAG_H) /* H */ \ + | (F & FLAG_C)); \ + delay(12); \ + } while (0) + +# define CB_RES(b, reg) do { \ + (reg) &= ~(1 << b); \ + delay(8); \ + } while (0) + +# define CB_RES_HL(b) do { \ + writeb(HL, readb(HL) & ~(1 << b)); \ + delay(15); \ + } while (0) + +# define CB_SET(b, reg) do { \ + (reg) |= (1 << b); \ + delay(8); \ + } while (0) + +# define CB_SET_HL(b) do { \ + writeb(HL, readb(HL) | (1 << b)); \ + delay(15); \ + } while (0) + +#endif + +switch (op) { + case 0x00: CBINST(rlc, B); break; + case 0x01: CBINST(rlc, C); break; + case 0x02: CBINST(rlc, D); break; + case 0x03: CBINST(rlc, E); break; + case 0x04: CBINST(rlc, H); break; + case 0x05: CBINST(rlc, L); break; + case 0x06: CBINST_HL(rlc); break; + case 0x07: CBINST(rlc, A); break; + case 0x08: CBINST(rrc, B); break; + case 0x09: CBINST(rrc, C); break; + case 0x0A: CBINST(rrc, D); break; + case 0x0B: CBINST(rrc, E); break; + case 0x0C: CBINST(rrc, H); break; + case 0x0D: CBINST(rrc, L); break; + case 0x0E: CBINST_HL(rrc); break; + case 0x0F: CBINST(rrc, A); break; + case 0x10: CBINST(rl, B); break; + case 0x11: CBINST(rl, C); break; + case 0x12: CBINST(rl, D); break; + case 0x13: CBINST(rl, E); break; + case 0x14: CBINST(rl, H); break; + case 0x15: CBINST(rl, L); break; + case 0x16: CBINST_HL(rl); break; + case 0x17: CBINST(rl, A); break; + case 0x18: CBINST(rr, B); break; + case 0x19: CBINST(rr, C); break; + case 0x1A: CBINST(rr, D); break; + case 0x1B: CBINST(rr, E); break; + case 0x1C: CBINST(rr, H); break; + case 0x1D: CBINST(rr, L); break; + case 0x1E: CBINST_HL(rr); break; + case 0x1F: CBINST(rr, A); break; + case 0x20: CBINST(sla, B); break; + case 0x21: CBINST(sla, C); break; + case 0x22: CBINST(sla, D); break; + case 0x23: CBINST(sla, E); break; + case 0x24: CBINST(sla, H); break; + case 0x25: CBINST(sla, L); break; + case 0x26: CBINST_HL(sla); break; + case 0x27: CBINST(sla, A); break; + case 0x28: CBINST(sra, B); break; + case 0x29: CBINST(sra, C); break; + case 0x2A: CBINST(sra, D); break; + case 0x2B: CBINST(sra, E); break; + case 0x2C: CBINST(sra, H); break; + case 0x2D: CBINST(sra, L); break; + case 0x2E: CBINST_HL(sra); break; + case 0x2F: CBINST(sra, A); break; + case 0x30: CBINST_UNDOC(slia, B); break; + case 0x31: CBINST_UNDOC(slia, C); break; + case 0x32: CBINST_UNDOC(slia, D); break; + case 0x33: CBINST_UNDOC(slia, E); break; + case 0x34: CBINST_UNDOC(slia, H); break; + case 0x35: CBINST_UNDOC(slia, L); break; + case 0x36: CBINST_UNDOC_HL(slia); break; + case 0x37: CBINST_UNDOC(slia, A); break; + case 0x38: CBINST(srl, B); break; + case 0x39: CBINST(srl, C); break; + case 0x3A: CBINST(srl, D); break; + case 0x3B: CBINST(srl, E); break; + case 0x3C: CBINST(srl, H); break; + case 0x3D: CBINST(srl, L); break; + case 0x3E: CBINST_HL(srl); break; + case 0x3F: CBINST(srl, A); break; + + case 0x40: CB_BIT(0, B); break; + case 0x41: CB_BIT(0, C); break; + case 0x42: CB_BIT(0, D); break; + case 0x43: CB_BIT(0, E); break; + case 0x44: CB_BIT(0, H); break; + case 0x45: CB_BIT(0, L); break; + case 0x46: CB_BIT_HL(0); break; + case 0x47: CB_BIT(0, A); break; + case 0x48: CB_BIT(1, B); break; + case 0x49: CB_BIT(1, C); break; + case 0x4A: CB_BIT(1, D); break; + case 0x4B: CB_BIT(1, E); break; + case 0x4C: CB_BIT(1, H); break; + case 0x4D: CB_BIT(1, L); break; + case 0x4E: CB_BIT_HL(1); break; + case 0x4F: CB_BIT(1, A); break; + case 0x50: CB_BIT(2, B); break; + case 0x51: CB_BIT(2, C); break; + case 0x52: CB_BIT(2, D); break; + case 0x53: CB_BIT(2, E); break; + case 0x54: CB_BIT(2, H); break; + case 0x55: CB_BIT(2, L); break; + case 0x56: CB_BIT_HL(2); break; + case 0x57: CB_BIT(2, A); break; + case 0x58: CB_BIT(3, B); break; + case 0x59: CB_BIT(3, C); break; + case 0x5A: CB_BIT(3, D); break; + case 0x5B: CB_BIT(3, E); break; + case 0x5C: CB_BIT(3, H); break; + case 0x5D: CB_BIT(3, L); break; + case 0x5E: CB_BIT_HL(3); break; + case 0x5F: CB_BIT(3, A); break; + case 0x60: CB_BIT(4, B); break; + case 0x61: CB_BIT(4, C); break; + case 0x62: CB_BIT(4, D); break; + case 0x63: CB_BIT(4, E); break; + case 0x64: CB_BIT(4, H); break; + case 0x65: CB_BIT(4, L); break; + case 0x66: CB_BIT_HL(4); break; + case 0x67: CB_BIT(4, A); break; + case 0x68: CB_BIT(5, B); break; + case 0x69: CB_BIT(5, C); break; + case 0x6A: CB_BIT(5, D); break; + case 0x6B: CB_BIT(5, E); break; + case 0x6C: CB_BIT(5, H); break; + case 0x6D: CB_BIT(5, L); break; + case 0x6E: CB_BIT_HL(5); break; + case 0x6F: CB_BIT(5, A); break; + case 0x70: CB_BIT(6, B); break; + case 0x71: CB_BIT(6, C); break; + case 0x72: CB_BIT(6, D); break; + case 0x73: CB_BIT(6, E); break; + case 0x74: CB_BIT(6, H); break; + case 0x75: CB_BIT(6, L); break; + case 0x76: CB_BIT_HL(6); break; + case 0x77: CB_BIT(6, A); break; + case 0x78: CB_BIT(7, B); break; + case 0x79: CB_BIT(7, C); break; + case 0x7A: CB_BIT(7, D); break; + case 0x7B: CB_BIT(7, E); break; + case 0x7C: CB_BIT(7, H); break; + case 0x7D: CB_BIT(7, L); break; + case 0x7E: CB_BIT_HL(7); break; + case 0x7F: CB_BIT(7, A); break; + + case 0x80: CB_RES(0, B); break; + case 0x81: CB_RES(0, C); break; + case 0x82: CB_RES(0, D); break; + case 0x83: CB_RES(0, E); break; + case 0x84: CB_RES(0, H); break; + case 0x85: CB_RES(0, L); break; + case 0x86: CB_RES_HL(0); break; + case 0x87: CB_RES(0, A); break; + case 0x88: CB_RES(1, B); break; + case 0x89: CB_RES(1, C); break; + case 0x8A: CB_RES(1, D); break; + case 0x8B: CB_RES(1, E); break; + case 0x8C: CB_RES(1, H); break; + case 0x8D: CB_RES(1, L); break; + case 0x8E: CB_RES_HL(1); break; + case 0x8F: CB_RES(1, A); break; + case 0x90: CB_RES(2, B); break; + case 0x91: CB_RES(2, C); break; + case 0x92: CB_RES(2, D); break; + case 0x93: CB_RES(2, E); break; + case 0x94: CB_RES(2, H); break; + case 0x95: CB_RES(2, L); break; + case 0x96: CB_RES_HL(2); break; + case 0x97: CB_RES(2, A); break; + case 0x98: CB_RES(3, B); break; + case 0x99: CB_RES(3, C); break; + case 0x9A: CB_RES(3, D); break; + case 0x9B: CB_RES(3, E); break; + case 0x9C: CB_RES(3, H); break; + case 0x9D: CB_RES(3, L); break; + case 0x9E: CB_RES_HL(3); break; + case 0x9F: CB_RES(3, A); break; + case 0xA0: CB_RES(4, B); break; + case 0xA1: CB_RES(4, C); break; + case 0xA2: CB_RES(4, D); break; + case 0xA3: CB_RES(4, E); break; + case 0xA4: CB_RES(4, H); break; + case 0xA5: CB_RES(4, L); break; + case 0xA6: CB_RES_HL(4); break; + case 0xA7: CB_RES(4, A); break; + case 0xA8: CB_RES(5, B); break; + case 0xA9: CB_RES(5, C); break; + case 0xAA: CB_RES(5, D); break; + case 0xAB: CB_RES(5, E); break; + case 0xAC: CB_RES(5, H); break; + case 0xAD: CB_RES(5, L); break; + case 0xAE: CB_RES_HL(5); break; + case 0xAF: CB_RES(5, A); break; + case 0xB0: CB_RES(6, B); break; + case 0xB1: CB_RES(6, C); break; + case 0xB2: CB_RES(6, D); break; + case 0xB3: CB_RES(6, E); break; + case 0xB4: CB_RES(6, H); break; + case 0xB5: CB_RES(6, L); break; + case 0xB6: CB_RES_HL(6); break; + case 0xB7: CB_RES(6, A); break; + case 0xB8: CB_RES(7, B); break; + case 0xB9: CB_RES(7, C); break; + case 0xBA: CB_RES(7, D); break; + case 0xBB: CB_RES(7, E); break; + case 0xBC: CB_RES(7, H); break; + case 0xBD: CB_RES(7, L); break; + case 0xBE: CB_RES_HL(7); break; + case 0xBF: CB_RES(7, A); break; + + case 0xC0: CB_SET(0, B); break; + case 0xC1: CB_SET(0, C); break; + case 0xC2: CB_SET(0, D); break; + case 0xC3: CB_SET(0, E); break; + case 0xC4: CB_SET(0, H); break; + case 0xC5: CB_SET(0, L); break; + case 0xC6: CB_SET_HL(0); break; + case 0xC7: CB_SET(0, A); break; + case 0xC8: CB_SET(1, B); break; + case 0xC9: CB_SET(1, C); break; + case 0xCA: CB_SET(1, D); break; + case 0xCB: CB_SET(1, E); break; + case 0xCC: CB_SET(1, H); break; + case 0xCD: CB_SET(1, L); break; + case 0xCE: CB_SET_HL(1); break; + case 0xCF: CB_SET(1, A); break; + case 0xD0: CB_SET(2, B); break; + case 0xD1: CB_SET(2, C); break; + case 0xD2: CB_SET(2, D); break; + case 0xD3: CB_SET(2, E); break; + case 0xD4: CB_SET(2, H); break; + case 0xD5: CB_SET(2, L); break; + case 0xD6: CB_SET_HL(2); break; + case 0xD7: CB_SET(2, A); break; + case 0xD8: CB_SET(3, B); break; + case 0xD9: CB_SET(3, C); break; + case 0xDA: CB_SET(3, D); break; + case 0xDB: CB_SET(3, E); break; + case 0xDC: CB_SET(3, H); break; + case 0xDD: CB_SET(3, L); break; + case 0xDE: CB_SET_HL(3); break; + case 0xDF: CB_SET(3, A); break; + case 0xE0: CB_SET(4, B); break; + case 0xE1: CB_SET(4, C); break; + case 0xE2: CB_SET(4, D); break; + case 0xE3: CB_SET(4, E); break; + case 0xE4: CB_SET(4, H); break; + case 0xE5: CB_SET(4, L); break; + case 0xE6: CB_SET_HL(4); break; + case 0xE7: CB_SET(4, A); break; + case 0xE8: CB_SET(5, B); break; + case 0xE9: CB_SET(5, C); break; + case 0xEA: CB_SET(5, D); break; + case 0xEB: CB_SET(5, E); break; + case 0xEC: CB_SET(5, H); break; + case 0xED: CB_SET(5, L); break; + case 0xEE: CB_SET_HL(5); break; + case 0xEF: CB_SET(5, A); break; + case 0xF0: CB_SET(6, B); break; + case 0xF1: CB_SET(6, C); break; + case 0xF2: CB_SET(6, D); break; + case 0xF3: CB_SET(6, E); break; + case 0xF4: CB_SET(6, H); break; + case 0xF5: CB_SET(6, L); break; + case 0xF6: CB_SET_HL(6); break; + case 0xF7: CB_SET(6, A); break; + case 0xF8: CB_SET(7, B); break; + case 0xF9: CB_SET(7, C); break; + case 0xFA: CB_SET(7, D); break; + case 0xFB: CB_SET(7, E); break; + case 0xFC: CB_SET(7, H); break; + case 0xFD: CB_SET(7, L); break; + case 0xFE: CB_SET_HL(7); break; + case 0xFF: CB_SET(7, A); break; + } + +#undef CBINST +#undef CBINST_HL +#undef CBINST_UNDOC +#undef CBINST_UNDOC_HL +#undef CB_BIT +#undef CB_BIT_HL +#undef CB_RES +#undef CB_RES_HL +#undef CB_SET +#undef CB_SET_HL + diff --git a/tool/tilem-src/emu/z80cmds.h b/tool/tilem-src/emu/z80cmds.h new file mode 100644 index 0000000..8ce23b2 --- /dev/null +++ b/tool/tilem-src/emu/z80cmds.h @@ -0,0 +1,840 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 2009-2011 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 + * . + */ + +/* Flag magic: + + S (the Sign flag) is set when the result is negative (i.e., copied + from the MS bit.) + + Z (the Zero flag) is set when the result is zero. + + Y (undocumented flag) is usually copied from the result. + + H (the Half-carry flag) is set when a carry occurs from LS to MS + nibble. Since bit 4 of the result is equal to (bit 4 of first arg) + xor (bit 4 of second arg) xor H, we can infer that H equals (bit 4 + of first arg) xor (bit 4 of second arg) xor (bit 4 of result). + + X (undocumented flag) is copied from the same place as is Y. + + P (parity flag) is set -- by logic operations -- when the result + has even parity; i.e., it's the xor of all the bits of the result + and 1. + + V (overflow flag, which is another name for the parity flag) is set + -- by arithmetic operations -- when the result overflows. This can + happen when a positive plus a positive is negative, or a negative + plus a negative is positive, or a positive minus a negative is + negative, or a negative minus a positive is positive. + + (In 8 bits, a positive plus a negative is at least 0 + -128 = -128 + and at most 127 + -1 + 1 = 127. A positive minus a positive is at + least 0 - 127 - 1 = -128 and at most 127 - 0 = 127. A negative + minus a negative is at least -128 - -1 - 1 = -128 and at most -1 - + -128 = 127. Thus these cases can never overflow.) + + When adding, then, overflow occurs if the args are equal in the + MS bit and the result differs in the MS bit. + + When subtracting, overflow occurs if the args differ in the MS + bit and the result differs from the first arg in the MS bit. + + N (subtract flag) is set or cleared depending on the operation. + + C (carry flag) is the leftover bit after an addition or + subtraction. +*/ + +#define FLAG_XY (FLAG_X | FLAG_Y) +#define FLAG_XYC (FLAG_X | FLAG_Y | FLAG_C) +#define FLAG_SXY (FLAG_S | FLAG_X | FLAG_Y) +#define FLAG_SXYC (FLAG_S | FLAG_X | FLAG_Y | FLAG_C) +#define FLAG_ZP (FLAG_Z | FLAG_P) +#define FLAG_SZPC (FLAG_S | FLAG_Z | FLAG_P | FLAG_C) + +#define lsb(xxx) ((xxx) & 0xff) + +#define UNDOCUMENTED(clks) \ + if (calc->z80.emuflags & TILEM_Z80_BREAK_UNDOCUMENTED) \ + tilem_z80_stop(calc, TILEM_STOP_UNDOCUMENTED_INST); \ + if (calc->z80.emuflags & TILEM_Z80_SKIP_UNDOCUMENTED) { \ + delay(clks); \ + break; \ + } \ + if (calc->z80.emuflags & TILEM_Z80_RESET_UNDOCUMENTED) { \ + tilem_warning(calc, "Invalid opcode %x", op); \ + tilem_z80_exception(calc, TILEM_EXC_INSTRUCTION); \ + break; \ + } + +#define add8(dst, src) do { \ + word arg1 = (dst); \ + word arg2 = (src); \ + word res = arg1 + arg2; \ + byte resb = res; \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (resb ? 0 : FLAG_Z) /* Z */ \ + | ((arg1 ^ arg2 ^ res) & FLAG_H) /* H */ \ + | (((arg1 ^ ~arg2) & (arg1 ^ res) & 0x80) >> 5) \ + | (res >> 8)); /* C */ \ + (dst) = resb; \ + } while (0) + +#define adc8(dst, src) do { \ + word arg1 = (dst); \ + word arg2 = (src); \ + word res = arg1 + arg2 + (F & 1); \ + byte resb = res; \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (resb ? 0 : FLAG_Z) /* Z */ \ + | ((arg1 ^ arg2 ^ res) & FLAG_H) /* H */ \ + | (((arg1 ^ ~arg2) & (arg1 ^ res) & 0x80) >> 5) \ + | (res >> 8)); /* C */ \ + (dst) = resb; \ + } while (0) + +#define sub8(dst, src) do { \ + word arg1 = (dst); \ + word arg2 = (src); \ + word res = arg1 - arg2; \ + byte resb = res; \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (resb ? 0 : FLAG_Z) /* Z */ \ + | ((arg1 ^ arg2 ^ res) & FLAG_H) /* H */ \ + | (((arg1 ^ arg2) & (arg1 ^ res) & 0x80) >> 5) \ + | (FLAG_N) /* N */ \ + | ((res >> 8) & 1)); /* C */ \ + (dst) = resb; \ + } while (0) + +#define sbc8(dst, src) do { \ + word arg1 = (dst); \ + word arg2 = (src); \ + word res = arg1 - arg2 - (F & 1); \ + byte resb = res; \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (resb ? 0 : FLAG_Z) /* Z */ \ + | ((arg1 ^ arg2 ^ res) & FLAG_H) /* H */ \ + | (((arg1 ^ arg2) & (arg1 ^ res) & 0x80) >> 5) \ + | (FLAG_N) /* N */ \ + | ((res >> 8) & 1)); /* C */ \ + (dst) = resb; \ + } while (0) + +#define and(dst, src) do { \ + byte arg1 = (dst); \ + byte arg2 = (src); \ + byte res = (arg1 & arg2); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | (FLAG_H) /* H */ \ + | parity_table[res]); /* P */ \ + (dst) = res; \ + } while (0) + +#define xor(dst, src) do { \ + byte arg1 = (dst); \ + byte arg2 = (src); \ + byte res = (arg1 ^ arg2); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res]); /* P */ \ + (dst) = res; \ + } while (0) + +#define or(dst, src) do { \ + byte arg1 = (dst); \ + byte arg2 = (src); \ + byte res = (arg1 | arg2); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res]); /* P */ \ + (dst) = res; \ + } while (0) + +#define cp(dst, src) do { \ + word arg1 = (dst); \ + word arg2 = (src); \ + word res = arg1 - arg2; \ + byte resb = res; \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (resb ? 0 : FLAG_Z) /* Z */ \ + | ((arg1 ^ arg2 ^ res) & FLAG_H) /* H */ \ + | (((arg1 ^ arg2) & (arg1 ^ res) & 0x80) >> 5) \ + | (FLAG_N) /* N */ \ + | ((res >> 8) & 1)); /* C */ \ + } while (0) + + +#define add16(dst, src) do { \ + dword arg1 = (dst); \ + dword arg2 = (src); \ + dword res = arg1 + arg2; \ + F = ((F & (FLAG_S | FLAG_Z | FLAG_P)) \ + | ((res >> 8) & FLAG_XY) /* X/Y */ \ + | (((arg1 ^ arg2 ^ res) >> 8) & FLAG_H) /* H */ \ + | (res >> 16)); /* C */ \ + (dst) = res; \ + } while (0) + +#define adc16(dst, src) do { \ + dword arg1 = (dst); \ + dword arg2 = (src); \ + dword res = arg1 + arg2 + (F & 1); \ + word resw = res; \ + F = (((res >> 8) & FLAG_SXY) /* S/X/Y */\ + | (resw ? 0 : FLAG_Z) /* Z */ \ + | (((arg1 ^ arg2 ^ res) >> 8) & FLAG_H) /* H */ \ + | (((arg1 ^ ~arg2) & (arg1 ^ res) & 0x8000) >> 11) \ + | (res >> 16)); /* C */ \ + (dst) = resw; \ + } while (0) + +#define sbc16(dst, src) do { \ + dword arg1 = (dst); \ + dword arg2 = (src); \ + dword res = arg1 - arg2 - (F & 1); \ + word resw = res; \ + F = (((res >> 8) & FLAG_SXY) /* S/X/Y */\ + | (resw ? 0 : FLAG_Z) /* Z */ \ + | (((arg1 ^ arg2 ^ res) >> 8) & FLAG_H) /* H */ \ + | (((arg1 ^ arg2) & (arg1 ^ res) & 0x8000) >> 11) \ + | (FLAG_N) /* N */ \ + | ((res >> 16) & 1)); /* C */ \ + (dst) = resw; \ + } while (0) + + +#define inc(reg) do { \ + byte arg = (reg); \ + byte res = arg + 1; \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | ((arg ^ res) & FLAG_H) /* H */ \ + | (res == 0x80 ? FLAG_V : 0) /* V */ \ + | (F & FLAG_C)); \ + (reg) = res; \ + } while (0) + +#define dec(reg) do { \ + byte arg = (reg); \ + byte res = arg - 1; \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | ((arg ^ res) & FLAG_H) /* H */ \ + | (res == 0x7f ? FLAG_V : 0) /* V */ \ + | (FLAG_N) /* N */ \ + | (F & FLAG_C)); \ + (reg) = res; \ + } while (0) + + +#define cpl(reg) do { \ + byte arg = (reg); \ + byte res = ~arg; \ + F = ((res & FLAG_XY) /* X/Y */ \ + | FLAG_H | FLAG_N /* H/N */ \ + | (F & FLAG_SZPC)); \ + (reg) = res; \ + } while (0) + +#define neg(reg) do { \ + byte arg = (reg); \ + byte res = -arg; \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | ((arg ^ res) & FLAG_H) /* H */ \ + | (arg == 0x80 ? FLAG_V : 0) /* V */ \ + | (FLAG_N) /* N */ \ + | (!!arg)); /* C */ \ + (reg) = res; \ + } while (0) + +#define daa do { \ + word idx = (((F & 0x10) << 6) | ((F & 0x03) << 8) | A); \ + AF = daa_table[idx]; \ + } while (0) + + +#define rlca do { \ + byte arg = A; \ + byte res = ((arg << 1) | (arg >> 7)); \ + F = ((F & (FLAG_S | FLAG_Z | FLAG_P)) \ + | (res & FLAG_XYC)); /* X/Y/C */ \ + A = res; \ + } while (0) + +#define rrca do { \ + byte arg = A; \ + byte res = ((arg >> 1) | (arg << 7)); \ + F = ((F & (FLAG_S | FLAG_Z | FLAG_P)) \ + | (res & FLAG_XY) /* X/Y */ \ + | (arg & FLAG_C)); /* C */ \ + A = res; \ + } while (0) + +#define rla do { \ + byte arg = A; \ + byte res = ((arg << 1) | (F & 1)); \ + F = ((F & (FLAG_S | FLAG_Z | FLAG_P)) \ + | (res & FLAG_XY) /* X/Y */ \ + | (arg >> 7)); /* C */ \ + A = res; \ + } while (0) + +#define rra do { \ + byte arg = A; \ + byte res = ((arg >> 1) | (F << 7)); \ + F = ((F & (FLAG_S | FLAG_Z | FLAG_P)) \ + | (res & FLAG_XY) /* X/Y */ \ + | (arg & FLAG_C)); /* C */ \ + A = res; \ + } while (0) + +#define rlc(reg) do { \ + byte arg = (reg); \ + byte res = ((arg << 1) | (arg >> 7)); \ + F = ((res & FLAG_SXYC) /* S/X/Y/C */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res]); /* P */ \ + (reg) = res; \ + } while (0) + +#define rrc(reg) do { \ + byte arg = (reg); \ + byte res = ((arg >> 1) | (arg << 7)); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (arg & FLAG_C)); /* C */ \ + (reg) = res; \ + } while (0) + +#define rl(reg) do { \ + byte arg = (reg); \ + byte res = ((arg << 1) | (F & 1)); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (arg >> 7)); /* C */ \ + (reg) = res; \ + } while (0) + +#define rr(reg) do { \ + byte arg = (reg); \ + byte res = ((arg >> 1) | (F << 7)); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (arg & FLAG_C)); /* C */ \ + (reg) = res; \ + } while (0) + +#define sla(reg) do { \ + byte arg = (reg); \ + byte res = (arg << 1); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (arg >> 7)); /* C */ \ + (reg) = res; \ + } while (0) + +#define sra(reg) do { \ + byte arg = (reg); \ + byte res = ((signed char) arg >> 1); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (arg & FLAG_C)); /* C */ \ + (reg) = res; \ + } while (0) + +#define slia(reg) do { \ + byte arg = (reg); \ + byte res = ((arg << 1) | 1); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (arg >> 7)); /* C */ \ + (reg) = res; \ + } while (0) + +#define srl(reg) do { \ + byte arg = (reg); \ + byte res = (arg >> 1); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (arg & FLAG_C)); /* C */ \ + (reg) = res; \ + } while (0) + +#define rld do { \ + byte arg1 = readb(HL); \ + byte arg2 = A; \ + byte res = ((arg2 & 0xf0) | (arg1 >> 4)); \ + WZ = HL + 1; \ + writeb(HL, (arg1 << 4) | (arg2 & 0xf)); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (F & FLAG_C)); \ + A = res; \ + } while (0) + +#define rrd do { \ + byte arg1 = readb(HL); \ + byte arg2 = A; \ + byte res = ((arg2 & 0xf0) | (arg1 & 0x0f)); \ + WZ = HL + 1; \ + writeb(HL, (arg1 >> 4) | (arg2 << 4)); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (F & FLAG_C)); \ + A = res; \ + } while (0) + +#define push(reg) do { \ + SP -= 2; \ + writew(SP, (reg)); \ + } while (0) + +#define pop(reg) do { \ + (reg) = readw(SP); \ + SP += 2; \ + } while (0) + +#define ex(aaa, bbb) do { \ + tmp2 = (aaa); \ + (aaa) = (bbb); \ + (bbb) = tmp2; \ + } while (0) + +#define in(reg) do { \ + byte res = input(WZ); \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | parity_table[res] /* P */ \ + | (F & FLAG_C)); \ + (reg) = res; \ + } while (0) + + +#define ld_a_ir(reg) do { \ + byte res = (reg); \ + A = res; \ + F = ((res & FLAG_SXY) /* S/X/Y */ \ + | (res ? 0 : FLAG_Z) /* Z */ \ + | (IFF2 ? FLAG_P : 0) /* P */ \ + | (F & FLAG_C)); \ + } while (0) + +#define ldi do { \ + byte res = readb(HL++); \ + writeb(DE++, res); \ + BC--; \ + F = ((F & (FLAG_S | FLAG_Z | FLAG_C)) \ + | ((res << 4) & FLAG_Y) /* Y */ \ + | (res & FLAG_X) /* X */ \ + | (BCw ? FLAG_P : 0)); /* P */ \ + } while (0) + +#define ldd do { \ + byte res = readb(HL--); \ + writeb(DE--, res); \ + BC--; \ + F = ((F & (FLAG_S | FLAG_Z | FLAG_C)) \ + | ((res << 4) & FLAG_Y) /* Y */ \ + | (res & FLAG_X) /* X */ \ + | (BCw ? FLAG_P : 0)); /* P */ \ + } while (0) + +/* FIXME: CPI and CPD obviously use WZ, but I haven't yet figured out + precisely how. */ + +#define cpi do { \ + word arg1 = A; \ + word arg2 = readb(HL++); \ + word res = arg1 - arg2; \ + byte hc = ((arg1 ^ arg2 ^ res) & 0x10); \ + byte res2 = res - (hc >> 4); \ + BC--; \ + F = ((res & FLAG_S) /* S */ \ + | (lsb(res) ? 0 : FLAG_Z) /* Z */ \ + | ((res2 << 4) & FLAG_Y) /* Y */ \ + | hc /* H */ \ + | (res2 & FLAG_X) /* X */ \ + | (BCw ? FLAG_P : 0) /* P */ \ + | FLAG_N /* N */ \ + | (F & FLAG_C)); \ + } while (0) + +#define cpd do { \ + word arg1 = A; \ + word arg2 = readb(HL--); \ + word res = arg1 - arg2; \ + byte hc = ((arg1 ^ arg2 ^ res) & 0x10); \ + byte res2 = res - (hc >> 4); \ + BC--; \ + F = ((res & FLAG_S) /* S */ \ + | (lsb(res) ? 0 : FLAG_Z) /* Z */ \ + | ((res2 << 4) & FLAG_Y) /* Y */ \ + | hc /* H */ \ + | (res2 & FLAG_X) /* X */ \ + | (BCw ? FLAG_P : 0) /* P */ \ + | FLAG_N /* N */ \ + | (F & FLAG_C)); \ + } while (0) + +#define ini do { \ + byte value, count; \ + word k; \ + value = input(BC); \ + writeb(HL++, value); \ + WZ = BC + 1; \ + k = Z + value; \ + count = --B; \ + F = ((count & FLAG_SXY) /* S/X/Y */ \ + | (count ? 0 : FLAG_Z) /* Z */ \ + | ((k >> 8) ? 0x11 : 0) /* H/C */ \ + | parity_table[(k & 7) ^ count] /* P */ \ + | ((value >> 6) & FLAG_N)); /* N */ \ + } while (0) + +#define ind do { \ + byte value, count; \ + word k; \ + value = input(BC); \ + writeb(HL--, value); \ + WZ = BC - 1; \ + k = Z + value; \ + count = --B; \ + F = ((count & FLAG_SXY) /* S/X/Y */ \ + | (count ? 0 : FLAG_Z) /* Z */ \ + | ((k >> 8) ? 0x11 : 0) /* H/C */ \ + | parity_table[(k & 7) ^ count] /* P */ \ + | ((value >> 6) & FLAG_N)); /* N */ \ + } while (0) + +#define outi do { \ + byte value, count; \ + word k; \ + value = readb(HL++); \ + count = --B; \ + output(BC, value); \ + k = L + value; \ + WZ = BC + 1; \ + F = ((count & FLAG_SXY) /* S/X/Y */ \ + | (count ? 0 : FLAG_Z) /* Z */ \ + | ((k >> 8) ? 0x11 : 0) /* H/C */ \ + | parity_table[(k & 7) ^ count] /* P */ \ + | ((value >> 6) & FLAG_N)); /* N */ \ + } while (0) + +#define outd do { \ + byte value, count; \ + word k; \ + value = readb(HL--); \ + count = --B; \ + output(BC, value); \ + k = L + value; \ + WZ = BC - 1; \ + F = ((count & FLAG_SXY) /* S/X/Y */ \ + | (count ? 0 : FLAG_Z) /* Z */ \ + | ((k >> 8) ? 0x11 : 0) /* H/C */ \ + | parity_table[(k & 7) ^ count] /* P */ \ + | ((value >> 6) & FLAG_N)); /* N */ \ + } while (0) + +/* Table giving parity flag values. (Also known as the Thue-Morse + sequence.) */ +static const byte parity_table[256] = { + 4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0, + + 0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4, 0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4, 4,0,0,4,0,4,4,0, 4,0,0,4,0,4,4,0, 0,4,4,0,4,0,0,4, +}; + +/* Table giving AF values following DAA */ +static const word daa_table[2048] = { + 0x0044, 0x0100, 0x0200, 0x0304, 0x0400, 0x0504, 0x0604, 0x0700, + 0x0808, 0x090c, 0x1010, 0x1114, 0x1214, 0x1310, 0x1414, 0x1510, + 0x1000, 0x1104, 0x1204, 0x1300, 0x1404, 0x1500, 0x1600, 0x1704, + 0x180c, 0x1908, 0x2030, 0x2134, 0x2234, 0x2330, 0x2434, 0x2530, + 0x2020, 0x2124, 0x2224, 0x2320, 0x2424, 0x2520, 0x2620, 0x2724, + 0x282c, 0x2928, 0x3034, 0x3130, 0x3230, 0x3334, 0x3430, 0x3534, + 0x3024, 0x3120, 0x3220, 0x3324, 0x3420, 0x3524, 0x3624, 0x3720, + 0x3828, 0x392c, 0x4010, 0x4114, 0x4214, 0x4310, 0x4414, 0x4510, + 0x4000, 0x4104, 0x4204, 0x4300, 0x4404, 0x4500, 0x4600, 0x4704, + 0x480c, 0x4908, 0x5014, 0x5110, 0x5210, 0x5314, 0x5410, 0x5514, + 0x5004, 0x5100, 0x5200, 0x5304, 0x5400, 0x5504, 0x5604, 0x5700, + 0x5808, 0x590c, 0x6034, 0x6130, 0x6230, 0x6334, 0x6430, 0x6534, + 0x6024, 0x6120, 0x6220, 0x6324, 0x6420, 0x6524, 0x6624, 0x6720, + 0x6828, 0x692c, 0x7030, 0x7134, 0x7234, 0x7330, 0x7434, 0x7530, + 0x7020, 0x7124, 0x7224, 0x7320, 0x7424, 0x7520, 0x7620, 0x7724, + 0x782c, 0x7928, 0x8090, 0x8194, 0x8294, 0x8390, 0x8494, 0x8590, + 0x8080, 0x8184, 0x8284, 0x8380, 0x8484, 0x8580, 0x8680, 0x8784, + 0x888c, 0x8988, 0x9094, 0x9190, 0x9290, 0x9394, 0x9490, 0x9594, + 0x9084, 0x9180, 0x9280, 0x9384, 0x9480, 0x9584, 0x9684, 0x9780, + 0x9888, 0x998c, 0x0055, 0x0111, 0x0211, 0x0315, 0x0411, 0x0515, + 0x0045, 0x0101, 0x0201, 0x0305, 0x0401, 0x0505, 0x0605, 0x0701, + 0x0809, 0x090d, 0x1011, 0x1115, 0x1215, 0x1311, 0x1415, 0x1511, + 0x1001, 0x1105, 0x1205, 0x1301, 0x1405, 0x1501, 0x1601, 0x1705, + 0x180d, 0x1909, 0x2031, 0x2135, 0x2235, 0x2331, 0x2435, 0x2531, + 0x2021, 0x2125, 0x2225, 0x2321, 0x2425, 0x2521, 0x2621, 0x2725, + 0x282d, 0x2929, 0x3035, 0x3131, 0x3231, 0x3335, 0x3431, 0x3535, + 0x3025, 0x3121, 0x3221, 0x3325, 0x3421, 0x3525, 0x3625, 0x3721, + 0x3829, 0x392d, 0x4011, 0x4115, 0x4215, 0x4311, 0x4415, 0x4511, + 0x4001, 0x4105, 0x4205, 0x4301, 0x4405, 0x4501, 0x4601, 0x4705, + 0x480d, 0x4909, 0x5015, 0x5111, 0x5211, 0x5315, 0x5411, 0x5515, + 0x5005, 0x5101, 0x5201, 0x5305, 0x5401, 0x5505, 0x5605, 0x5701, + 0x5809, 0x590d, 0x6035, 0x6131, 0x6231, 0x6335, 0x6431, 0x6535, + 0x6025, 0x6121, 0x6221, 0x6325, 0x6421, 0x6525, 0x6625, 0x6721, + 0x6829, 0x692d, 0x7031, 0x7135, 0x7235, 0x7331, 0x7435, 0x7531, + 0x7021, 0x7125, 0x7225, 0x7321, 0x7425, 0x7521, 0x7621, 0x7725, + 0x782d, 0x7929, 0x8091, 0x8195, 0x8295, 0x8391, 0x8495, 0x8591, + 0x8081, 0x8185, 0x8285, 0x8381, 0x8485, 0x8581, 0x8681, 0x8785, + 0x888d, 0x8989, 0x9095, 0x9191, 0x9291, 0x9395, 0x9491, 0x9595, + 0x9085, 0x9181, 0x9281, 0x9385, 0x9481, 0x9585, 0x9685, 0x9781, + 0x9889, 0x998d, 0xa0b5, 0xa1b1, 0xa2b1, 0xa3b5, 0xa4b1, 0xa5b5, + 0xa0a5, 0xa1a1, 0xa2a1, 0xa3a5, 0xa4a1, 0xa5a5, 0xa6a5, 0xa7a1, + 0xa8a9, 0xa9ad, 0xb0b1, 0xb1b5, 0xb2b5, 0xb3b1, 0xb4b5, 0xb5b1, + 0xb0a1, 0xb1a5, 0xb2a5, 0xb3a1, 0xb4a5, 0xb5a1, 0xb6a1, 0xb7a5, + 0xb8ad, 0xb9a9, 0xc095, 0xc191, 0xc291, 0xc395, 0xc491, 0xc595, + 0xc085, 0xc181, 0xc281, 0xc385, 0xc481, 0xc585, 0xc685, 0xc781, + 0xc889, 0xc98d, 0xd091, 0xd195, 0xd295, 0xd391, 0xd495, 0xd591, + 0xd081, 0xd185, 0xd285, 0xd381, 0xd485, 0xd581, 0xd681, 0xd785, + 0xd88d, 0xd989, 0xe0b1, 0xe1b5, 0xe2b5, 0xe3b1, 0xe4b5, 0xe5b1, + 0xe0a1, 0xe1a5, 0xe2a5, 0xe3a1, 0xe4a5, 0xe5a1, 0xe6a1, 0xe7a5, + 0xe8ad, 0xe9a9, 0xf0b5, 0xf1b1, 0xf2b1, 0xf3b5, 0xf4b1, 0xf5b5, + 0xf0a5, 0xf1a1, 0xf2a1, 0xf3a5, 0xf4a1, 0xf5a5, 0xf6a5, 0xf7a1, + 0xf8a9, 0xf9ad, 0x0055, 0x0111, 0x0211, 0x0315, 0x0411, 0x0515, + 0x0045, 0x0101, 0x0201, 0x0305, 0x0401, 0x0505, 0x0605, 0x0701, + 0x0809, 0x090d, 0x1011, 0x1115, 0x1215, 0x1311, 0x1415, 0x1511, + 0x1001, 0x1105, 0x1205, 0x1301, 0x1405, 0x1501, 0x1601, 0x1705, + 0x180d, 0x1909, 0x2031, 0x2135, 0x2235, 0x2331, 0x2435, 0x2531, + 0x2021, 0x2125, 0x2225, 0x2321, 0x2425, 0x2521, 0x2621, 0x2725, + 0x282d, 0x2929, 0x3035, 0x3131, 0x3231, 0x3335, 0x3431, 0x3535, + 0x3025, 0x3121, 0x3221, 0x3325, 0x3421, 0x3525, 0x3625, 0x3721, + 0x3829, 0x392d, 0x4011, 0x4115, 0x4215, 0x4311, 0x4415, 0x4511, + 0x4001, 0x4105, 0x4205, 0x4301, 0x4405, 0x4501, 0x4601, 0x4705, + 0x480d, 0x4909, 0x5015, 0x5111, 0x5211, 0x5315, 0x5411, 0x5515, + 0x5005, 0x5101, 0x5201, 0x5305, 0x5401, 0x5505, 0x5605, 0x5701, + 0x5809, 0x590d, 0x6035, 0x6131, 0x6231, 0x6335, 0x6431, 0x6535, + 0x0046, 0x0102, 0x0202, 0x0306, 0x0402, 0x0506, 0x0606, 0x0702, + 0x080a, 0x090e, 0x0402, 0x0506, 0x0606, 0x0702, 0x080a, 0x090e, + 0x1002, 0x1106, 0x1206, 0x1302, 0x1406, 0x1502, 0x1602, 0x1706, + 0x180e, 0x190a, 0x1406, 0x1502, 0x1602, 0x1706, 0x180e, 0x190a, + 0x2022, 0x2126, 0x2226, 0x2322, 0x2426, 0x2522, 0x2622, 0x2726, + 0x282e, 0x292a, 0x2426, 0x2522, 0x2622, 0x2726, 0x282e, 0x292a, + 0x3026, 0x3122, 0x3222, 0x3326, 0x3422, 0x3526, 0x3626, 0x3722, + 0x382a, 0x392e, 0x3422, 0x3526, 0x3626, 0x3722, 0x382a, 0x392e, + 0x4002, 0x4106, 0x4206, 0x4302, 0x4406, 0x4502, 0x4602, 0x4706, + 0x480e, 0x490a, 0x4406, 0x4502, 0x4602, 0x4706, 0x480e, 0x490a, + 0x5006, 0x5102, 0x5202, 0x5306, 0x5402, 0x5506, 0x5606, 0x5702, + 0x580a, 0x590e, 0x5402, 0x5506, 0x5606, 0x5702, 0x580a, 0x590e, + 0x6026, 0x6122, 0x6222, 0x6326, 0x6422, 0x6526, 0x6626, 0x6722, + 0x682a, 0x692e, 0x6422, 0x6526, 0x6626, 0x6722, 0x682a, 0x692e, + 0x7022, 0x7126, 0x7226, 0x7322, 0x7426, 0x7522, 0x7622, 0x7726, + 0x782e, 0x792a, 0x7426, 0x7522, 0x7622, 0x7726, 0x782e, 0x792a, + 0x8082, 0x8186, 0x8286, 0x8382, 0x8486, 0x8582, 0x8682, 0x8786, + 0x888e, 0x898a, 0x8486, 0x8582, 0x8682, 0x8786, 0x888e, 0x898a, + 0x9086, 0x9182, 0x9282, 0x9386, 0x9482, 0x9586, 0x9686, 0x9782, + 0x988a, 0x998e, 0x3423, 0x3527, 0x3627, 0x3723, 0x382b, 0x392f, + 0x4003, 0x4107, 0x4207, 0x4303, 0x4407, 0x4503, 0x4603, 0x4707, + 0x480f, 0x490b, 0x4407, 0x4503, 0x4603, 0x4707, 0x480f, 0x490b, + 0x5007, 0x5103, 0x5203, 0x5307, 0x5403, 0x5507, 0x5607, 0x5703, + 0x580b, 0x590f, 0x5403, 0x5507, 0x5607, 0x5703, 0x580b, 0x590f, + 0x6027, 0x6123, 0x6223, 0x6327, 0x6423, 0x6527, 0x6627, 0x6723, + 0x682b, 0x692f, 0x6423, 0x6527, 0x6627, 0x6723, 0x682b, 0x692f, + 0x7023, 0x7127, 0x7227, 0x7323, 0x7427, 0x7523, 0x7623, 0x7727, + 0x782f, 0x792b, 0x7427, 0x7523, 0x7623, 0x7727, 0x782f, 0x792b, + 0x8083, 0x8187, 0x8287, 0x8383, 0x8487, 0x8583, 0x8683, 0x8787, + 0x888f, 0x898b, 0x8487, 0x8583, 0x8683, 0x8787, 0x888f, 0x898b, + 0x9087, 0x9183, 0x9283, 0x9387, 0x9483, 0x9587, 0x9687, 0x9783, + 0x988b, 0x998f, 0x9483, 0x9587, 0x9687, 0x9783, 0x988b, 0x998f, + 0xa0a7, 0xa1a3, 0xa2a3, 0xa3a7, 0xa4a3, 0xa5a7, 0xa6a7, 0xa7a3, + 0xa8ab, 0xa9af, 0xa4a3, 0xa5a7, 0xa6a7, 0xa7a3, 0xa8ab, 0xa9af, + 0xb0a3, 0xb1a7, 0xb2a7, 0xb3a3, 0xb4a7, 0xb5a3, 0xb6a3, 0xb7a7, + 0xb8af, 0xb9ab, 0xb4a7, 0xb5a3, 0xb6a3, 0xb7a7, 0xb8af, 0xb9ab, + 0xc087, 0xc183, 0xc283, 0xc387, 0xc483, 0xc587, 0xc687, 0xc783, + 0xc88b, 0xc98f, 0xc483, 0xc587, 0xc687, 0xc783, 0xc88b, 0xc98f, + 0xd083, 0xd187, 0xd287, 0xd383, 0xd487, 0xd583, 0xd683, 0xd787, + 0xd88f, 0xd98b, 0xd487, 0xd583, 0xd683, 0xd787, 0xd88f, 0xd98b, + 0xe0a3, 0xe1a7, 0xe2a7, 0xe3a3, 0xe4a7, 0xe5a3, 0xe6a3, 0xe7a7, + 0xe8af, 0xe9ab, 0xe4a7, 0xe5a3, 0xe6a3, 0xe7a7, 0xe8af, 0xe9ab, + 0xf0a7, 0xf1a3, 0xf2a3, 0xf3a7, 0xf4a3, 0xf5a7, 0xf6a7, 0xf7a3, + 0xf8ab, 0xf9af, 0xf4a3, 0xf5a7, 0xf6a7, 0xf7a3, 0xf8ab, 0xf9af, + 0x0047, 0x0103, 0x0203, 0x0307, 0x0403, 0x0507, 0x0607, 0x0703, + 0x080b, 0x090f, 0x0403, 0x0507, 0x0607, 0x0703, 0x080b, 0x090f, + 0x1003, 0x1107, 0x1207, 0x1303, 0x1407, 0x1503, 0x1603, 0x1707, + 0x180f, 0x190b, 0x1407, 0x1503, 0x1603, 0x1707, 0x180f, 0x190b, + 0x2023, 0x2127, 0x2227, 0x2323, 0x2427, 0x2523, 0x2623, 0x2727, + 0x282f, 0x292b, 0x2427, 0x2523, 0x2623, 0x2727, 0x282f, 0x292b, + 0x3027, 0x3123, 0x3223, 0x3327, 0x3423, 0x3527, 0x3627, 0x3723, + 0x382b, 0x392f, 0x3423, 0x3527, 0x3627, 0x3723, 0x382b, 0x392f, + 0x4003, 0x4107, 0x4207, 0x4303, 0x4407, 0x4503, 0x4603, 0x4707, + 0x480f, 0x490b, 0x4407, 0x4503, 0x4603, 0x4707, 0x480f, 0x490b, + 0x5007, 0x5103, 0x5203, 0x5307, 0x5403, 0x5507, 0x5607, 0x5703, + 0x580b, 0x590f, 0x5403, 0x5507, 0x5607, 0x5703, 0x580b, 0x590f, + 0x6027, 0x6123, 0x6223, 0x6327, 0x6423, 0x6527, 0x6627, 0x6723, + 0x682b, 0x692f, 0x6423, 0x6527, 0x6627, 0x6723, 0x682b, 0x692f, + 0x7023, 0x7127, 0x7227, 0x7323, 0x7427, 0x7523, 0x7623, 0x7727, + 0x782f, 0x792b, 0x7427, 0x7523, 0x7623, 0x7727, 0x782f, 0x792b, + 0x8083, 0x8187, 0x8287, 0x8383, 0x8487, 0x8583, 0x8683, 0x8787, + 0x888f, 0x898b, 0x8487, 0x8583, 0x8683, 0x8787, 0x888f, 0x898b, + 0x9087, 0x9183, 0x9283, 0x9387, 0x9483, 0x9587, 0x9687, 0x9783, + 0x988b, 0x998f, 0x9483, 0x9587, 0x9687, 0x9783, 0x988b, 0x998f, + 0x0604, 0x0700, 0x0808, 0x090c, 0x0a0c, 0x0b08, 0x0c0c, 0x0d08, + 0x0e08, 0x0f0c, 0x1010, 0x1114, 0x1214, 0x1310, 0x1414, 0x1510, + 0x1600, 0x1704, 0x180c, 0x1908, 0x1a08, 0x1b0c, 0x1c08, 0x1d0c, + 0x1e0c, 0x1f08, 0x2030, 0x2134, 0x2234, 0x2330, 0x2434, 0x2530, + 0x2620, 0x2724, 0x282c, 0x2928, 0x2a28, 0x2b2c, 0x2c28, 0x2d2c, + 0x2e2c, 0x2f28, 0x3034, 0x3130, 0x3230, 0x3334, 0x3430, 0x3534, + 0x3624, 0x3720, 0x3828, 0x392c, 0x3a2c, 0x3b28, 0x3c2c, 0x3d28, + 0x3e28, 0x3f2c, 0x4010, 0x4114, 0x4214, 0x4310, 0x4414, 0x4510, + 0x4600, 0x4704, 0x480c, 0x4908, 0x4a08, 0x4b0c, 0x4c08, 0x4d0c, + 0x4e0c, 0x4f08, 0x5014, 0x5110, 0x5210, 0x5314, 0x5410, 0x5514, + 0x5604, 0x5700, 0x5808, 0x590c, 0x5a0c, 0x5b08, 0x5c0c, 0x5d08, + 0x5e08, 0x5f0c, 0x6034, 0x6130, 0x6230, 0x6334, 0x6430, 0x6534, + 0x6624, 0x6720, 0x6828, 0x692c, 0x6a2c, 0x6b28, 0x6c2c, 0x6d28, + 0x6e28, 0x6f2c, 0x7030, 0x7134, 0x7234, 0x7330, 0x7434, 0x7530, + 0x7620, 0x7724, 0x782c, 0x7928, 0x7a28, 0x7b2c, 0x7c28, 0x7d2c, + 0x7e2c, 0x7f28, 0x8090, 0x8194, 0x8294, 0x8390, 0x8494, 0x8590, + 0x8680, 0x8784, 0x888c, 0x8988, 0x8a88, 0x8b8c, 0x8c88, 0x8d8c, + 0x8e8c, 0x8f88, 0x9094, 0x9190, 0x9290, 0x9394, 0x9490, 0x9594, + 0x9684, 0x9780, 0x9888, 0x998c, 0x9a8c, 0x9b88, 0x9c8c, 0x9d88, + 0x9e88, 0x9f8c, 0x0055, 0x0111, 0x0211, 0x0315, 0x0411, 0x0515, + 0x0605, 0x0701, 0x0809, 0x090d, 0x0a0d, 0x0b09, 0x0c0d, 0x0d09, + 0x0e09, 0x0f0d, 0x1011, 0x1115, 0x1215, 0x1311, 0x1415, 0x1511, + 0x1601, 0x1705, 0x180d, 0x1909, 0x1a09, 0x1b0d, 0x1c09, 0x1d0d, + 0x1e0d, 0x1f09, 0x2031, 0x2135, 0x2235, 0x2331, 0x2435, 0x2531, + 0x2621, 0x2725, 0x282d, 0x2929, 0x2a29, 0x2b2d, 0x2c29, 0x2d2d, + 0x2e2d, 0x2f29, 0x3035, 0x3131, 0x3231, 0x3335, 0x3431, 0x3535, + 0x3625, 0x3721, 0x3829, 0x392d, 0x3a2d, 0x3b29, 0x3c2d, 0x3d29, + 0x3e29, 0x3f2d, 0x4011, 0x4115, 0x4215, 0x4311, 0x4415, 0x4511, + 0x4601, 0x4705, 0x480d, 0x4909, 0x4a09, 0x4b0d, 0x4c09, 0x4d0d, + 0x4e0d, 0x4f09, 0x5015, 0x5111, 0x5211, 0x5315, 0x5411, 0x5515, + 0x5605, 0x5701, 0x5809, 0x590d, 0x5a0d, 0x5b09, 0x5c0d, 0x5d09, + 0x5e09, 0x5f0d, 0x6035, 0x6131, 0x6231, 0x6335, 0x6431, 0x6535, + 0x6625, 0x6721, 0x6829, 0x692d, 0x6a2d, 0x6b29, 0x6c2d, 0x6d29, + 0x6e29, 0x6f2d, 0x7031, 0x7135, 0x7235, 0x7331, 0x7435, 0x7531, + 0x7621, 0x7725, 0x782d, 0x7929, 0x7a29, 0x7b2d, 0x7c29, 0x7d2d, + 0x7e2d, 0x7f29, 0x8091, 0x8195, 0x8295, 0x8391, 0x8495, 0x8591, + 0x8681, 0x8785, 0x888d, 0x8989, 0x8a89, 0x8b8d, 0x8c89, 0x8d8d, + 0x8e8d, 0x8f89, 0x9095, 0x9191, 0x9291, 0x9395, 0x9491, 0x9595, + 0x9685, 0x9781, 0x9889, 0x998d, 0x9a8d, 0x9b89, 0x9c8d, 0x9d89, + 0x9e89, 0x9f8d, 0xa0b5, 0xa1b1, 0xa2b1, 0xa3b5, 0xa4b1, 0xa5b5, + 0xa6a5, 0xa7a1, 0xa8a9, 0xa9ad, 0xaaad, 0xaba9, 0xacad, 0xada9, + 0xaea9, 0xafad, 0xb0b1, 0xb1b5, 0xb2b5, 0xb3b1, 0xb4b5, 0xb5b1, + 0xb6a1, 0xb7a5, 0xb8ad, 0xb9a9, 0xbaa9, 0xbbad, 0xbca9, 0xbdad, + 0xbead, 0xbfa9, 0xc095, 0xc191, 0xc291, 0xc395, 0xc491, 0xc595, + 0xc685, 0xc781, 0xc889, 0xc98d, 0xca8d, 0xcb89, 0xcc8d, 0xcd89, + 0xce89, 0xcf8d, 0xd091, 0xd195, 0xd295, 0xd391, 0xd495, 0xd591, + 0xd681, 0xd785, 0xd88d, 0xd989, 0xda89, 0xdb8d, 0xdc89, 0xdd8d, + 0xde8d, 0xdf89, 0xe0b1, 0xe1b5, 0xe2b5, 0xe3b1, 0xe4b5, 0xe5b1, + 0xe6a1, 0xe7a5, 0xe8ad, 0xe9a9, 0xeaa9, 0xebad, 0xeca9, 0xedad, + 0xeead, 0xefa9, 0xf0b5, 0xf1b1, 0xf2b1, 0xf3b5, 0xf4b1, 0xf5b5, + 0xf6a5, 0xf7a1, 0xf8a9, 0xf9ad, 0xfaad, 0xfba9, 0xfcad, 0xfda9, + 0xfea9, 0xffad, 0x0055, 0x0111, 0x0211, 0x0315, 0x0411, 0x0515, + 0x0605, 0x0701, 0x0809, 0x090d, 0x0a0d, 0x0b09, 0x0c0d, 0x0d09, + 0x0e09, 0x0f0d, 0x1011, 0x1115, 0x1215, 0x1311, 0x1415, 0x1511, + 0x1601, 0x1705, 0x180d, 0x1909, 0x1a09, 0x1b0d, 0x1c09, 0x1d0d, + 0x1e0d, 0x1f09, 0x2031, 0x2135, 0x2235, 0x2331, 0x2435, 0x2531, + 0x2621, 0x2725, 0x282d, 0x2929, 0x2a29, 0x2b2d, 0x2c29, 0x2d2d, + 0x2e2d, 0x2f29, 0x3035, 0x3131, 0x3231, 0x3335, 0x3431, 0x3535, + 0x3625, 0x3721, 0x3829, 0x392d, 0x3a2d, 0x3b29, 0x3c2d, 0x3d29, + 0x3e29, 0x3f2d, 0x4011, 0x4115, 0x4215, 0x4311, 0x4415, 0x4511, + 0x4601, 0x4705, 0x480d, 0x4909, 0x4a09, 0x4b0d, 0x4c09, 0x4d0d, + 0x4e0d, 0x4f09, 0x5015, 0x5111, 0x5211, 0x5315, 0x5411, 0x5515, + 0x5605, 0x5701, 0x5809, 0x590d, 0x5a0d, 0x5b09, 0x5c0d, 0x5d09, + 0x5e09, 0x5f0d, 0x6035, 0x6131, 0x6231, 0x6335, 0x6431, 0x6535, + 0xfabe, 0xfbba, 0xfcbe, 0xfdba, 0xfeba, 0xffbe, 0x0046, 0x0102, + 0x0202, 0x0306, 0x0402, 0x0506, 0x0606, 0x0702, 0x080a, 0x090e, + 0x0a1e, 0x0b1a, 0x0c1e, 0x0d1a, 0x0e1a, 0x0f1e, 0x1002, 0x1106, + 0x1206, 0x1302, 0x1406, 0x1502, 0x1602, 0x1706, 0x180e, 0x190a, + 0x1a1a, 0x1b1e, 0x1c1a, 0x1d1e, 0x1e1e, 0x1f1a, 0x2022, 0x2126, + 0x2226, 0x2322, 0x2426, 0x2522, 0x2622, 0x2726, 0x282e, 0x292a, + 0x2a3a, 0x2b3e, 0x2c3a, 0x2d3e, 0x2e3e, 0x2f3a, 0x3026, 0x3122, + 0x3222, 0x3326, 0x3422, 0x3526, 0x3626, 0x3722, 0x382a, 0x392e, + 0x3a3e, 0x3b3a, 0x3c3e, 0x3d3a, 0x3e3a, 0x3f3e, 0x4002, 0x4106, + 0x4206, 0x4302, 0x4406, 0x4502, 0x4602, 0x4706, 0x480e, 0x490a, + 0x4a1a, 0x4b1e, 0x4c1a, 0x4d1e, 0x4e1e, 0x4f1a, 0x5006, 0x5102, + 0x5202, 0x5306, 0x5402, 0x5506, 0x5606, 0x5702, 0x580a, 0x590e, + 0x5a1e, 0x5b1a, 0x5c1e, 0x5d1a, 0x5e1a, 0x5f1e, 0x6026, 0x6122, + 0x6222, 0x6326, 0x6422, 0x6526, 0x6626, 0x6722, 0x682a, 0x692e, + 0x6a3e, 0x6b3a, 0x6c3e, 0x6d3a, 0x6e3a, 0x6f3e, 0x7022, 0x7126, + 0x7226, 0x7322, 0x7426, 0x7522, 0x7622, 0x7726, 0x782e, 0x792a, + 0x7a3a, 0x7b3e, 0x7c3a, 0x7d3e, 0x7e3e, 0x7f3a, 0x8082, 0x8186, + 0x8286, 0x8382, 0x8486, 0x8582, 0x8682, 0x8786, 0x888e, 0x898a, + 0x8a9a, 0x8b9e, 0x8c9a, 0x8d9e, 0x8e9e, 0x8f9a, 0x9086, 0x9182, + 0x9282, 0x9386, 0x3423, 0x3527, 0x3627, 0x3723, 0x382b, 0x392f, + 0x3a3f, 0x3b3b, 0x3c3f, 0x3d3b, 0x3e3b, 0x3f3f, 0x4003, 0x4107, + 0x4207, 0x4303, 0x4407, 0x4503, 0x4603, 0x4707, 0x480f, 0x490b, + 0x4a1b, 0x4b1f, 0x4c1b, 0x4d1f, 0x4e1f, 0x4f1b, 0x5007, 0x5103, + 0x5203, 0x5307, 0x5403, 0x5507, 0x5607, 0x5703, 0x580b, 0x590f, + 0x5a1f, 0x5b1b, 0x5c1f, 0x5d1b, 0x5e1b, 0x5f1f, 0x6027, 0x6123, + 0x6223, 0x6327, 0x6423, 0x6527, 0x6627, 0x6723, 0x682b, 0x692f, + 0x6a3f, 0x6b3b, 0x6c3f, 0x6d3b, 0x6e3b, 0x6f3f, 0x7023, 0x7127, + 0x7227, 0x7323, 0x7427, 0x7523, 0x7623, 0x7727, 0x782f, 0x792b, + 0x7a3b, 0x7b3f, 0x7c3b, 0x7d3f, 0x7e3f, 0x7f3b, 0x8083, 0x8187, + 0x8287, 0x8383, 0x8487, 0x8583, 0x8683, 0x8787, 0x888f, 0x898b, + 0x8a9b, 0x8b9f, 0x8c9b, 0x8d9f, 0x8e9f, 0x8f9b, 0x9087, 0x9183, + 0x9283, 0x9387, 0x9483, 0x9587, 0x9687, 0x9783, 0x988b, 0x998f, + 0x9a9f, 0x9b9b, 0x9c9f, 0x9d9b, 0x9e9b, 0x9f9f, 0xa0a7, 0xa1a3, + 0xa2a3, 0xa3a7, 0xa4a3, 0xa5a7, 0xa6a7, 0xa7a3, 0xa8ab, 0xa9af, + 0xaabf, 0xabbb, 0xacbf, 0xadbb, 0xaebb, 0xafbf, 0xb0a3, 0xb1a7, + 0xb2a7, 0xb3a3, 0xb4a7, 0xb5a3, 0xb6a3, 0xb7a7, 0xb8af, 0xb9ab, + 0xbabb, 0xbbbf, 0xbcbb, 0xbdbf, 0xbebf, 0xbfbb, 0xc087, 0xc183, + 0xc283, 0xc387, 0xc483, 0xc587, 0xc687, 0xc783, 0xc88b, 0xc98f, + 0xca9f, 0xcb9b, 0xcc9f, 0xcd9b, 0xce9b, 0xcf9f, 0xd083, 0xd187, + 0xd287, 0xd383, 0xd487, 0xd583, 0xd683, 0xd787, 0xd88f, 0xd98b, + 0xda9b, 0xdb9f, 0xdc9b, 0xdd9f, 0xde9f, 0xdf9b, 0xe0a3, 0xe1a7, + 0xe2a7, 0xe3a3, 0xe4a7, 0xe5a3, 0xe6a3, 0xe7a7, 0xe8af, 0xe9ab, + 0xeabb, 0xebbf, 0xecbb, 0xedbf, 0xeebf, 0xefbb, 0xf0a7, 0xf1a3, + 0xf2a3, 0xf3a7, 0xf4a3, 0xf5a7, 0xf6a7, 0xf7a3, 0xf8ab, 0xf9af, + 0xfabf, 0xfbbb, 0xfcbf, 0xfdbb, 0xfebb, 0xffbf, 0x0047, 0x0103, + 0x0203, 0x0307, 0x0403, 0x0507, 0x0607, 0x0703, 0x080b, 0x090f, + 0x0a1f, 0x0b1b, 0x0c1f, 0x0d1b, 0x0e1b, 0x0f1f, 0x1003, 0x1107, + 0x1207, 0x1303, 0x1407, 0x1503, 0x1603, 0x1707, 0x180f, 0x190b, + 0x1a1b, 0x1b1f, 0x1c1b, 0x1d1f, 0x1e1f, 0x1f1b, 0x2023, 0x2127, + 0x2227, 0x2323, 0x2427, 0x2523, 0x2623, 0x2727, 0x282f, 0x292b, + 0x2a3b, 0x2b3f, 0x2c3b, 0x2d3f, 0x2e3f, 0x2f3b, 0x3027, 0x3123, + 0x3223, 0x3327, 0x3423, 0x3527, 0x3627, 0x3723, 0x382b, 0x392f, + 0x3a3f, 0x3b3b, 0x3c3f, 0x3d3b, 0x3e3b, 0x3f3f, 0x4003, 0x4107, + 0x4207, 0x4303, 0x4407, 0x4503, 0x4603, 0x4707, 0x480f, 0x490b, + 0x4a1b, 0x4b1f, 0x4c1b, 0x4d1f, 0x4e1f, 0x4f1b, 0x5007, 0x5103, + 0x5203, 0x5307, 0x5403, 0x5507, 0x5607, 0x5703, 0x580b, 0x590f, + 0x5a1f, 0x5b1b, 0x5c1f, 0x5d1b, 0x5e1b, 0x5f1f, 0x6027, 0x6123, + 0x6223, 0x6327, 0x6423, 0x6527, 0x6627, 0x6723, 0x682b, 0x692f, + 0x6a3f, 0x6b3b, 0x6c3f, 0x6d3b, 0x6e3b, 0x6f3f, 0x7023, 0x7127, + 0x7227, 0x7323, 0x7427, 0x7523, 0x7623, 0x7727, 0x782f, 0x792b, + 0x7a3b, 0x7b3f, 0x7c3b, 0x7d3f, 0x7e3f, 0x7f3b, 0x8083, 0x8187, + 0x8287, 0x8383, 0x8487, 0x8583, 0x8683, 0x8787, 0x888f, 0x898b, + 0x8a9b, 0x8b9f, 0x8c9b, 0x8d9f, 0x8e9f, 0x8f9b, 0x9087, 0x9183, + 0x9283, 0x9387, 0x9483, 0x9587, 0x9687, 0x9783, 0x988b, 0x998f +}; diff --git a/tool/tilem-src/emu/z80ddfd.h b/tool/tilem-src/emu/z80ddfd.h new file mode 100644 index 0000000..63288f6 --- /dev/null +++ b/tool/tilem-src/emu/z80ddfd.h @@ -0,0 +1,343 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 PREFIX_DD +# define RegH IXh +# define RegL IXl +# define RegHL IX +#else +# define RegH IYh +# define RegL IYl +# define RegHL IY +#endif + +switch (op) { + case 0x09: /* ADD IX, BC */ + WZ = RegHL + 1; + add16(RegHL, BC); + delay(11); + break; + case 0x19: /* ADD IX, DE */ + WZ = RegHL + 1; + add16(RegHL, DE); + delay(11); + break; + case 0x21: /* LD IX, nn */ + RegHL = readw(PC); + PC += 2; + delay(10); + break; + case 0x22: /* LD (nn), IX */ + WZ = readw(PC); + PC += 2; + writeb(WZ, RegL); + WZ++; + writeb(WZ, RegH); + delay(16); + break; + case 0x23: /* INC IX */ + RegHL++; + delay(6); + break; + case 0x24: /* INC IXH */ + UNDOCUMENTED(4); + inc(RegH); + delay(4); + break; + case 0x25: /* DEC IXH */ + UNDOCUMENTED(4); + dec(RegH); + delay(4); + break; + case 0x26: /* LD IXH, n */ + UNDOCUMENTED(4); + RegH = readb(PC++); + delay(7); + break; + case 0x29: /* ADD IX, IX */ + WZ = RegHL + 1; + add16(RegHL, RegHL); + delay(11); + break; + case 0x2A: /* LD IX, (nn) */ + WZ = readw(PC); + PC += 2; + RegL = readb(WZ); + WZ++; + RegH = readb(WZ); + delay(16); + break; + case 0x2B: /* DEC IX */ + RegHL--; + delay(6); + break; + case 0x2C: /* INC IXL */ + UNDOCUMENTED(4); + inc(RegL); + delay(4); + break; + case 0x2D: /* DEC IXL */ + UNDOCUMENTED(4); + dec(RegL); + delay(4); + break; + case 0x2E: /* LD IXL,n */ + UNDOCUMENTED(4); + RegL = readb(PC++); + delay(7); + break; + + case 0x34: /* INC (IX + n) */ + offs = (int) (signed char) readb(PC++); + WZ = RegHL + offs; + tmp1 = readb(WZ); + inc(tmp1); + writeb(WZ, tmp1); + delay(19); + break; + case 0x35: /* DEC (IX + n) */ + offs = (int) (signed char) readb(PC++); + WZ = RegHL + offs; + tmp1 = readb(WZ); + dec(tmp1); + writeb(WZ, tmp1); + delay(19); + break; + case 0x36: /* LD (IX + n), n */ + offs = (int) (signed char) readb(PC++); + writeb(RegHL + offs, readb(PC++)); + delay(15); /* Yes, really! */ + break; + + case 0x39: /* ADD IX, SP */ + WZ = RegHL + 1; + add16(RegHL, SP); + delay(11); + break; + + case 0x44: UNDOCUMENTED(4); B = RegH; delay(4); break; + case 0x45: UNDOCUMENTED(4); B = RegL; delay(4); break; + case 0x46: + offs = (int) (signed char) readb(PC++); + B = readb(RegHL + offs); + delay(15); + break; + + case 0x4C: UNDOCUMENTED(4); C = RegH; delay(4); break; + case 0x4D: UNDOCUMENTED(4); C = RegL; delay(4); break; + case 0x4E: + offs = (int) (signed char) readb(PC++); + C = readb(RegHL + offs); + delay(15); + break; + + case 0x54: UNDOCUMENTED(4); D = RegH; delay(4); break; + case 0x55: UNDOCUMENTED(4); D = RegL; delay(4); break; + case 0x56: + offs = (int) (signed char) readb(PC++); + D = readb(RegHL + offs); + delay(15); + break; + + case 0x5C: UNDOCUMENTED(4); E = RegH; delay(4); break; + case 0x5D: UNDOCUMENTED(4); E = RegL; delay(4); break; + case 0x5E: + offs = (int) (signed char) readb(PC++); + E = readb(RegHL + offs); + delay(15); + break; + + case 0x60: UNDOCUMENTED(4); RegH = B; delay(4); break; + case 0x61: UNDOCUMENTED(4); RegH = C; delay(4); break; + case 0x62: UNDOCUMENTED(4); RegH = D; delay(4); break; + case 0x63: UNDOCUMENTED(4); RegH = E; delay(4); break; + case 0x64: UNDOCUMENTED(4); RegH = RegH; delay(4); break; + case 0x65: UNDOCUMENTED(4); RegH = RegL; delay(4); break; + case 0x66: + offs = (int) (signed char) readb(PC++); + H = readb(RegHL + offs); + delay(15); + break; + case 0x67: UNDOCUMENTED(4); RegH = A; delay(4); break; + + case 0x68: UNDOCUMENTED(4); RegL = B; delay(4); break; + case 0x69: UNDOCUMENTED(4); RegL = C; delay(4); break; + case 0x6A: UNDOCUMENTED(4); RegL = D; delay(4); break; + case 0x6B: UNDOCUMENTED(4); RegL = E; delay(4); break; + case 0x6C: UNDOCUMENTED(4); RegL = RegH; delay(4); break; + case 0x6D: UNDOCUMENTED(4); RegL = RegL; delay(4); break; + case 0x6E: + offs = (int) (signed char) readb(PC++); + L = readb(RegHL + offs); + delay(15); + break; + case 0x6F: UNDOCUMENTED(4); RegL = A; delay(4); break; + + case 0x70: + offs = (int) (signed char) readb(PC++); + writeb(RegHL + offs, B); + delay(15); + break; + case 0x71: + offs = (int) (signed char) readb(PC++); + writeb(RegHL + offs, C); + delay(15); + break; + case 0x72: + offs = (int) (signed char) readb(PC++); + writeb(RegHL + offs, D); + delay(15); + break; + case 0x73: + offs = (int) (signed char) readb(PC++); + writeb(RegHL + offs, E); + delay(15); + break; + case 0x74: + offs = (int) (signed char) readb(PC++); + writeb(RegHL + offs, H); + delay(15); + break; + case 0x75: + offs = (int) (signed char) readb(PC++); + writeb(RegHL + offs, L); + delay(15); + break; + case 0x77: + offs = (int) (signed char) readb(PC++); + writeb(RegHL + offs, A); + delay(15); + break; + + case 0x7C: UNDOCUMENTED(4); A = RegH; delay(4); break; + case 0x7D: UNDOCUMENTED(4); A = RegL; delay(4); break; + case 0x7E: + offs = (int) (signed char) readb(PC++); + A = readb(RegHL + offs); + delay(15); + break; + + case 0x84: UNDOCUMENTED(4); add8(A, RegH); delay(4); break; + case 0x85: UNDOCUMENTED(4); add8(A, RegL); delay(4); break; + case 0x86: + offs = (int) (signed char) readb(PC++); + add8(A, readb(RegHL + offs)); + delay(7); + break; + + case 0x8C: UNDOCUMENTED(4); adc8(A, RegH); delay(4); break; + case 0x8D: UNDOCUMENTED(4); adc8(A, RegL); delay(4); break; + case 0x8E: + offs = (int) (signed char) readb(PC++); + adc8(A, readb(RegHL + offs)); + delay(15); + break; + + case 0x94: UNDOCUMENTED(4); sub8(A, RegH); delay(4); break; + case 0x95: UNDOCUMENTED(4); sub8(A, RegL); delay(4); break; + case 0x96: + offs = (int) (signed char) readb(PC++); + sub8(A, readb(RegHL + offs)); + delay(15); + break; + + case 0x9C: UNDOCUMENTED(4); sbc8(A, RegH); delay(4); break; + case 0x9D: UNDOCUMENTED(4); sbc8(A, RegL); delay(4); break; + case 0x9E: + offs = (int) (signed char) readb(PC++); + sbc8(A, readb(RegHL + offs)); + delay(15); + break; + + case 0xA4: UNDOCUMENTED(4); and(A, RegH); delay(4); break; + case 0xA5: UNDOCUMENTED(4); and(A, RegL); delay(4); break; + case 0xA6: + offs = (int) (signed char) readb(PC++); + and(A, readb(RegHL + offs)); + delay(15); + break; + + case 0xAC: UNDOCUMENTED(4); xor(A, RegH); delay(4); break; + case 0xAD: UNDOCUMENTED(4); xor(A, RegL); delay(4); break; + case 0xAE: + offs = (int) (signed char) readb(PC++); + xor(A, readb(RegHL + offs)); + delay(15); + break; + + case 0xB4: UNDOCUMENTED(4); or(A, RegH); delay(4); break; + case 0xB5: UNDOCUMENTED(4); or(A, RegL); delay(4); break; + case 0xB6: + offs = (int) (signed char) readb(PC++); + or(A, readb(RegHL + offs)); + delay(15); + break; + + case 0xBC: UNDOCUMENTED(4); cp(A, RegH); delay(4); break; + case 0xBD: UNDOCUMENTED(4); cp(A, RegL); delay(4); break; + case 0xBE: + offs = (int) (signed char) readb(PC++); + cp(A, readb(RegHL + offs)); + delay(15); + break; + + case 0xCB: + offs = (int) (signed char) readb(PC++); + WZ = RegHL + offs; + op = readb(PC++); +#ifdef PREFIX_DD + goto opcode_ddcb; +#else + goto opcode_fdcb; +#endif + + case 0xE1: /* POP IX */ + pop(RegHL); + delay(10); + break; + case 0xE3: /* EX (SP), IX */ + WZ = readw(SP); + writew(SP, RegHL); + RegHL = WZ; + delay(19); + break; + case 0xE5: /* PUSH IX */ + push(RegHL); + delay(11); + break; + + case 0xE9: /* JP IX */ + PC = RegHL; + delay(4); + break; + + case 0xF9: /* LD SP, IX */ + SP = RegHL; + delay(4); + break; + + default: + goto opcode_main; + } + +#undef RegH +#undef RegL +#undef RegHL + diff --git a/tool/tilem-src/emu/z80ed.h b/tool/tilem-src/emu/z80ed.h new file mode 100644 index 0000000..930bf63 --- /dev/null +++ b/tool/tilem-src/emu/z80ed.h @@ -0,0 +1,457 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +switch (op) { + case 0x40: /* IN B, (C) */ + WZ = BC; + delay(12); + in(B); + break; + case 0x41: /* OUT (C), B */ + delay(12); + output(BC, B); + break; + case 0x42: /* SBC HL, BC */ + WZ = HL + 1; + sbc16(HLw, BCw); + delay(15); + break; + case 0x43: /* LD (nn), BC */ + WZ = readw(PC); + PC += 2; + writew(WZ++, BC); + delay(20); + break; + case 0x44: /* NEG */ + neg(A); + delay(8); + break; + case 0x45: /* RETN */ + IFF1 = IFF2; + pop(PC); + delay(14); + break; + case 0x46: /* IM 0 */ + IM = 0; + delay(8); + break; + case 0x47: /* LD I,A */ + I = A; + delay(9); + break; + + case 0x48: /* IN C, (C) */ + WZ = BC; + delay(12); + in(C); + break; + case 0x49: /* OUT (C), C */ + delay(12); + output(BC, C); + break; + case 0x4A: /* ADC HL, BC */ + WZ = HL + 1; + adc16(HLw, BCw); + delay(15); + break; + case 0x4B: /* LD BC, (nn) */ + WZ = readw(PC); + PC += 2; + BC = readw(WZ++); + delay(20); + break; + case 0x4C: /* NEG */ + UNDOCUMENTED(8); + neg(A); + delay(8); + break; + case 0x4D: /* RETI */ + IFF1 = IFF2; + pop(PC); + delay(14); + break; + case 0x4E: /* IM 0 */ + UNDOCUMENTED(8); + IM = 0; + delay(8); + break; + case 0x4F: /* LD R,A */ + Rl = A; + Rh = A & 0x80; + delay(9); + break; + + case 0x50: /* IN D, (C) */ + WZ = BC; + delay(12); + in(D); + break; + case 0x51: /* OUT (C), D */ + delay(12); + output(BC, D); + break; + case 0x52: /* SBC HL, DE */ + WZ = HL + 1; + sbc16(HLw, DEw); + delay(15); + break; + case 0x53: /* LD (nn), DE */ + WZ = readw(PC); + PC += 2; + writew(WZ++, DE); + delay(20); + break; + case 0x54: /* NEG */ + UNDOCUMENTED(8); + neg(A); + delay(8); + break; + case 0x55: /* RETN */ + UNDOCUMENTED(8); + IFF1 = IFF2; + pop(PC); + delay(14); + break; + case 0x56: /* IM 1 */ + IM = 1; + delay(8); + break; + case 0x57: /* LD A,I */ + ld_a_ir(I); + delay(9); + break; + + case 0x58: /* IN E, (C) */ + WZ = BC; + delay(12); + in(E); + break; + case 0x59: /* OUT (C), E */ + delay(12); + output(BC, E); + break; + case 0x5A: /* ADC HL, DE */ + WZ = HL + 1; + adc16(HLw, DEw); + delay(15); + break; + case 0x5B: /* LD DE, (nn) */ + WZ = readw(PC); + PC += 2; + DE = readw(WZ++); + delay(20); + break; + case 0x5C: /* NEG */ + UNDOCUMENTED(8); + neg(A); + delay(8); + break; + case 0x5D: /* RETN */ + UNDOCUMENTED(8); + IFF1 = IFF2; + pop(PC); + delay(14); + break; + case 0x5E: /* IM 2 */ + IM = 2; + delay(8); + break; + case 0x5F: /* LD A,R */ + ld_a_ir(R); + delay(9); + break; + + case 0x60: /* IN H, (C) */ + WZ = BC; + delay(12); + in(H); + break; + case 0x61: /* OUT (C), H */ + delay(12); + output(BC, H); + break; + case 0x62: /* SBC HL, HL */ + WZ = HL + 1; + sbc16(HLw, HLw); + delay(15); + break; + case 0x63: /* LD (nn), HL */ + WZ = readw(PC); + PC += 2; + writew(WZ++, HL); + delay(20); + break; + case 0x64: /* NEG */ + UNDOCUMENTED(8); + neg(A); + delay(8); + break; + case 0x65: /* RETN */ + UNDOCUMENTED(8); + IFF1 = IFF2; + pop(PC); + delay(14); + break; + case 0x66: /* IM 0 */ + UNDOCUMENTED(8); + IM = 0; + delay(8); + break; + case 0x67: /* RRD */ + rrd; + delay(18); + break; + + case 0x68: /* IN L, (C) */ + WZ = BC; + delay(12); + in(L); + break; + case 0x69: /* OUT (C), L */ + delay(12); + output(BC, L); + break; + case 0x6A: /* ADC HL, HL */ + WZ = HL + 1; + adc16(HLw, HLw); + delay(15); + break; + case 0x6B: /* LD HL, (nn) */ + WZ = readw(PC); + PC += 2; + HL = readw(WZ++); + delay(20); + break; + case 0x6C: /* NEG */ + UNDOCUMENTED(8); + neg(A); + delay(8); + break; + case 0x6D: /* RETN */ + UNDOCUMENTED(8); + IFF1 = IFF2; + pop(PC); + delay(14); + break; + case 0x6E: /* IM 0 */ + UNDOCUMENTED(8); + IM = 0; + delay(8); + break; + case 0x6F: /* RLD */ + rld; + delay(18); + break; + + case 0x70: /* IN (C) */ + UNDOCUMENTED(8); + WZ = BC; + delay(12); + in(tmp1); + break; + case 0x71: /* OUT (C), 0 */ + UNDOCUMENTED(8); + delay(12); + output(BC, 0); + break; + case 0x72: /* SBC HL, SP */ + WZ = HL + 1; + sbc16(HLw, SPw); + delay(15); + break; + case 0x73: /* LD (nn), SP */ + WZ = readw(PC); + PC += 2; + writew(WZ++, SP); + delay(20); + break; + case 0x74: /* NEG */ + UNDOCUMENTED(8); + neg(A); + delay(8); + break; + case 0x75: /* RETN */ + UNDOCUMENTED(8); + IFF1 = IFF2; + pop(PC); + delay(14); + break; + case 0x76: /* IM 1 */ + UNDOCUMENTED(8); + IM = 1; + delay(8); + break; + + case 0x78: /* IN A, (C) */ + WZ = BC; + delay(12); + in(A); + break; + case 0x79: /* OUT (C), A */ + delay(12); + output(BC, A); + break; + case 0x7A: /* ADC HL, SP */ + WZ = HL + 1; + adc16(HLw, SPw); + delay(15); + break; + case 0x7B: /* LD SP, (nn) */ + WZ = readw(PC); + PC += 2; + SP = readw(WZ); + WZ++; + delay(20); + break; + case 0x7C: /* NEG */ + UNDOCUMENTED(8); + neg(A); + delay(8); + break; + case 0x7D: /* RETN */ + UNDOCUMENTED(8); + IFF1 = IFF2; + pop(PC); + delay(14); + break; + case 0x7E: /* IM 2 */ + UNDOCUMENTED(8); + IM = 2; + delay(8); + break; + + case 0xA0: /* LDI */ + ldi; + delay(16); + break; + case 0xA1: /* CPI */ + cpi; + delay(16); + break; + case 0xA2: /* INI */ + delay(13); + ini; + delay(3); + break; + case 0xA3: /* OUTI */ + delay(16); + outi; + break; + + case 0xA8: /* LDD */ + ldd; + delay(16); + break; + case 0xA9: /* CPD */ + cpd; + delay(16); + break; + case 0xAA: /* IND */ + delay(13); + ind; + delay(3); + break; + case 0xAB: /* OUTD */ + delay(16); + outd; + break; + + case 0xB0: /* LDIR */ + ldi; + if (BCw) { + PC -= 2; + delay(21); + } + else + delay(16); + break; + case 0xB1: /* CPIR */ + cpi; + if (BCw && !(F & FLAG_Z)) { + PC -= 2; + delay(21); + } + else + delay(16); + break; + case 0xB2: /* INIR */ + delay(13); + ini; + if (B) { + PC -= 2; + delay(8); + } + else + delay(3); + break; + case 0xB3: /* OTIR */ + delay(16); + outi; + if (B) { + PC -= 2; + delay(5); + } + break; + + case 0xB8: /* LDDR */ + ldd; + if (BCw) { + PC -= 2; + delay(21); + } + else + delay(16); + break; + case 0xB9: /* CPDR */ + cpd; + if (BCw && !(F & FLAG_Z)) { + PC -= 2; + delay(21); + } + else + delay(16); + break; + case 0xBA: /* INDR */ + delay(13); + ind; + if (B) { + PC -= 2; + delay(8); + } + else + delay(3); + break; + case 0xBB: /* OTDR */ + delay(16); + outd; + if (B) { + PC -= 2; + delay(5); + } + break; + + default: + delay(8); + if (calc->hw.z80_instr) + (*calc->hw.z80_instr)(calc, 0xed00 | op); + else if (calc->z80.emuflags & TILEM_Z80_BREAK_INVALID) + tilem_z80_stop(calc, TILEM_STOP_INVALID_INST); + break; + } diff --git a/tool/tilem-src/emu/z80main.h b/tool/tilem-src/emu/z80main.h new file mode 100644 index 0000000..26d83e7 --- /dev/null +++ b/tool/tilem-src/emu/z80main.h @@ -0,0 +1,893 @@ +/* + * libtilemcore - Graphing calculator emulation library + * + * Copyright (C) 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 + * . + */ + +switch (op) { + case 0x00: /* NOP */ + delay(4); + break; + case 0x01: /* LD BC, nn */ + BC = readw(PC); + PC += 2; + delay(10); + break; + case 0x02: /* LD (BC), A */ + W = A; + writeb(BC, A); + delay(7); + break; + case 0x03: /* INC BC */ + BC++; + delay(6); + break; + case 0x04: /* INC B */ + inc(B); + delay(4); + break; + case 0x05: /* DEC B */ + dec(B); + delay(4); + break; + case 0x06: /* LD B, n */ + B = readb(PC++); + delay(7); + break; + case 0x07: /* RLCA */ + rlca; + delay(4); + break; + + case 0x08: /* EX AF, AF' */ + ex(AF, AF2); + delay(4); + break; + case 0x09: /* ADD HL, BC */ + WZ = HL + 1; + add16(HLw, BCw); + delay(11); + break; + case 0x0A: /* LD A, (BC) */ + WZ = BC; + A = readb(WZ++); + delay(7); + break; + case 0x0B: /* DEC BC */ + BC--; + delay(6); + break; + case 0x0C: /* INC C */ + inc(C); + delay(4); + break; + case 0x0D: /* DEC C */ + dec(C); + delay(4); + break; + case 0x0E: /* LD C, n */ + C = readb(PC++); + delay(7); + break; + case 0x0F: /* RRCA */ + rrca; + delay(4); + break; + + case 0x10: /* DJNZ $+n */ + offs = (int) (signed char) readb(PC++); + B--; + if (B) { + WZ = PC + offs; + PC = WZ; + delay(13); + } + else + delay(8); + break; + case 0x11: /* LD DE, nn */ + DE = readw(PC); + PC += 2; + delay(10); + break; + case 0x12: /* LD (DE), A */ + W = A; + writeb(DE, A); + delay(7); + break; + case 0x13: /* INC DE */ + DE++; + delay(6); + break; + case 0x14: /* INC D */ + inc(D); + delay(4); + break; + case 0x15: /* DEC D */ + dec(D); + delay(4); + break; + case 0x16: /* LD D, n */ + D = readb(PC++); + delay(7); + break; + case 0x17: /* RLA */ + rla; + delay(4); + break; + + case 0x18: /* JR $+n */ + offs = (int) (signed char) readb(PC++); + WZ = PC + offs; + PC = WZ; + delay(12); + break; + case 0x19: /* ADD HL, DE */ + WZ = HL + 1; + add16(HLw, DEw); + delay(11); + break; + case 0x1A: /* LD A, (DE) */ + WZ = DE; + A = readb(WZ++); + delay(7); + break; + case 0x1B: /* DEC DE */ + DE--; + delay(6); + break; + case 0x1C: /* INC E */ + inc(E); + delay(4); + break; + case 0x1D: /* DEC E */ + dec(E); + delay(4); + break; + case 0x1E: /* LD E, n */ + E = readb(PC++); + delay(7); + break; + case 0x1F: /* RRA */ + rra; + delay(4); + break; + + case 0x20: /* JR NZ, $+n */ + offs = (int) (signed char) readb(PC++); + if (!(F & FLAG_Z)) { + WZ = PC + offs; + PC = WZ; + delay(12); + } + else + delay(7); + break; + case 0x21: /* LD HL, nn */ + HL = readw(PC); + PC += 2; + delay(10); + break; + case 0x22: /* LD (nn), HL */ + WZ = readw(PC); + PC += 2; + writew(WZ++, HL); + delay(16); + break; + case 0x23: /* INC HL */ + HL++; + delay(6); + break; + case 0x24: /* INC H */ + inc(H); + delay(4); + break; + case 0x25: /* DEC H */ + dec(H); + delay(4); + break; + case 0x26: /* LD H, n */ + H = readb(PC++); + delay(7); + break; + case 0x27: /* DAA */ + daa; + delay(4); + break; + + case 0x28: /* JR Z, $+n */ + offs = (int) (signed char) readb(PC++); + if (F & FLAG_Z) { + WZ = PC + offs; + PC = WZ; + delay(12); + } + else + delay(7); + break; + case 0x29: /* ADD HL, HL */ + WZ = HL + 1; + add16(HLw, HLw); + delay(11); + break; + case 0x2A: /* LD HL, (nn) */ + WZ = readw(PC); + PC += 2; + HL = readw(WZ++); + delay(16); + break; + case 0x2B: /* DEC HL */ + HL--; + delay(6); + break; + case 0x2C: /* INC L */ + inc(L); + delay(4); + break; + case 0x2D: /* DEC L */ + dec(L); + delay(4); + break; + case 0x2E: /* LD L,n */ + L = readb(PC++); + delay(7); + break; + case 0x2F: /* CPL */ + cpl(A); + delay(4); + break; + + case 0x30: /* JR NC, $+n */ + offs = (int) (signed char) readb(PC++); + if (!(F & FLAG_C)) { + WZ = PC + offs; + PC = WZ; + delay(12); + } + else + delay(7); + break; + case 0x31: /* LD SP, nn */ + SP = readw(PC); + PC += 2; + delay(10); + break; + case 0x32: /* LD (nn), A */ + tmp2 = readw(PC); + PC += 2; + writeb(tmp2, A); + W = A; /* is this really correct?! */ + delay(13); + break; + case 0x33: /* INC SP */ + SP++; + delay(6); + break; + case 0x34: /* INC (HL) */ + tmp1 = readb(HL); + inc(tmp1); + writeb(HL, tmp1); + delay(11); + break; + case 0x35: /* DEC (HL) */ + tmp1 = readb(HL); + dec(tmp1); + writeb(HL, tmp1); + delay(11); + break; + case 0x36: /* LD (HL), n */ + tmp1 = readb(PC++); + writeb(HL, tmp1); + delay(10); + break; + case 0x37: /* SCF */ + F |= FLAG_C; + delay(4); + break; + case 0x38: /* JR C, $+n */ + offs = (int) (signed char) readb(PC++); + if (F & FLAG_C) { + WZ = PC + offs; + PC = WZ; + delay(12); + } + else + delay(7); + break; + case 0x39: /* ADD HL, SP */ + WZ = HL + 1; + add16(HLw, SPw); + delay(11); + break; + case 0x3A: /* LD A, (nn) */ + WZ = readw(PC); + PC += 2; + A = readb(WZ++); + delay(13); + break; + case 0x3B: /* DEC SP */ + SP--; + delay(6); + break; + case 0x3C: /* INC A */ + inc(A); + delay(4); + break; + case 0x3D: /* DEC A */ + dec(A); + delay(4); + break; + case 0x3E: /* LD A, n */ + A = readb(PC++); + delay(7); + break; + case 0x3F: /* CCF */ + F ^= FLAG_C; + delay(4); + break; + + case 0x40: B = B; delay(4); break; + case 0x41: B = C; delay(4); break; + case 0x42: B = D; delay(4); break; + case 0x43: B = E; delay(4); break; + case 0x44: B = H; delay(4); break; + case 0x45: B = L; delay(4); break; + case 0x46: B = readb(HL); delay(7); break; + case 0x47: B = A; delay(4); break; + case 0x48: C = B; delay(4); break; + case 0x49: C = C; delay(4); break; + case 0x4A: C = D; delay(4); break; + case 0x4B: C = E; delay(4); break; + case 0x4C: C = H; delay(4); break; + case 0x4D: C = L; delay(4); break; + case 0x4E: C = readb(HL); delay(7); break; + case 0x4F: C = A; delay(4); break; + case 0x50: D = B; delay(4); break; + case 0x51: D = C; delay(4); break; + case 0x52: D = D; delay(4); break; + case 0x53: D = E; delay(4); break; + case 0x54: D = H; delay(4); break; + case 0x55: D = L; delay(4); break; + case 0x56: D = readb(HL); delay(7); break; + case 0x57: D = A; delay(4); break; + case 0x58: E = B; delay(4); break; + case 0x59: E = C; delay(4); break; + case 0x5A: E = D; delay(4); break; + case 0x5B: E = E; delay(4); break; + case 0x5C: E = H; delay(4); break; + case 0x5D: E = L; delay(4); break; + case 0x5E: E = readb(HL); delay(7); break; + case 0x5F: E = A; delay(4); break; + case 0x60: H = B; delay(4); break; + case 0x61: H = C; delay(4); break; + case 0x62: H = D; delay(4); break; + case 0x63: H = E; delay(4); break; + case 0x64: H = H; delay(4); break; + case 0x65: H = L; delay(4); break; + case 0x66: H = readb(HL); delay(7); break; + case 0x67: H = A; delay(4); break; + case 0x68: L = B; delay(4); break; + case 0x69: L = C; delay(4); break; + case 0x6A: L = D; delay(4); break; + case 0x6B: L = E; delay(4); break; + case 0x6C: L = H; delay(4); break; + case 0x6D: L = L; delay(4); break; + case 0x6E: L = readb(HL); delay(7); break; + case 0x6F: L = A; delay(4); break; + case 0x70: writeb(HL, B); delay(7); break; + case 0x71: writeb(HL, C); delay(7); break; + case 0x72: writeb(HL, D); delay(7); break; + case 0x73: writeb(HL, E); delay(7); break; + case 0x74: writeb(HL, H); delay(7); break; + case 0x75: writeb(HL, L); delay(7); break; + case 0x76: delay(4); break; + case 0x77: writeb(HL, A); delay(7); break; + case 0x78: A = B; delay(4); break; + case 0x79: A = C; delay(4); break; + case 0x7A: A = D; delay(4); break; + case 0x7B: A = E; delay(4); break; + case 0x7C: A = H; delay(4); break; + case 0x7D: A = L; delay(4); break; + case 0x7E: A = readb(HL); delay(7); break; + case 0x7F: A = A; delay(4); break; + + case 0x80: add8(A, B); delay(4); break; + case 0x81: add8(A, C); delay(4); break; + case 0x82: add8(A, D); delay(4); break; + case 0x83: add8(A, E); delay(4); break; + case 0x84: add8(A, H); delay(4); break; + case 0x85: add8(A, L); delay(4); break; + case 0x86: add8(A, readb(HL)); delay(7); break; + case 0x87: add8(A, A); delay(4); break; + case 0x88: adc8(A, B); delay(4); break; + case 0x89: adc8(A, C); delay(4); break; + case 0x8A: adc8(A, D); delay(4); break; + case 0x8B: adc8(A, E); delay(4); break; + case 0x8C: adc8(A, H); delay(4); break; + case 0x8D: adc8(A, L); delay(4); break; + case 0x8E: adc8(A, readb(HL)); delay(7); break; + case 0x8F: adc8(A, A); delay(4); break; + case 0x90: sub8(A, B); delay(4); break; + case 0x91: sub8(A, C); delay(4); break; + case 0x92: sub8(A, D); delay(4); break; + case 0x93: sub8(A, E); delay(4); break; + case 0x94: sub8(A, H); delay(4); break; + case 0x95: sub8(A, L); delay(4); break; + case 0x96: sub8(A, readb(HL)); delay(7); break; + case 0x97: sub8(A, A); delay(4); break; + case 0x98: sbc8(A, B); delay(4); break; + case 0x99: sbc8(A, C); delay(4); break; + case 0x9A: sbc8(A, D); delay(4); break; + case 0x9B: sbc8(A, E); delay(4); break; + case 0x9C: sbc8(A, H); delay(4); break; + case 0x9D: sbc8(A, L); delay(4); break; + case 0x9E: sbc8(A, readb(HL)); delay(7); break; + case 0x9F: sbc8(A, A); delay(4); break; + case 0xA0: and(A, B); delay(4); break; + case 0xA1: and(A, C); delay(4); break; + case 0xA2: and(A, D); delay(4); break; + case 0xA3: and(A, E); delay(4); break; + case 0xA4: and(A, H); delay(4); break; + case 0xA5: and(A, L); delay(4); break; + case 0xA6: and(A, readb(HL)); delay(7); break; + case 0xA7: and(A, A); delay(4); break; + case 0xA8: xor(A, B); delay(4); break; + case 0xA9: xor(A, C); delay(4); break; + case 0xAA: xor(A, D); delay(4); break; + case 0xAB: xor(A, E); delay(4); break; + case 0xAC: xor(A, H); delay(4); break; + case 0xAD: xor(A, L); delay(4); break; + case 0xAE: xor(A, readb(HL)); delay(7); break; + case 0xAF: xor(A, A); delay(4); break; + case 0xB0: or(A, B); delay(4); break; + case 0xB1: or(A, C); delay(4); break; + case 0xB2: or(A, D); delay(4); break; + case 0xB3: or(A, E); delay(4); break; + case 0xB4: or(A, H); delay(4); break; + case 0xB5: or(A, L); delay(4); break; + case 0xB6: or(A, readb(HL)); delay(7); break; + case 0xB7: or(A, A); delay(4); break; + case 0xB8: cp(A, B); delay(4); break; + case 0xB9: cp(A, C); delay(4); break; + case 0xBA: cp(A, D); delay(4); break; + case 0xBB: cp(A, E); delay(4); break; + case 0xBC: cp(A, H); delay(4); break; + case 0xBD: cp(A, L); delay(4); break; + case 0xBE: cp(A, readb(HL)); delay(7); break; + case 0xBF: cp(A, A); delay(4); break; + + case 0xC0: /* RET NZ */ + if (!(F & FLAG_Z)) { + pop(WZ); + PC = WZ; + delay(11); + } + else + delay(5); + break; + case 0xC1: /* POP BC */ + pop(BC); + delay(10); + break; + case 0xC2: /* JP NZ, nn */ + WZ = readw(PC); + if (!(F & FLAG_Z)) + PC = WZ; + else + PC += 2; + delay(10); + break; + case 0xC3: /* JP nn */ + WZ = readw(PC); + PC = WZ; + delay(10); + break; + case 0xC4: /* CALL NZ, nn */ + WZ = readw(PC); + PC += 2; + if (!(F & FLAG_Z)) { + push(PC); + PC = WZ; + delay(17); + } + else + delay(10); + break; + case 0xC5: /* PUSH BC */ + push(BC); + delay(11); + break; + case 0xC6: /* ADD A, n */ + add8(A, readb(PC++)); + delay(7); + break; + case 0xC7: /* RST 00h */ + /* FIXME: I have not tested whether RST affects WZ */ + push(PC); + PC = 0x0000; + delay(11); + break; + + case 0xC8: /* RET Z */ + if (F & FLAG_Z) { + pop(WZ); + PC = WZ; + delay(11); + } + else + delay(5); + break; + case 0xC9: /* RET */ + pop(WZ); + PC = WZ; + delay(10); + break; + case 0xCA: /* JP Z, nn */ + WZ = readw(PC); + if (F & FLAG_Z) + PC = WZ; + else + PC += 2; + delay(10); + break; + + case 0xCB: + op = readb_m1(PC++); + goto opcode_cb; + + case 0xCC: /* CALL Z, nn */ + WZ = readw(PC); + PC += 2; + if (F & FLAG_Z) { + push(PC); + PC = WZ; + delay(17); + } + else + delay(10); + break; + case 0xCD: /* CALL nn */ + WZ = readw(PC); + PC += 2; + push(PC); + PC = WZ; + delay(17); + break; + case 0xCE: /* ADC A, n */ + adc8(A, readb(PC++)); + delay(7); + break; + case 0xCF: /* RST 08h */ + push(PC); + PC = 0x0008; + delay(11); + break; + + case 0xD0: /* RET NC */ + if (!(F & FLAG_C)) { + pop(WZ); + PC = WZ; + delay(11); + } + else + delay(5); + break; + case 0xD1: /* POP DE */ + pop(DE); + delay(10); + break; + case 0xD2: /* JP NC, nn */ + WZ = readw(PC); + if (!(F & FLAG_C)) + PC = WZ; + else + PC += 2; + delay(10); + break; + case 0xD3: /* OUT (n), A */ + W = A; + Z = readb(PC++); + delay(11); + output(WZ, A); + break; + case 0xD4: /* CALL NC, nn */ + WZ = readw(PC); + PC += 2; + if (!(F & FLAG_C)) { + push(PC); + PC = WZ; + delay(17); + } + else + delay(10); + break; + case 0xD5: /* PUSH DE */ + push(DE); + delay(11); + break; + case 0xD6: /* SUB n */ + sub8(A, readb(PC++)); + delay(7); + break; + case 0xD7: /* RST 10h */ + push(PC); + PC = 0x0010; + delay(11); + break; + + case 0xD8: /* RET C */ + if (F & FLAG_C) { + pop(WZ); + PC = WZ; + delay(11); + } + else + delay(5); + break; + case 0xD9: /* EXX */ + ex(BC, BC2); + ex(DE, DE2); + ex(HL, HL2); + ex(WZ, WZ2); + delay(4); + break; + case 0xDA: /* JP C, nn */ + WZ = readw(PC); + if (F & FLAG_C) + PC = WZ; + else + PC += 2; + delay(10); + break; + case 0xDB: /* IN A, (n) */ + W = A; + Z = readb(PC++); + delay(11); + A = input(WZ); + break; + case 0xDC: /* CALL C, nn */ + WZ = readw(PC); + PC += 2; + if (F & FLAG_C) { + push(PC); + PC = WZ; + delay(17); + } + else + delay(10); + break; + + case 0xDD: + op = readb_m1(PC++); + delay(4); + goto opcode_dd; + + case 0xDE: /* SBC A, n */ + sbc8(A, readb(PC++)); + delay(7); + break; + case 0xDF: /* RST 18h */ + push(PC); + PC = 0x0018; + delay(11); + break; + + case 0xE0: /* RET PO */ + if (!(F & FLAG_P)) { + pop(WZ); + PC = WZ; + delay(11); + } + else + delay(5); + break; + case 0xE1: /* POP HL */ + pop(HL); + delay(10); + break; + case 0xE2: /* JP PO, nn */ + WZ = readw(PC); + if (!(F & FLAG_P)) + PC = WZ; + else + PC += 2; + delay(10); + break; + case 0xE3: /* EX (SP), HL */ + WZ = readw(SP); + writew(SP, HL); + HL = WZ; + delay(19); + break; + case 0xE4: /* CALL PO, nn */ + WZ = readw(PC); + PC += 2; + if (!(F & FLAG_P)) { + push(PC); + PC = WZ; + delay(17); + } + else + delay(10); + break; + case 0xE5: /* PUSH HL */ + push(HL); + delay(11); + break; + case 0xE6: /* AND n */ + and(A, readb(PC++)); + delay(7); + break; + case 0xE7: /* RST 20h */ + push(PC); + PC = 0x0020; + delay(11); + break; + + case 0xE8: /* RET PE */ + if (F & FLAG_P) { + pop(WZ); + PC = WZ; + delay(11); + } + else + delay(5); + break; + case 0xE9: /* JP HL */ + PC = HL; + delay(4); + break; + case 0xEA: /* JP PE, nn */ + WZ = readw(PC); + if (F & FLAG_P) + PC = WZ; + else + PC += 2; + delay(10); + break; + case 0xEB: /* EX DE,HL */ + ex(DE, HL); + delay(4); + break; + case 0xEC: /* CALL PE, nn */ + WZ = readw(PC); + PC += 2; + if (F & FLAG_P) { + push(PC); + PC = WZ; + delay(17); + } + else + delay(10); + break; + + case 0xED: + op = readb_m1(PC++); + goto opcode_ed; + + case 0xEE: /* XOR n */ + xor(A, readb(PC++)); + delay(7); + break; + case 0xEF: /* RST 28h */ + push(PC); + PC = 0x0028; + delay(11); + break; + + case 0xF0: /* RET P */ + if (!(F & FLAG_S)) { + pop(WZ); + PC = WZ; + delay(11); + } + else + delay(5); + break; + case 0xF1: /* POP AF */ + pop(AF); + delay(10); + break; + case 0xF2: /* JP P, nn */ + WZ = readw(PC); + if (!(F & FLAG_S)) + PC = WZ; + else + PC += 2; + delay(10); + break; + case 0xF3: /* DI */ + IFF1 = IFF2 = 0; + delay(4); + break; + case 0xF4: /* CALL P, nn */ + WZ = readw(PC); + PC += 2; + if (!(F & FLAG_S)) { + push(PC); + PC = WZ; + delay(17); + } + else + delay(10); + break; + case 0xF5: /* PUSH AF */ + push(AF); + delay(11); + break; + case 0xF6: /* OR n */ + or(A, readb(PC++)); + delay(7); + break; + case 0xF7: /* RST 30h */ + push(PC); + PC = 0x0030; + delay(11); + break; + + case 0xF8: /* RET M */ + if (F & FLAG_S) { + pop(WZ); + PC = WZ; + delay(11); + } + else + delay(5); + break; + case 0xF9: /* LD SP, HL */ + SP = HL; + delay(4); + break; + case 0xFA: /* JP M, nn */ + WZ = readw(PC); + if (F & FLAG_S) + PC = WZ; + else + PC += 2; + delay(10); + break; + case 0xFB: /* EI */ + IFF1 = IFF2 = 1; + delay(4); + break; + case 0xFC: /* CALL M, nn */ + WZ = readw(PC); + PC += 2; + if (F & FLAG_S) { + push(PC); + PC = WZ; + delay(17); + } + else + delay(10); + break; + + case 0xFD: + op = readb_m1(PC++); + delay(4); + goto opcode_fd; + + case 0xFE: /* CP n */ + cp(A, readb(PC++)); + delay(7); + break; + case 0xFF: /* RST 38h */ + push(PC); + PC = 0x0038; + delay(11); + break; + } diff --git a/tool/tilem-src/gui/Makefile.in b/tool/tilem-src/gui/Makefile.in new file mode 100644 index 0000000..4057e5b --- /dev/null +++ b/tool/tilem-src/gui/Makefile.in @@ -0,0 +1,263 @@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +bindir = @bindir@ +datadir = @datadir@ +pkgdatadir = @datadir@/tilem2 +mandir = @mandir@ + +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +@SET_MAKE@ + +CC = @CC@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +DEFS = @DEFS@ +GUI_LDFLAGS = @GUI_LDFLAGS@ +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +SHELL = @SHELL@ +WINDRES = @WINDRES@ + +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_LIBS = @GTK_LIBS@ + +TICALCS_CFLAGS = @TICALCS_CFLAGS@ +TICALCS_LIBS = @TICALCS_LIBS@ + +TILEMCORE_CFLAGS = -I$(top_srcdir)/emu +TILEMCORE_LIBS = -L$(top_builddir)/emu -ltilemcore + +TILEMDB_CFLAGS = -I$(top_srcdir)/db +TILEMDB_LIBS = -L$(top_builddir)/db -ltilemdb + +DEF_SHARE_DIR = -DSHARE_DIR=\"$(pkgdatadir)\" \ + -DUNINSTALLED_SHARE_DIR=\"$(top_srcdir)/data\" + +gui_extra_objects = @gui_extra_objects@ + +objects = tilem2.o \ + address.o \ + animatedgif.o \ + animation.o \ + breakpoints.o \ + config.o \ + charmap.o \ + debugger.o \ + disasmview.o \ + emulator.o \ + emucore.o \ + emuwin.o \ + event.o \ + filedlg.o \ + files.o \ + fixedtreeview.o \ + gifencod.o \ + icons.o \ + keybindings.o \ + keypaddlg.o \ + link.o \ + macro.o \ + memmodel.o \ + memview.o \ + memory.o \ + pbar.o \ + preferences.o \ + sendfile.o \ + screenshot.o \ + skinops.o \ + ti81prg.o \ + menu.o \ + rcvmenu.o \ + tool.o \ + $(gui_extra_objects) + +libs = $(TILEMDB_LIBS) $(TILEMCORE_LIBS) $(GTK_LIBS) $(TICALCS_LIBS) $(LIBS) + +compile = $(CC) -I$(top_builddir) -I$(srcdir) $(CFLAGS) $(CPPFLAGS) $(DEFS) \ + $(TILEMCORE_CFLAGS) $(TILEMDB_CFLAGS) \ + $(GTK_CFLAGS) $(TICALCS_CFLAGS) + +link = $(CC) $(CFLAGS) $(LDFLAGS) $(GUI_LDFLAGS) + +common_headers = ../config.h ../emu/tilem.h ../db/tilemdb.h \ + gui.h emulator.h debugger.h emuwin.h skinops.h animation.h \ + gtk-compat.h msgbox.h fixedtreeview.h + +all: tilem2@EXEEXT@ + +#Main emulator GUI +tilem2@EXEEXT@: $(objects) ../emu/libtilemcore.a + $(link) -o tilem2@EXEEXT@ $(objects) $(libs) + +tilem2.o: tilem2.c icons.h files.h $(common_headers) + $(compile) -c $(srcdir)/tilem2.c + +# Debugger +debugger.o: debugger.c disasmview.h $(common_headers) + $(compile) -c $(srcdir)/debugger.c + +# Disassembly view +disasmview.o: disasmview.c disasmview.h $(common_headers) + $(compile) -c $(srcdir)/disasmview.c + +# Memory view +memview.o: memview.c memmodel.h $(common_headers) + $(compile) -c $(srcdir)/memview.c + +# Tree model interface for calc memory +memmodel.o: memmodel.c memmodel.h $(common_headers) + $(compile) -c $(srcdir)/memmodel.c + +# Breakpoint dialog +breakpoints.o: breakpoints.c $(common_headers) + $(compile) -c $(srcdir)/breakpoints.c + +# Utility functions for debugging +address.o: address.c $(common_headers) + $(compile) -c $(srcdir)/address.c + +# Keypad dialog +keypaddlg.o: keypaddlg.c $(common_headers) + $(compile) -c $(srcdir)/keypaddlg.c + +# Memory management and messages +memory.o: memory.c ../emu/tilem.h + $(compile) -c $(srcdir)/memory.c + +# Emulator management +emulator.o: emulator.c emucore.h $(common_headers) + $(compile) -c $(srcdir)/emulator.c + +# Emulator main loop +emucore.o: emucore.c emucore.h $(common_headers) + $(compile) -c $(srcdir)/emucore.c + +# Emulator GUI (main window) +emuwin.o: emuwin.c $(common_headers) + $(compile) -c $(srcdir)/emuwin.c + +# Handle events +event.o: event.c $(common_headers) + $(compile) -c $(srcdir)/event.c + +# Preferences dialog +preferences.o: preferences.c $(common_headers) + $(compile) -c $(srcdir)/preferences.c + +# Open skin (skn format file) originally created by Julien Blache and Romain Lievins +skinops.o: skinops.c skinops.h + $(compile) -c $(srcdir)/skinops.c + +# Popups and other stuff +tool.o: tool.c $(common_headers) + $(compile) -c $(srcdir)/tool.c + +# Manage config.ini +config.o: config.c files.h $(common_headers) + $(compile) -c $(srcdir)/config.c + +# Handle internal link +link.o: link.c emucore.h ti81prg.h $(common_headers) + $(compile) -c $(srcdir)/link.c + +# Handle macro +macro.o: macro.c $(common_headers) + $(compile) -c $(srcdir)/macro.c + +# Create and modify animated gif +gifencod.o: gifencod.c gifencod.h + $(compile) -c $(srcdir)/gifencod.c + +# Handle screenshot anim (animated gif) +animatedgif.o: animatedgif.c $(common_headers) + $(compile) -c $(srcdir)/animatedgif.c + +# Screenshot widget +screenshot.o: screenshot.c $(common_headers) + $(compile) -c $(srcdir)/screenshot.c + +# Progress bar widget +pbar.o: pbar.c $(common_headers) + $(compile) -c $(srcdir)/pbar.c + +# Screenshot/animation recording +animation.o: animation.c $(common_headers) + $(compile) -c $(srcdir)/animation.c + + +# Shared/configuration files +files.o: files.c files.h + $(compile) $(DEF_SHARE_DIR) -c $(srcdir)/files.c + +# Custom icons +icons.o: icons.c icons.h + $(compile) $(DEF_SHARE_DIR) -c $(srcdir)/icons.c + +# Keybindings +keybindings.o: keybindings.c files.h $(common_headers) + $(compile) -c $(srcdir)/keybindings.c + +# Menu +menu.o: menu.c $(common_headers) + $(compile) -c $(srcdir)/menu.c + +# Link receive dialog +rcvmenu.o: rcvmenu.c $(common_headers) + $(compile) -c $(srcdir)/rcvmenu.c + +# Link send dialog +sendfile.o: sendfile.c emucore.h $(common_headers) + $(compile) -c $(srcdir)/sendfile.c + +# File open/save dialogs +filedlg.o: filedlg.c filedlg.h + $(compile) -c $(srcdir)/filedlg.c + +# Fixed-width tree view +fixedtreeview.o: fixedtreeview.c fixedtreeview.h + $(compile) -c $(srcdir)/fixedtreeview.c + +# TI-81 program file functions +ti81prg.o: ti81prg.c ti81prg.h ../emu/tilem.h + $(compile) -c $(srcdir)/ti81prg.c + +# Character conversion +charmap.o: charmap.c charmap.h ../emu/tilem.h + $(compile) -c $(srcdir)/charmap.c + +# Windows resource file +tilem2rc.o: tilem2.rc + major=`echo "@PACKAGE_VERSION@" | sed 's/\..*//'` ; \ + minor=`echo "@PACKAGE_VERSION@" | sed 's/.*\.//;s/[^0-9].*//'` ; \ + svnver=`svnversion "$(top_srcdir)" 2>/dev/null | sed 's/[^0-9].*//'` ; \ + [ -n "$$svnver" ] || svnver=0 ; \ + $(WINDRES) -DBUILD_VERSION=$$major,$$minor,0,$$svnver tilem2.rc tilem2rc.o + +tilem2.rc: tilem2.rc.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status gui/tilem2.rc + +install: tilem2@EXEEXT@ + $(INSTALL) -d -m 755 $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) -m 755 tilem2@EXEEXT@ $(DESTDIR)$(bindir) + +uninstall: + rm -f $(DESTDIR)$(bindir)/tilem2@EXEEXT@ + +clean: + rm -f *.o + rm -f tilem2@EXEEXT@ + +Makefile: Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status + +$(top_builddir)/config.status: $(top_srcdir)/configure + cd $(top_builddir) && $(SHELL) ./config.status --recheck + +.PRECIOUS: Makefile $(top_builddir)/config.status +.PHONY: all clean install uninstall diff --git a/tool/tilem-src/gui/address.c b/tool/tilem-src/gui/address.c new file mode 100644 index 0000000..997d6bc --- /dev/null +++ b/tool/tilem-src/gui/address.c @@ -0,0 +1,242 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" + +char * tilem_format_addr(TilemDebugger *dbg, dword addr, gboolean physical) +{ + dword page, addr_l; + + g_return_val_if_fail(dbg != NULL, NULL); + g_return_val_if_fail(dbg->emu != NULL, NULL); + g_return_val_if_fail(dbg->emu->calc != NULL, NULL); + + if (!physical) + return g_strdup_printf("%04X", addr); + + if (addr >= dbg->emu->calc->hw.romsize) + page = (((addr - dbg->emu->calc->hw.romsize) >> 14) + + dbg->emu->calc->hw.rampagemask); + else + page = addr >> 14; + + addr_l = (*dbg->emu->calc->hw.mem_ptol)(dbg->emu->calc, addr); + if (addr_l == 0xffffffff) + addr_l = (addr & 0x3fff) | 0x4000; + + return g_strdup_printf("%02X:%04X", page, addr_l); +} + +static gboolean parse_hex(const char *string, dword *value) +{ + const char *n; + char *e; + dword a; + + if (string[0] == '$') + n = string + 1; + else if (string[0] == '0' && (string[1] == 'x' || string[1] == 'X')) + n = string + 2; + else + n = string; + + a = strtol(n, &e, 16); + if (value) + *value = a; + + if (e == n) + return FALSE; + + if (*e == 'h' || *e == 'H') + e++; + + return (*e == 0); +} + +gboolean tilem_parse_paged_addr(TilemDebugger *dbg, const char *pagestr, + const char *offsstr, dword *value) +{ + dword page, offs; + + g_return_val_if_fail(dbg != NULL, FALSE); + g_return_val_if_fail(dbg->emu != NULL, FALSE); + g_return_val_if_fail(dbg->emu->calc != NULL, FALSE); + + if (!parse_hex(pagestr, &page)) + return FALSE; + if (!tilem_parse_addr(dbg, offsstr, &offs, NULL)) + return FALSE; + + offs &= 0x3fff; + if (page & dbg->emu->calc->hw.rampagemask) { + page &= ~dbg->emu->calc->hw.rampagemask; + offs += (offs << 14); + if (offs > dbg->emu->calc->hw.ramsize) + return FALSE; + offs += dbg->emu->calc->hw.romsize; + } + else { + offs += (page << 14); + if (offs > dbg->emu->calc->hw.romsize) + return FALSE; + } + + if (value) *value = offs; + return TRUE; +} + +gboolean tilem_parse_addr(TilemDebugger *dbg, const char *string, + dword *value, gboolean *physical) +{ + const char *offstr; + char *pagestr; + + g_return_val_if_fail(dbg != NULL, FALSE); + g_return_val_if_fail(dbg->emu != NULL, FALSE); + g_return_val_if_fail(dbg->emu->calc != NULL, FALSE); + + if (parse_hex(string, value)) { + if (physical) *physical = FALSE; + return TRUE; + } + + if (physical && (offstr = strchr(string, ':'))) { + pagestr = g_strndup(string, offstr - string); + offstr++; + if (tilem_parse_paged_addr(dbg, pagestr, offstr, value)) { + *physical = TRUE; + return TRUE; + } + } + + if (dbg->dasm && tilem_disasm_get_label(dbg->dasm, string, value)) { + if (physical) *physical = FALSE; + return TRUE; + } + + return FALSE; +} + +struct addrdlg { + GtkWidget *dlg; + TilemDebugger *dbg; + gboolean physical; +}; + +static void edited(GtkEntry *entry, gpointer data) +{ + struct addrdlg *adlg = data; + const char *text; + gboolean valid, phys; + + text = gtk_entry_get_text(entry); + valid = tilem_parse_addr(adlg->dbg, text, NULL, + adlg->physical ? &phys : NULL); + gtk_dialog_set_response_sensitive(GTK_DIALOG(adlg->dlg), + GTK_RESPONSE_OK, + valid); +} + +gboolean tilem_prompt_address(TilemDebugger *dbg, GtkWindow *parent, + const char *title, const char *prompt, + dword *value, gboolean physical, + gboolean usedefault) +{ + GtkWidget *dlg, *hbox, *vbox, *lbl, *ent; + struct addrdlg adlg; + const char *text; + gboolean phys; + char *s; + + g_return_val_if_fail(dbg != NULL, FALSE); + g_return_val_if_fail(dbg->emu != NULL, FALSE); + g_return_val_if_fail(dbg->emu->calc != NULL, FALSE); + + dlg = gtk_dialog_new_with_buttons(title, parent, GTK_DIALOG_MODAL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + gtk_dialog_set_default_response(GTK_DIALOG(dlg), + GTK_RESPONSE_OK); + + hbox = gtk_hbox_new(FALSE, 6); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 6); + + lbl = gtk_label_new(prompt); + gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0); + + ent = gtk_entry_new(); + gtk_entry_set_activates_default(GTK_ENTRY(ent), TRUE); + gtk_box_pack_start(GTK_BOX(hbox), ent, TRUE, TRUE, 0); + + if (usedefault) { + s = tilem_format_addr(dbg, *value, physical); + gtk_entry_set_text(GTK_ENTRY(ent), s); + g_free(s); + } + + adlg.dlg = dlg; + adlg.dbg = dbg; + adlg.physical = physical; + + g_signal_connect(ent, "changed", + G_CALLBACK(edited), &adlg); + edited(GTK_ENTRY(ent), &adlg); + + gtk_widget_show_all(hbox); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + if (gtk_dialog_run(GTK_DIALOG(dlg)) != GTK_RESPONSE_OK) { + gtk_widget_destroy(dlg); + return FALSE; + } + + text = gtk_entry_get_text(GTK_ENTRY(ent)); + if (!tilem_parse_addr(dbg, text, value, physical ? &phys : NULL)) { + gtk_widget_destroy(dlg); + return FALSE; + } + + if (physical && !phys) { + tilem_calc_emulator_lock(dbg->emu); + *value &= 0xffff; + *value = (*dbg->emu->calc->hw.mem_ltop)(dbg->emu->calc, *value); + tilem_calc_emulator_unlock(dbg->emu); + } + + gtk_widget_destroy(dlg); + return TRUE; +} diff --git a/tool/tilem-src/gui/animatedgif.c b/tool/tilem-src/gui/animatedgif.c new file mode 100644 index 0000000..a45ba6b --- /dev/null +++ b/tool/tilem-src/gui/animatedgif.c @@ -0,0 +1,251 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include "gui.h" + + +static void write_global_header(FILE* fp, int width, int height, byte* palette, int palette_size); +static void write_global_footer(FILE* fp); +static void write_extension_block(FILE* fout, word delay); +static void write_image_block_start(FILE *fp, int width, int height); +static void write_image_block_end(FILE *fp); +static void write_comment(FILE* fp); +static void write_application_extension(FILE * fp) ; + +static void write_global_header(FILE* fp, int width, int height, byte* palette, int palette_size) { + + /* Magic number for Gif file format */ + char global_header_magic_number[] = {'G', 'I', 'F', '8', '9', 'a'}; + /* Size of canvas width on 2 bytes, heigth on 2 bytes */ + char global_header_canvas[] = {96, 0, 64, 0 }; + + global_header_canvas[0] = width; + global_header_canvas[1] = (width >> 8) ; + global_header_canvas[2] = height; + global_header_canvas[3] = (height >> 8); + + /* Flag */ + /* The 11th byte is a set of flags : + bit 0: Global Color Table Flag (GCTF) + bit 1..3: Color Resolution + bit 4: Sort Flag to Global Color Table + bit 5..7: Size of Global Color Table: 2^(1+n) + It means "use the GCT wich is given after (from the size bit 5..7) and a resolution bit 1..3 + The Background color is an index in the Global Color Table + */ + /* FIXME : if we change the palette size, we need to change this flag too and I don't do this currently */ + char global_header_flag[] = { 0xf7 }; + /* The index in global color table */ + char global_header_background_index[] = {0x00}; + /* Aspect pixel ratio (unknown) */ + char global_header_aspect_pixel_ratio[] = {0x00}; + + + fwrite(global_header_magic_number, 6, 1, fp); + fwrite(global_header_canvas, 4, 1, fp); + fwrite(global_header_flag, 1, 1, fp); + fwrite(global_header_background_index, 1, 1, fp); + fwrite(global_header_aspect_pixel_ratio, 1, 1, fp); + + //byte* palette = tilem_color_palette_new_packed(255, 255, 255, 0, 0, 0, 2.2); + + fwrite(palette, palette_size * 3, 1, fp); +} + +static void write_global_footer(FILE* fp) { + + /* This value means end of gif file */ + char footer_trailer[1] = { 0x3b}; + + fwrite(footer_trailer, 1, 1,fp); +} + + +static void write_extension_block(FILE* fp, word delay) { + + /* Extension block introduced by 0x21 ('!'), size before extension_block_terminator, flag byte, delay (10/100) 2 bytes */ + char extension_block_header[2] = {0x21, 0xf9}; + /* Size before extension_block_terminator */ + char extension_block_size[1] = { 0x04} ; + /* Flag (unknown) */ + char extension_block_flag[1] = { 0x00} ; + /* Delay (x/100 sec) on 2 bytes*/ + char extension_block_delay[2] = {10, 0} ; + extension_block_delay[0] = delay; + /* The index designed by this variable become transparent even if palette gives a black(or something else) color. */ + char extension_block_transparent_index[1] = {0xff}; + /* End of extension block */ + char extension_block_terminator[1] = {0x00}; + + fwrite(extension_block_header, 2, 1, fp); + fwrite(extension_block_size, 1, 1, fp); + fwrite(extension_block_flag, 1, 1, fp); + fwrite(extension_block_delay, 2, 1, fp); + fwrite(extension_block_transparent_index, 1, 1, fp); + fwrite(extension_block_terminator, 1, 1, fp); + +} + +static void write_image_block_start(FILE *fp, int width, int height) { + + /* Header */ + char image_block_header[] = { 0x2c}; + /* Left corner x (2 bytes), left corner y (2 bytes), width (2 bytes), height (2 bytes) */ + char image_block_canvas[] = { 0, 0, 0, 0, 96, 0, 64, 0}; + + image_block_canvas[4] = width; + image_block_canvas[5] = (width >> 8) ; + image_block_canvas[6] = height; + image_block_canvas[7] = (height >> 8); + /* Flag */ + char image_block_flag[] = { 0x00 }; + + fwrite(image_block_header, 1, 1, fp); + fwrite(image_block_canvas, 8, 1, fp); + fwrite(image_block_flag, 1, 1, fp); + +} + +static void write_image_block_end(FILE *fp) { + + /* Give an end to the image block */ + char image_block_end[1] = {0x00}; + + fwrite(image_block_end, 1, 1,fp); +} + +static void write_comment(FILE* fp) { + + char comment[] = {0x21, 0xfe, 8, 'T', 'i', 'l', 'E', 'm', '2', 0, 0, 0}; + fwrite(comment, 12, 1, fp); +} + +static void write_application_extension(FILE * fp) { + + /* Magic number to start the block */ + char application_extension_magic_number[] = { 0x21, 0xff, 0x0b }; + /* Application name */ + char application_extension_application_name[] = { 'N', 'E', 'T', 'S', 'C', 'A', 'P', 'E', '2', '.', '0' }; + /* magic number */ + char application_extension_data_follow[] = { 0x03, 0x01 }; + /* 0 to 65535 loop */ + char application_extension_number_of_loop[] = { 0xff, 0xff}; + /* the end of the block */ + char application_extension_terminator[] = { 0x00 }; + + //char gif_infos[31] = { + //0x21, 0xff, 0x0b, 'N', 'E', 'T', 'S', 'C', 'A', 'P', 'E', '2', '.', '0', 3, 1, 0xff, 0xff, 0x00 }; + + //fwrite(gif_infos, 19, 1, fp); + fwrite(application_extension_magic_number, 3, 1, fp); + fwrite(application_extension_application_name, 11, 1, fp); + fwrite(application_extension_data_follow, 2, 1, fp); + fwrite(application_extension_number_of_loop, 2, 1, fp); + fwrite(application_extension_terminator, 1, 1, fp); +} + +/* Apparently, most current web browsers are seriously and + deliberately broken in their handling of animated GIFs. Internet + Explorer does not allow any frame to be shorter than 60 ms, and + Gecko does not allow any frame shorter than 20 ms. Furthermore, + rather than simply imposing a lower limit, or skipping frames, + these browsers take any frame they deem "too short" and extend it + to a full 100 ms out of sheer spite. + + If we want animations to look correct in all web browsers (which + is, after all, the main reason for using GIF animations in the + first place), we have to limit ourselves to 60-ms frames or + longer. */ +#define MIN_FRAME_DELAY 6 + +void tilem_animation_write_gif(TilemAnimation *anim, byte* palette, int palette_size, FILE *fp) +{ + GdkPixbufAnimation *ganim; + int width, height, delay, n; + gdouble time_stretch, t; + byte *image; + TilemAnimFrame *frm, *next; + gboolean is_static; + + g_return_if_fail(TILEM_IS_ANIMATION(anim)); + g_return_if_fail(fp != NULL); + + ganim = GDK_PIXBUF_ANIMATION(anim); + width = gdk_pixbuf_animation_get_width(ganim); + height = gdk_pixbuf_animation_get_height(ganim); + is_static = gdk_pixbuf_animation_is_static_image(ganim); + time_stretch = 1.0 / tilem_animation_get_speed(anim); + + frm = tilem_animation_next_frame(anim, NULL); + g_return_if_fail(frm != NULL); + + write_global_header(fp, width, height, palette, palette_size); + + if (!is_static) + write_application_extension(fp); + + write_comment(fp); + + t = MIN_FRAME_DELAY * 5.0; + + /* FIXME: combine multiple frames by averaging rather than + simply taking the last one */ + + while (frm) { + next = tilem_animation_next_frame(anim, frm); + + if (!is_static) { + delay = tilem_anim_frame_get_duration(frm); + t += delay * time_stretch; + n = t / 10.0; + + if (n < MIN_FRAME_DELAY && next != NULL) { + frm = next; + continue; + } + + t -= n * 10.0; + if (n > 0xffff) + n = 0xffff; + else if (n < MIN_FRAME_DELAY) + n = MIN_FRAME_DELAY; + write_extension_block(fp, n); + } + + tilem_animation_get_indexed_image(anim, frm, &image, + &width, &height); + write_image_block_start(fp, width, height); + GifEncode(fp, image, 8, width * height); + write_image_block_end(fp); + g_free(image); + + frm = next; + } + + write_global_footer(fp); +} diff --git a/tool/tilem-src/gui/animation.c b/tool/tilem-src/gui/animation.c new file mode 100644 index 0000000..f40e727 --- /dev/null +++ b/tool/tilem-src/gui/animation.c @@ -0,0 +1,594 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#define GDK_PIXBUF_ENABLE_BACKEND + +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" + +#define GAMMA 2.2 + +struct _TilemAnimFrame { + struct _TilemAnimFrame *next; + unsigned duration : 24; + unsigned contrast : 8; + byte data[1]; +}; + +struct _TilemAnimation { + GdkPixbufAnimation parent; + + int num_frames; + TilemAnimFrame *start; + TilemAnimFrame *end; + dword last_stamp; + + TilemLCDBuffer *temp_buffer; + + GdkPixbuf *static_pixbuf; + + int base_contrast; + int display_width; + int display_height; + int frame_rowstride; + int frame_size; + int image_width; + int image_height; + dword *palette; + gdouble speed; + gdouble time_stretch; + + gboolean out_of_memory; +}; + +struct _TilemAnimationClass { + GdkPixbufAnimationClass parent_class; +}; + +#define TILEM_TYPE_ANIM_ITER (tilem_anim_iter_get_type()) +#define TILEM_ANIM_ITER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TILEM_TYPE_ANIM_ITER, TilemAnimIter)) +#define TILEM_ANIM_ITER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls), TILEM_TYPE_ANIM_ITER, TilemAnimIterClass)) +#define TILEM_IS_ANIM_ITER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TILEM_TYPE_ANIM_ITER)) +#define TILEM_IS_ANIM_ITER_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE((cls), TILEM_TYPE_ANIM_ITER)) +#define TILEM_ANIM_ITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TILEM_TYPE_ANIM_ITER, TilemAnimIterClass)) + +typedef struct _TilemAnimIter { + GdkPixbufAnimationIter parent; + GTimeVal current_time; + int time_elapsed; + TilemAnimation *anim; + TilemAnimFrame *frame; + GdkPixbuf *pixbuf; +} TilemAnimIter; + +typedef struct _TilemAnimIterClass { + GdkPixbufAnimationIterClass parent_class; +} TilemAnimIterClass; + +G_DEFINE_TYPE(TilemAnimation, tilem_animation, + GDK_TYPE_PIXBUF_ANIMATION); + +G_DEFINE_TYPE(TilemAnimIter, tilem_anim_iter, + GDK_TYPE_PIXBUF_ANIMATION_ITER); + +static TilemAnimFrame * alloc_frame(int bufsize) +{ + TilemAnimFrame *frm; + + frm = g_try_malloc(sizeof(TilemAnimFrame) + bufsize - 1); + if (!frm) + return NULL; + frm->next = NULL; + return frm; +} + +static void free_frame(TilemAnimFrame *frm) +{ + g_free(frm); +} + +static int adjust_contrast(TilemAnimation *anim, int contrast) +{ + TilemAnimFrame *frm; + + if (!contrast) + return 0; + + if (!anim->base_contrast) { + for (frm = anim->start; frm; frm = frm->next) { + if (frm->contrast != 0) { + anim->base_contrast = frm->contrast; + break; + } + } + } + + contrast = (contrast - anim->base_contrast + 32); + return CLAMP(contrast, 0, 63); +} + +static void set_lcdbuf_from_frame(TilemAnimation *anim, + TilemLCDBuffer *buf, + const TilemAnimFrame *frm) +{ + buf->width = anim->display_width; + buf->height = anim->display_height; + buf->rowstride = anim->frame_rowstride; + buf->contrast = adjust_contrast(anim, frm->contrast); + buf->data = (byte *) frm->data; +} + +static GdkPixbuf * frame_to_pixbuf(TilemAnimation *anim, + const TilemAnimFrame *frm) +{ + GdkPixbuf *pb; + + pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, + anim->image_width, anim->image_height); + + set_lcdbuf_from_frame(anim, anim->temp_buffer, frm); + tilem_draw_lcd_image_rgb(anim->temp_buffer, + gdk_pixbuf_get_pixels(pb), + anim->image_width, + anim->image_height, + gdk_pixbuf_get_rowstride(pb), + 3, anim->palette, + TILEM_SCALE_SMOOTH); + anim->temp_buffer->data = NULL; + + return pb; +} + +static gboolean tilem_animation_is_static_image(GdkPixbufAnimation *ganim) +{ + TilemAnimation *anim = TILEM_ANIMATION(ganim); + + g_return_val_if_fail(TILEM_IS_ANIMATION(ganim), FALSE); + + if (anim->start == anim->end) + return TRUE; + else + return FALSE; +} + +static GdkPixbuf * tilem_animation_get_static_image(GdkPixbufAnimation *ganim) +{ + TilemAnimation *anim = TILEM_ANIMATION(ganim); + + g_return_val_if_fail(TILEM_IS_ANIMATION(anim), NULL); + g_return_val_if_fail(anim->start != NULL, NULL); + + if (!anim->static_pixbuf) + anim->static_pixbuf = frame_to_pixbuf(anim, anim->start); + + return anim->static_pixbuf; +} + +static void tilem_animation_get_size(GdkPixbufAnimation *ganim, + int *width, int *height) +{ + TilemAnimation *anim = TILEM_ANIMATION(ganim); + + g_return_if_fail(TILEM_IS_ANIMATION(anim)); + + if (width) *width = anim->image_width; + if (height) *height = anim->image_height; +} + +static GdkPixbufAnimationIter * +tilem_animation_get_iter(GdkPixbufAnimation *ganim, + const GTimeVal *start_time) +{ + TilemAnimation *anim = TILEM_ANIMATION(ganim); + TilemAnimIter *iter; + + g_return_val_if_fail(TILEM_IS_ANIMATION(anim), NULL); + + iter = g_object_new(TILEM_TYPE_ANIM_ITER, NULL); + iter->anim = anim; + iter->frame = anim->start; + iter->current_time = *start_time; + + g_object_ref(anim); + + return GDK_PIXBUF_ANIMATION_ITER(iter); +} + +static void tilem_animation_init(G_GNUC_UNUSED TilemAnimation *anim) +{ +} + +static void tilem_animation_finalize(GObject *obj) +{ + TilemAnimation *anim = TILEM_ANIMATION(obj); + TilemAnimFrame *frm; + + g_return_if_fail(TILEM_IS_ANIMATION(anim)); + + while (anim->start) { + frm = anim->start; + anim->start = frm->next; + free_frame(frm); + } + + anim->start = anim->end = NULL; + + if (anim->temp_buffer) + tilem_lcd_buffer_free(anim->temp_buffer); + anim->temp_buffer = NULL; + + if (anim->palette) + tilem_free(anim->palette); + anim->palette = NULL; + + if (anim->static_pixbuf) + g_object_unref(anim->static_pixbuf); + anim->static_pixbuf = NULL; + + if (G_OBJECT_CLASS(tilem_animation_parent_class)->finalize) + (*G_OBJECT_CLASS(tilem_animation_parent_class)->finalize)(obj); +} + +static void tilem_animation_class_init(TilemAnimationClass *klass) +{ + GdkPixbufAnimationClass *aclass = GDK_PIXBUF_ANIMATION_CLASS(klass); + GObjectClass *oclass = G_OBJECT_CLASS(klass); + + aclass->is_static_image = tilem_animation_is_static_image; + aclass->get_static_image = tilem_animation_get_static_image; + aclass->get_size = tilem_animation_get_size; + aclass->get_iter = tilem_animation_get_iter; + + oclass->finalize = tilem_animation_finalize; +} + +static int tilem_anim_iter_get_delay_time(GdkPixbufAnimationIter *giter) +{ + TilemAnimIter *iter = TILEM_ANIM_ITER(giter); + + g_return_val_if_fail(TILEM_IS_ANIM_ITER(iter), 0); + g_return_val_if_fail(iter->anim != NULL, 0); + g_return_val_if_fail(iter->frame != NULL, 0); + + if (iter->anim->start == iter->anim->end) + return -1; + else + return ((iter->frame->duration - iter->time_elapsed) + * iter->anim->time_stretch); +} + +static GdkPixbuf * tilem_anim_iter_get_pixbuf(GdkPixbufAnimationIter *giter) +{ + TilemAnimIter *iter = TILEM_ANIM_ITER(giter); + + g_return_val_if_fail(TILEM_IS_ANIM_ITER(iter), NULL); + g_return_val_if_fail(iter->anim != NULL, NULL); + g_return_val_if_fail(iter->frame != NULL, NULL); + + if (!iter->pixbuf) + iter->pixbuf = frame_to_pixbuf(iter->anim, iter->frame); + + return iter->pixbuf; +} + +static gboolean tilem_anim_iter_on_currently_loading_frame(G_GNUC_UNUSED GdkPixbufAnimationIter *giter) +{ + return FALSE; +} + +static gboolean +tilem_anim_iter_advance(GdkPixbufAnimationIter *giter, + const GTimeVal *current_time) +{ + TilemAnimIter *iter = TILEM_ANIM_ITER(giter); + int ms; + + g_return_val_if_fail(TILEM_IS_ANIM_ITER(iter), FALSE); + g_return_val_if_fail(iter->anim != NULL, FALSE); + g_return_val_if_fail(iter->frame != NULL, FALSE); + + ms = ((current_time->tv_usec - iter->current_time.tv_usec) / 1000 + + (current_time->tv_sec - iter->current_time.tv_sec) * 1000); + + g_time_val_add(&iter->current_time, ms * 1000); + + ms *= iter->anim->speed; + + ms += iter->time_elapsed; + if (ms < iter->frame->duration) { + iter->time_elapsed = ms; + return FALSE; + } + + if (iter->pixbuf) + g_object_unref(iter->pixbuf); + iter->pixbuf = NULL; + + while (ms >= iter->frame->duration) { + ms -= iter->frame->duration; + if (iter->frame->next) + iter->frame = iter->frame->next; + else + iter->frame = iter->anim->start; + } + + iter->time_elapsed = ms; + return TRUE; +} + +static void tilem_anim_iter_init(G_GNUC_UNUSED TilemAnimIter *iter) +{ +} + +static void tilem_anim_iter_finalize(GObject *obj) +{ + TilemAnimIter *iter = TILEM_ANIM_ITER(obj); + + g_return_if_fail(TILEM_IS_ANIM_ITER(obj)); + + if (iter->anim) + g_object_unref(iter->anim); + iter->anim = NULL; + + if (iter->pixbuf) + g_object_unref(iter->pixbuf); + iter->pixbuf = NULL; + + if (G_OBJECT_CLASS(tilem_anim_iter_parent_class)->finalize) + (*G_OBJECT_CLASS(tilem_anim_iter_parent_class)->finalize)(obj); +} + +static void tilem_anim_iter_class_init(TilemAnimIterClass *klass) +{ + GdkPixbufAnimationIterClass *iclass = GDK_PIXBUF_ANIMATION_ITER_CLASS(klass); + GObjectClass *oclass = G_OBJECT_CLASS(klass); + + iclass->get_delay_time = tilem_anim_iter_get_delay_time; + iclass->get_pixbuf = tilem_anim_iter_get_pixbuf; + iclass->on_currently_loading_frame = tilem_anim_iter_on_currently_loading_frame; + iclass->advance = tilem_anim_iter_advance; + + oclass->finalize = tilem_anim_iter_finalize; +} + +TilemAnimation * tilem_animation_new(int display_width, int display_height) +{ + TilemAnimation *anim; + TilemAnimFrame *dummy_frame; + + g_return_val_if_fail(display_width > 0, NULL); + g_return_val_if_fail(display_height > 0, NULL); + + anim = g_object_new(TILEM_TYPE_ANIMATION, NULL); + anim->display_width = display_width; + anim->display_height = display_height; + anim->frame_rowstride = (display_width + 7) & ~7; + anim->frame_size = anim->frame_rowstride * display_height; + + anim->image_width = display_width; + anim->image_height = display_height; + anim->speed = 1.0; + anim->time_stretch = 1.0; + + anim->temp_buffer = tilem_lcd_buffer_new(); + anim->palette = tilem_color_palette_new(255, 255, 255, 0, 0, 0, GAMMA); + + dummy_frame = alloc_frame(anim->frame_size); + dummy_frame->duration = 0; + dummy_frame->contrast = 0; + anim->start = anim->end = dummy_frame; + + return anim; +} + +gboolean tilem_animation_append_frame(TilemAnimation *anim, + const TilemLCDBuffer *buf, + int duration) +{ + TilemAnimFrame *frm; + + g_return_val_if_fail(TILEM_IS_ANIMATION(anim), FALSE); + g_return_val_if_fail(anim->end != NULL, FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(buf->data != NULL, FALSE); + g_return_val_if_fail(buf->height == anim->display_height, FALSE); + g_return_val_if_fail(buf->rowstride == anim->frame_rowstride, FALSE); + + if (anim->out_of_memory) + return FALSE; + + if (anim->end->contrast == buf->contrast + && (anim->last_stamp == buf->stamp + || !memcmp(anim->end->data, buf->data, anim->frame_size))) { + anim->end->duration += duration; + } + else { + if (anim->end->duration == 0) { + frm = anim->end; + } + else { + frm = alloc_frame(anim->frame_size); + if (!frm) { + anim->out_of_memory = TRUE; + return FALSE; + } + anim->end->next = frm; + anim->end = frm; + } + + frm->contrast = buf->contrast; + frm->duration = duration; + memcpy(frm->data, buf->data, anim->frame_size); + } + + anim->last_stamp = buf->stamp; + return TRUE; +} + +void tilem_animation_set_size(TilemAnimation *anim, int width, int height) +{ + g_return_if_fail(TILEM_IS_ANIMATION(anim)); + anim->image_width = width; + anim->image_height = height; +} + +void tilem_animation_set_colors(TilemAnimation *anim, + const GdkColor *foreground, + const GdkColor *background) +{ + g_return_if_fail(TILEM_IS_ANIMATION(anim)); + g_return_if_fail(foreground != NULL); + g_return_if_fail(background != NULL); + + if (anim->palette) + tilem_free(anim->palette); + + anim->palette = tilem_color_palette_new(background->red >> 8, + background->green >> 8, + background->blue >> 8, + foreground->red >> 8, + foreground->green >> 8, + foreground->blue >> 8, + GAMMA); +} + +void tilem_animation_set_speed(TilemAnimation *anim, gdouble factor) +{ + g_return_if_fail(TILEM_IS_ANIMATION(anim)); + g_return_if_fail(factor > 0.0); + anim->speed = factor; + anim->time_stretch = 1.0 / factor; +} + +gdouble tilem_animation_get_speed(TilemAnimation *anim) +{ + g_return_val_if_fail(TILEM_IS_ANIMATION(anim), 1.0); + return anim->speed; +} + +TilemAnimFrame *tilem_animation_next_frame(TilemAnimation *anim, + TilemAnimFrame *frm) +{ + g_return_val_if_fail(TILEM_IS_ANIMATION(anim), NULL); + if (frm) + return frm->next; + else + return anim->start; +} + +int tilem_anim_frame_get_duration(TilemAnimFrame *frm) +{ + g_return_val_if_fail(frm != NULL, 0); + return frm->duration; +} + +void tilem_animation_get_indexed_image(TilemAnimation *anim, + TilemAnimFrame *frm, + byte **buffer, + int *width, int *height) +{ + g_return_if_fail(TILEM_IS_ANIMATION(anim)); + g_return_if_fail(frm != NULL); + g_return_if_fail(buffer != NULL); + g_return_if_fail(width != NULL); + g_return_if_fail(height != NULL); + + *width = anim->image_width; + *height = anim->image_height; + *buffer = g_new(byte, anim->image_width * anim->image_height); + + set_lcdbuf_from_frame(anim, anim->temp_buffer, frm); + tilem_draw_lcd_image_indexed(anim->temp_buffer, *buffer, + anim->image_width, anim->image_height, + anim->image_width, + TILEM_SCALE_SMOOTH); + anim->temp_buffer->data = NULL; +} + +gboolean tilem_animation_save(TilemAnimation *anim, + const char *fname, const char *type, + char **option_keys, char **option_values, + GError **err) +{ + FILE *fp; + char *dname; + int errnum; + GdkPixbuf *pb; + gboolean status; + byte palette[768]; + int i; + + g_return_val_if_fail(TILEM_IS_ANIMATION(anim), FALSE); + g_return_val_if_fail(fname != NULL, FALSE); + g_return_val_if_fail(type != NULL, FALSE); + g_return_val_if_fail(err == NULL || *err == NULL, FALSE); + + if (strcmp(type, "gif") != 0) { + pb = gdk_pixbuf_animation_get_static_image + (GDK_PIXBUF_ANIMATION(anim)); + status = gdk_pixbuf_savev(pb, fname, type, + option_keys, option_values, + err); + return status; + } + + fp = g_fopen(fname, "wb"); + if (!fp) { + errnum = errno; + dname = g_filename_display_name(fname); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Failed to open '%s' for writing: %s", + dname, g_strerror(errnum)); + g_free(dname); + return FALSE; + } + + for (i = 0; i < 256; i++) { + palette[3 * i] = anim->palette[i] >> 16; + palette[3 * i + 1] = anim->palette[i] >> 8; + palette[3 * i + 2] = anim->palette[i]; + } + + tilem_animation_write_gif(anim, palette, 256, fp); + + if (fclose(fp)) { + errnum = errno; + dname = g_filename_display_name(fname); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Error while closing '%s': %s", + dname, g_strerror(errnum)); + g_free(dname); + return FALSE; + } + + return TRUE; +} diff --git a/tool/tilem-src/gui/animation.h b/tool/tilem-src/gui/animation.h new file mode 100644 index 0000000..ec14391 --- /dev/null +++ b/tool/tilem-src/gui/animation.h @@ -0,0 +1,88 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +G_BEGIN_DECLS + +#define TILEM_TYPE_ANIMATION (tilem_animation_get_type()) +#define TILEM_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TILEM_TYPE_ANIMATION, TilemAnimation)) +#define TILEM_ANIMATION_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls), TILEM_TYPE_ANIMATION, TilemAnimationClass)) +#define TILEM_IS_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TILEM_TYPE_ANIMATION)) +#define TILEM_IS_ANIMATION_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE((cls), TILEM_TYPE_ANIMATION)) +#define TILEM_ANIMATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TILEM_TYPE_ANIMATION, TilemAnimationClass)) + +typedef struct _TilemAnimation TilemAnimation; +typedef struct _TilemAnimationClass TilemAnimationClass; +typedef struct _TilemAnimFrame TilemAnimFrame; + +GType tilem_animation_get_type(void) G_GNUC_CONST; + +/* Create a new TilemAnimation for the given display dimensions. */ +TilemAnimation * tilem_animation_new(int display_width, + int display_height); + +/* Add a frame to the animation. BUF holds the LCD contents, DURATION + is the length of time this frame should be displayed (in + milliseconds.) */ +gboolean tilem_animation_append_frame(TilemAnimation *anim, + const TilemLCDBuffer *buf, + int duration); + +/* Set output image size. */ +void tilem_animation_set_size(TilemAnimation *anim, int width, int height); + +/* Set output image colors. */ +void tilem_animation_set_colors(TilemAnimation *anim, + const GdkColor *foreground, + const GdkColor *background); + +/* Set animation speed factor */ +void tilem_animation_set_speed(TilemAnimation *anim, gdouble factor); + +/* Get animation speed factor */ +gdouble tilem_animation_get_speed(TilemAnimation *anim); + +/* Retrieve the next frame of the animation. If FRM is NULL, retrieve + the first frame. If FRM is non-null, it must be a frame belonging + to this animation. */ +TilemAnimFrame *tilem_animation_next_frame(TilemAnimation *anim, + TilemAnimFrame *frm); + +/* Get the duration of this frame (milliseconds by the original + clock.) */ +int tilem_anim_frame_get_duration(TilemAnimFrame *frm); + +/* Convert frame to an indexed-color image buffer. FRM must be a + frame belonging to this animation. The returned buffer must be + freed with g_free(). */ +void tilem_animation_get_indexed_image(TilemAnimation *anim, + TilemAnimFrame *frm, + byte **buffer, + int *width, int *height); + +/* Save animation to a file. TYPE is an ASCII string describing the + type. Options are specified by OPTION_KEYS and OPTION_VALUES (see + gdk_pixbuf_savev().) */ +gboolean tilem_animation_save(TilemAnimation *anim, + const char *fname, const char *type, + char **option_keys, char **option_values, + GError **err); + +G_END_DECLS diff --git a/tool/tilem-src/gui/breakpoints.c b/tool/tilem-src/gui/breakpoints.c new file mode 100644 index 0000000..e1f9e5c --- /dev/null +++ b/tool/tilem-src/gui/breakpoints.c @@ -0,0 +1,1060 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "disasmview.h" +#include "fixedtreeview.h" + +/**************** Add/edit breakpoint dialog ****************/ + +struct hex_entry { + GtkWidget *addr_label; + GtkWidget *addr_entry; + GtkWidget *page_label; + GtkWidget *page_entry; +}; + +struct breakpoint_dlg { + TilemDebugger *dbg; + + GtkWidget *dlg; + GtkWidget *box; + + GtkWidget *type_combo; + GtkWidget *access_cb[3]; + GtkWidget *single_rb; + GtkWidget *range_rb; + GtkWidget *access_label; + GtkWidget *address_label; + + struct hex_entry start; + struct hex_entry end; +}; + +static const struct { + char abbrev; + const char *desc; + const char *value_label; + int use_pages; + guint access_mask; +} type_info[] = { + { 'M', "Memory address (logical)", "Address", 0, 7 }, + { 'M', "Memory address (absolute)", "Address", 1, 7 }, + { 'P', "I/O port", "Port Number", 0, 6 }, + { 'I', "Z80 instruction", "Opcode", 0, 0 } +}; + +/* Determine currently selected address type */ +static guint get_bp_type(struct breakpoint_dlg *bpdlg) +{ + int i = gtk_combo_box_get_active(GTK_COMBO_BOX(bpdlg->type_combo)); + return (i < 0 ? 0 : i); +} + +/* Format address as a string */ +static void hex_entry_set_value(struct hex_entry *he, TilemDebugger *dbg, + int type, dword value) +{ + const TilemCalc *calc; + char buf[20]; + unsigned int page; + + g_return_if_fail(dbg->emu != NULL); + g_return_if_fail(dbg->emu->calc != NULL); + + calc = dbg->emu->calc; + + switch (type) { + case TILEM_DB_BREAK_LOGICAL: + g_snprintf(buf, sizeof(buf), "%04X", value); + break; + + case TILEM_DB_BREAK_PHYSICAL: + if (value >= calc->hw.romsize) { + value -= calc->hw.romsize; + page = (value >> 14) + calc->hw.rampagemask; + } + else { + page = (value >> 14); + } + + g_snprintf(buf, sizeof(buf), "%02X", page); + gtk_entry_set_text(GTK_ENTRY(he->page_entry), buf); + + g_snprintf(buf, sizeof(buf), "%04X", value & 0x3fff); + break; + + case TILEM_DB_BREAK_PORT: + g_snprintf(buf, sizeof(buf), "%02X", value); + break; + + case TILEM_DB_BREAK_OPCODE: + if (value < 0x100) + g_snprintf(buf, sizeof(buf), "%02X", value); + else if (value < 0x10000) + g_snprintf(buf, sizeof(buf), "%04X", value); + else if (value < 0x1000000) + g_snprintf(buf, sizeof(buf), "%06X", value); + else + g_snprintf(buf, sizeof(buf), "%08X", value); + break; + + default: + g_return_if_reached(); + } + + gtk_entry_set_text(GTK_ENTRY(he->addr_entry), buf); +} + +/* Parse contents of entry */ +static gboolean parse_num(TilemDebugger *dbg, const char *s, dword *a) +{ + const char *n; + char *e; + + g_return_val_if_fail(s != NULL, FALSE); + + if (s[0] == '$') + n = s + 1; + else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) + n = s + 2; + else + n = s; + + *a = strtol(n, &e, 16); + if (e != n) { + if (*e == 'h' || *e == 'H') + e++; + if (*e == 0) + return TRUE; + } + + if (dbg->dasm && tilem_disasm_get_label(dbg->dasm, s, a)) + return TRUE; + + return FALSE; +} + +/* Parse user input from hex entry */ +static gboolean hex_entry_parse_value(struct hex_entry *he, TilemDebugger *dbg, + int type, dword *value) +{ + const TilemCalc *calc = dbg->emu->calc; + dword page; + const char *s; + + g_return_val_if_fail(calc != NULL, 0); + + s = gtk_entry_get_text(GTK_ENTRY(he->addr_entry)); + if (!parse_num(dbg, s, value)) + return FALSE; + + if (type != TILEM_DB_BREAK_PHYSICAL) + return TRUE; + + s = gtk_entry_get_text(GTK_ENTRY(he->page_entry)); + if (!parse_num(dbg, s, &page)) + return FALSE; + + *value &= 0x3fff; + + if (page >= calc->hw.rampagemask) { + *value += ((page - calc->hw.rampagemask) << 14); + *value %= calc->hw.ramsize; + *value += calc->hw.romsize; + } + else { + *value += (page << 14); + *value %= calc->hw.romsize; + } + + return TRUE; +} + +/* Parse input fields and check if they make sense */ +static gboolean parse_input(struct breakpoint_dlg *bpdlg, + TilemDebugBreakpoint *bp) +{ + int i; + dword addr0, addr1; + + bp->mask = bp->start = bp->end = 0xffffffff; + bp->type = get_bp_type(bpdlg); + bp->mode = 0; + + for (i = 0; i < 3; i++) + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bpdlg->access_cb[i]))) + bp->mode += (1 << i); + + bp->mode &= type_info[bp->type].access_mask; + if (bp->type == TILEM_DB_BREAK_OPCODE) + bp->mode = TILEM_DB_BREAK_EXEC; + else if (bp->mode == 0) + return FALSE; + + if (!hex_entry_parse_value(&bpdlg->start, bpdlg->dbg, + bp->type, &addr0)) + return FALSE; + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bpdlg->range_rb))) { + if (!hex_entry_parse_value(&bpdlg->end, bpdlg->dbg, + bp->type, &addr1)) + return FALSE; + } + else { + addr1 = addr0; + } + + if (bp->type == TILEM_DB_BREAK_LOGICAL) + bp->mask = 0xffff; + else if (bp->type == TILEM_DB_BREAK_PORT) + bp->mask = 0xff; + else + bp->mask = 0xffffffff; + + bp->start = addr0 & bp->mask; + bp->end = addr1 & bp->mask; + if (bp->end < bp->start) + return FALSE; + + return TRUE; +} + +/* Check if input fields are valid, and enable/disable OK response as + appropriate */ +static void validate(struct breakpoint_dlg *bpdlg) +{ + TilemDebugBreakpoint tmpbp; + + if (parse_input(bpdlg, &tmpbp)) + gtk_dialog_set_response_sensitive(GTK_DIALOG(bpdlg->dlg), + GTK_RESPONSE_OK, TRUE); + else + gtk_dialog_set_response_sensitive(GTK_DIALOG(bpdlg->dlg), + GTK_RESPONSE_OK, FALSE); +} + +/* Enable/disable check buttons for access mode */ +static void set_access_mask(struct breakpoint_dlg *bpdlg, guint mask) +{ + int i; + + if (mask) + gtk_widget_show(bpdlg->access_label); + else + gtk_widget_hide(bpdlg->access_label); + + for (i = 0; i < 3; i++) { + if (mask & (1 << i)) + gtk_widget_show(bpdlg->access_cb[i]); + else + gtk_widget_hide(bpdlg->access_cb[i]); + } +} + +/* Combo box changed */ +static void addr_type_changed(G_GNUC_UNUSED GtkComboBox *combo, gpointer data) +{ + struct breakpoint_dlg *bpdlg = data; + int type = get_bp_type(bpdlg); + gboolean range = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bpdlg->range_rb)); + char *s; + + s = g_strdup_printf("%s", type_info[type].value_label); + gtk_label_set_markup(GTK_LABEL(bpdlg->address_label), s); + g_free(s); + + set_access_mask(bpdlg, type_info[type].access_mask); + + if (type_info[type].use_pages) { + gtk_widget_show(bpdlg->start.page_label); + gtk_widget_show(bpdlg->start.page_entry); + } + else { + gtk_widget_hide(bpdlg->start.page_label); + gtk_widget_hide(bpdlg->start.page_entry); + } + + if (range) { + gtk_label_set_text_with_mnemonic(GTK_LABEL(bpdlg->start.addr_label), + "_Start:"); + gtk_widget_show(bpdlg->end.addr_label); + gtk_widget_show(bpdlg->end.addr_entry); + } + else { + gtk_label_set_text_with_mnemonic(GTK_LABEL(bpdlg->start.addr_label), + "_Value:"); + gtk_widget_hide(bpdlg->end.addr_label); + gtk_widget_hide(bpdlg->end.addr_entry); + } + + if (type_info[type].use_pages && range) { + gtk_widget_show(bpdlg->end.page_label); + gtk_widget_show(bpdlg->end.page_entry); + } + else { + gtk_widget_hide(bpdlg->end.page_label); + gtk_widget_hide(bpdlg->end.page_entry); + } + + validate(bpdlg); +} + +/* Access mode changed */ +static void access_changed(G_GNUC_UNUSED GtkToggleButton *tb, gpointer data) +{ + struct breakpoint_dlg *bpdlg = data; + validate(bpdlg); +} + +/* Single/range mode changed */ +static void range_mode_changed(G_GNUC_UNUSED GtkToggleButton *tb, gpointer data) +{ + struct breakpoint_dlg *bpdlg = data; + addr_type_changed(NULL, bpdlg); +} + +/* Text of entry changed */ +static void entry_edited(G_GNUC_UNUSED GtkEntry *entry, + gpointer data) +{ + struct breakpoint_dlg *bpdlg = data; + validate(bpdlg); +} + +/* Key presssed in entry */ +static gboolean entry_key_event(G_GNUC_UNUSED GtkWidget *entry, + GdkEventKey *ev, gpointer data) +{ + struct breakpoint_dlg *bpdlg = data; + TilemDebugBreakpoint tmpbp; + + if (ev->state & GDK_MODIFIER_MASK) + return FALSE; + + if (ev->keyval != GDK_Return + && ev->keyval != GDK_KP_Enter + && ev->keyval != GDK_ISO_Enter) + return FALSE; + + if (parse_input(bpdlg, &tmpbp)) + gtk_dialog_response(GTK_DIALOG(bpdlg->dlg), GTK_RESPONSE_OK); + else + gtk_widget_child_focus(bpdlg->box, GTK_DIR_TAB_FORWARD); + + return TRUE; +} + +static void init_hex_entry(struct breakpoint_dlg *bpdlg, + struct hex_entry *he, const char *label, + GtkTable *tbl, int ypos) +{ + GtkWidget *align, *lbl; + + he->addr_label = lbl = gtk_label_new_with_mnemonic(label); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + align = gtk_alignment_new(0.5, 0.5, 1.0, 1.0); + gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 6); + gtk_container_add(GTK_CONTAINER(align), lbl); + gtk_table_attach(tbl, align, 0, 1, ypos, ypos + 1, + GTK_FILL, GTK_FILL, 0, 0); + + he->addr_entry = gtk_entry_new(); + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), he->addr_entry); + gtk_table_attach(tbl, he->addr_entry, 1, 2, ypos, ypos + 1, + GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + + g_signal_connect(he->addr_entry, "changed", + G_CALLBACK(entry_edited), bpdlg); + g_signal_connect(he->addr_entry, "key-press-event", + G_CALLBACK(entry_key_event), bpdlg); + + he->page_label = lbl = gtk_label_new("Page:"); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(tbl, lbl, 2, 3, ypos, ypos + 1, + GTK_FILL, GTK_FILL, 6, 0); + + he->page_entry = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(he->page_entry), 5); + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), he->page_entry); + gtk_table_attach(tbl, he->page_entry, 3, 4, ypos, ypos + 1, + GTK_FILL, GTK_FILL, 0, 0); + + g_signal_connect(he->page_entry, "changed", + G_CALLBACK(entry_edited), bpdlg); + g_signal_connect(he->page_entry, "key-press-event", + G_CALLBACK(entry_key_event), bpdlg); +} + +static gboolean edit_breakpoint(TilemDebugger *dbg, + GtkWindow *parent_window, + const char *title, + TilemDebugBreakpoint *bp, + gboolean edit_existing) +{ + GtkWidget *dlg, *vbox, *frame, *tbl, *hbox, *lbl, *combo, *cb, *rb; + struct breakpoint_dlg bpdlg; + gsize i; + + g_return_val_if_fail(bp != NULL, FALSE); + g_return_val_if_fail(dbg != NULL, FALSE); + g_return_val_if_fail(dbg->emu != NULL, FALSE); + g_return_val_if_fail(dbg->emu->calc != NULL, FALSE); + + bpdlg.dbg = dbg; + + dlg = gtk_dialog_new_with_buttons(title, parent_window, + GTK_DIALOG_MODAL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + gtk_dialog_set_default_response(GTK_DIALOG(dlg), + GTK_RESPONSE_OK); + + bpdlg.dlg = dlg; + + bpdlg.box = gtk_vbox_new(FALSE, 6); + gtk_container_set_border_width(GTK_CONTAINER(bpdlg.box), 6); + + tbl = gtk_table_new(2, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(tbl), 6); + gtk_table_set_col_spacings(GTK_TABLE(tbl), 6); + + /* Breakpoint type */ + + lbl = gtk_label_new_with_mnemonic("Breakpoint _type:"); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(GTK_TABLE(tbl), lbl, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + + combo = gtk_combo_box_new_text(); + for (i = 0; i < G_N_ELEMENTS(type_info); i++) + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), type_info[i].desc); + + bpdlg.type_combo = combo; + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), combo); + gtk_table_attach(GTK_TABLE(tbl), combo, 1, 2, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), bp->type); + + /* Access mode */ + + bpdlg.access_label = lbl = gtk_label_new("Break when:"); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(GTK_TABLE(tbl), lbl, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + + hbox = gtk_hbox_new(FALSE, 6); + + cb = gtk_check_button_new_with_mnemonic("_Reading"); + gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0); + bpdlg.access_cb[2] = cb; + + cb = gtk_check_button_new_with_mnemonic("_Writing"); + gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0); + bpdlg.access_cb[1] = cb; + + cb = gtk_check_button_new_with_mnemonic("E_xecuting"); + gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0); + bpdlg.access_cb[0] = cb; + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bpdlg.access_cb[0]), + bp->mode & 1); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bpdlg.access_cb[1]), + bp->mode & 2); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bpdlg.access_cb[2]), + bp->mode & 4); + + gtk_table_attach(GTK_TABLE(tbl), hbox, 1, 2, 1, 2, + GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + + frame = new_frame("Breakpoint Condition", tbl); + gtk_box_pack_start(GTK_BOX(bpdlg.box), frame, FALSE, FALSE, 0); + + /* Addresses */ + + tbl = gtk_table_new(3, 4, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(tbl), 6); + + hbox = gtk_hbox_new(FALSE, 6); + + rb = gtk_radio_button_new_with_mnemonic(NULL, "Si_ngle"); + gtk_box_pack_start(GTK_BOX(hbox), rb, FALSE, FALSE, 0); + bpdlg.single_rb = rb; + + rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(rb), "R_ange"); + gtk_box_pack_start(GTK_BOX(hbox), rb, FALSE, FALSE, 0); + bpdlg.range_rb = rb; + + if (edit_existing && bp->end != bp->start) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rb), TRUE); + + gtk_table_attach(GTK_TABLE(tbl), hbox, 0, 2, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + + init_hex_entry(&bpdlg, &bpdlg.start, "S_tart:", GTK_TABLE(tbl), 1); + init_hex_entry(&bpdlg, &bpdlg.end, "_End:", GTK_TABLE(tbl), 2); + + frame = new_frame("Address", tbl); + bpdlg.address_label = gtk_frame_get_label_widget(GTK_FRAME(frame)); + gtk_box_pack_start(GTK_BOX(bpdlg.box), frame, FALSE, FALSE, 0); + gtk_widget_show_all(bpdlg.box); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); + gtk_box_pack_start(GTK_BOX(vbox), bpdlg.box, FALSE, FALSE, 0); + + if (edit_existing) { + hex_entry_set_value(&bpdlg.start, dbg, bp->type, bp->start); + hex_entry_set_value(&bpdlg.end, dbg, bp->type, bp->end); + } + + g_signal_connect(combo, "changed", + G_CALLBACK(addr_type_changed), &bpdlg); + g_signal_connect(bpdlg.access_cb[0], "toggled", + G_CALLBACK(access_changed), &bpdlg); + g_signal_connect(bpdlg.access_cb[1], "toggled", + G_CALLBACK(access_changed), &bpdlg); + g_signal_connect(bpdlg.access_cb[2], "toggled", + G_CALLBACK(access_changed), &bpdlg); + g_signal_connect(bpdlg.single_rb, "toggled", + G_CALLBACK(range_mode_changed), &bpdlg); + + addr_type_changed(NULL, &bpdlg); + + gtk_widget_grab_focus(bpdlg.start.addr_entry); + + do { + if (gtk_dialog_run(GTK_DIALOG(dlg)) != GTK_RESPONSE_OK) { + gtk_widget_destroy(dlg); + return FALSE; + } + } while (!parse_input(&bpdlg, bp)); + + gtk_widget_destroy(dlg); + return TRUE; +} + +/**************** Breakpoint list dialog ****************/ + +enum { + COL_BP, + COL_START, + COL_END, + COL_TYPE_STR, + COL_START_STR, + COL_END_STR, + COL_ENABLED, + N_COLUMNS +}; + +struct bplist_dlg { + TilemDebugger *dbg; + GtkWidget *dlg; + GtkListStore *store; + GtkWidget *treeview; + GtkWidget *remove_btn; + GtkWidget *edit_btn; + GtkWidget *clear_btn; +}; + +/* Convert address into a displayable string */ +static void format_address(TilemDebugger *dbg, + char *buf, int bufsize, + int type, dword value) +{ + const TilemCalc *calc; + unsigned int page; + + g_return_if_fail(dbg->emu != NULL); + g_return_if_fail(dbg->emu->calc != NULL); + + calc = dbg->emu->calc; + + switch (type) { + case TILEM_DB_BREAK_LOGICAL: + g_snprintf(buf, bufsize, "%04X", value); + break; + + case TILEM_DB_BREAK_PHYSICAL: + if (value >= calc->hw.romsize) { + value -= calc->hw.romsize; + page = (value >> 14) + calc->hw.rampagemask; + } + else { + page = (value >> 14); + } + + g_snprintf(buf, bufsize, "%02X:%04X", page, value & 0x3fff); + break; + + case TILEM_DB_BREAK_PORT: + g_snprintf(buf, bufsize, "%02X", value); + break; + + case TILEM_DB_BREAK_OPCODE: + if (value < 0x100) + g_snprintf(buf, bufsize, "%02X", value); + else if (value < 0x10000) + g_snprintf(buf, bufsize, "%04X", value); + else if (value < 0x1000000) + g_snprintf(buf, bufsize, "%06X", value); + else + g_snprintf(buf, bufsize, "%08X", value); + break; + + default: + g_return_if_reached(); + } +} + +/* Store breakpoint properties in tree model */ +static void set_iter_from_bp(struct bplist_dlg *bpldlg, GtkTreeIter *iter, + const TilemDebugBreakpoint *bp) +{ + char tbuf[5], sbuf[10], ebuf[10]; + int i, j; + + g_return_if_fail(bp != NULL); + + tbuf[0] = type_info[bp->type].abbrev; + j = 1; + for (i = 0; i < 3; i++) + if (bp->mode & (4 >> i)) + tbuf[j++] = "RWX"[i]; + tbuf[j] = 0; + + format_address(bpldlg->dbg, sbuf, sizeof(sbuf), bp->type, bp->start); + format_address(bpldlg->dbg, ebuf, sizeof(ebuf), bp->type, bp->end); + + gtk_list_store_set(bpldlg->store, iter, + COL_BP, bp, + COL_START, bp->start, + COL_END, bp->end, + COL_TYPE_STR, tbuf, + COL_START_STR, sbuf, + COL_END_STR, ebuf, + COL_ENABLED, !bp->disabled, + -1); +} + +/* Get breakpoint pointer for the given tree model row */ +static TilemDebugBreakpoint *get_iter_bp(struct bplist_dlg *bpldlg, + GtkTreeIter *iter) +{ + gpointer ptr; + gtk_tree_model_get(GTK_TREE_MODEL(bpldlg->store), iter, + COL_BP, &ptr, -1); + return (TilemDebugBreakpoint *) ptr; +} + +/* Set buttons sensitive or insensitive depending on whether any BPs + are selected */ +static void update_buttons(struct bplist_dlg *bpldlg) +{ + GtkTreeSelection *sel; + gboolean any_sel; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(bpldlg->treeview)); + + any_sel = gtk_tree_selection_get_selected(sel, NULL, NULL); + gtk_widget_set_sensitive(bpldlg->remove_btn, any_sel); + gtk_widget_set_sensitive(bpldlg->edit_btn, any_sel); + + gtk_widget_set_sensitive(bpldlg->clear_btn, + bpldlg->dbg->breakpoints != NULL); +} + +/* "Add breakpoint" button clicked */ +static void add_clicked(G_GNUC_UNUSED GtkButton *btn, gpointer data) +{ + struct bplist_dlg *bpldlg = data; + TilemDebugBreakpoint tmpbp, *newbp; + TilemDebugger *dbg = bpldlg->dbg; + GtkTreeIter iter; + + memset(&tmpbp, 0, sizeof(tmpbp)); + tmpbp.type = dbg->last_bp_type; + tmpbp.mode = dbg->last_bp_mode; + + if (!edit_breakpoint(dbg, GTK_WINDOW(bpldlg->dlg), + "Add Breakpoint", &tmpbp, FALSE)) + return; + + dbg->last_bp_type = tmpbp.type; + dbg->last_bp_mode = tmpbp.mode; + + newbp = tilem_debugger_add_breakpoint(dbg, &tmpbp); + gtk_list_store_append(bpldlg->store, &iter); + set_iter_from_bp(bpldlg, &iter, newbp); + + update_buttons(bpldlg); +} + +/* "Remove breakpoint" button clicked */ +static void remove_clicked(G_GNUC_UNUSED GtkButton *btn, gpointer data) +{ + struct bplist_dlg *bpldlg = data; + GtkTreeSelection *sel; + GtkTreeIter iter; + TilemDebugBreakpoint *bp; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(bpldlg->treeview)); + + if (!gtk_tree_selection_get_selected(sel, NULL, &iter)) + return; + + bp = get_iter_bp(bpldlg, &iter); + g_return_if_fail(bp != NULL); + gtk_list_store_remove(bpldlg->store, &iter); + tilem_debugger_remove_breakpoint(bpldlg->dbg, bp); + + update_buttons(bpldlg); +} + +/* Edit an existing breakpoint */ +static void edit_row(struct bplist_dlg *bpldlg, GtkTreeIter *iter) +{ + TilemDebugBreakpoint *bp, tmpbp; + + bp = get_iter_bp(bpldlg, iter); + g_return_if_fail(bp != NULL); + tmpbp = *bp; + + if (!edit_breakpoint(bpldlg->dbg, GTK_WINDOW(bpldlg->dlg), + "Edit Breakpoint", &tmpbp, TRUE)) + return; + + tmpbp.disabled = 0; + tilem_debugger_change_breakpoint(bpldlg->dbg, bp, &tmpbp); + set_iter_from_bp(bpldlg, iter, bp); + + update_buttons(bpldlg); +} + +/* "Edit breakpoint" button clicked */ +static void edit_clicked(G_GNUC_UNUSED GtkButton *btn, gpointer data) +{ + struct bplist_dlg *bpldlg = data; + GtkTreeSelection *sel; + GtkTreeIter iter; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(bpldlg->treeview)); + + if (!gtk_tree_selection_get_selected(sel, NULL, &iter)) + return; + + edit_row(bpldlg, &iter); +} + +/* "Clear breakpoints" button clicked */ +static void clear_clicked(G_GNUC_UNUSED GtkButton *btn, gpointer data) +{ + struct bplist_dlg *bpldlg = data; + GtkWidget *dlg; + TilemDebugBreakpoint *bp; + + dlg = gtk_message_dialog_new(GTK_WINDOW(bpldlg->dlg), + GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + "Clear all breakpoints?"); + gtk_message_dialog_format_secondary_markup + (GTK_MESSAGE_DIALOG(dlg), + "All existing breakpoints will be deleted and" + " cannot be restored."); + gtk_dialog_add_buttons(GTK_DIALOG(dlg), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_CLEAR, GTK_RESPONSE_ACCEPT, + NULL); + gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + + if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_ACCEPT) { + while (bpldlg->dbg->breakpoints) { + bp = bpldlg->dbg->breakpoints->data; + tilem_debugger_remove_breakpoint(bpldlg->dbg, bp); + } + gtk_list_store_clear(bpldlg->store); + update_buttons(bpldlg); + } + + gtk_widget_destroy(dlg); +} + +/* Row activated (double-clicked, usually) */ +static void row_activated(G_GNUC_UNUSED GtkTreeView *treeview, + GtkTreePath *path, + G_GNUC_UNUSED GtkTreeViewColumn *col, + gpointer data) +{ + struct bplist_dlg *bpldlg = data; + GtkTreeIter iter; + + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(bpldlg->store), + &iter, path)) + edit_row(bpldlg, &iter); +} + +/* Toggle button clicked for a breakpoint */ +static void enabled_toggled(G_GNUC_UNUSED GtkCellRendererToggle *cell, + gchar *pathstr, gpointer data) +{ + struct bplist_dlg *bpldlg = data; + GtkTreePath *path; + GtkTreeIter iter; + TilemDebugBreakpoint *bp, tmpbp; + + path = gtk_tree_path_new_from_string(pathstr); + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(bpldlg->store), + &iter, path)) { + gtk_tree_path_free(path); + return; + } + gtk_tree_path_free(path); + + bp = get_iter_bp(bpldlg, &iter); + g_return_if_fail(bp != NULL); + tmpbp = *bp; + tmpbp.disabled = !tmpbp.disabled; + tilem_debugger_change_breakpoint(bpldlg->dbg, bp, &tmpbp); + set_iter_from_bp(bpldlg, &iter, bp); +} + +/* Selection changed */ +static void selection_changed(G_GNUC_UNUSED GtkTreeSelection *sel, + gpointer data) +{ + struct bplist_dlg *bpldlg = data; + update_buttons(bpldlg); +} + +/* Show a dialog letting the user add, remove, and edit breakpoints */ +void tilem_debugger_edit_breakpoints(TilemDebugger *dbg) +{ + struct bplist_dlg bpldlg; + GtkWidget *dlg, *hbox, *treeview, *sw, *bbox, *btn, *vbox, *vbox2, + *invalid_cb, *undoc_cb; + GtkListStore *store; + GtkTreeViewColumn *col; + GtkCellRenderer *cell; + GtkTreeIter iter; + GSList *l; + GtkTreeSelection *sel; + unsigned int flags; + + g_return_if_fail(dbg != NULL); + g_return_if_fail(dbg->emu != NULL); + g_return_if_fail(dbg->emu->calc != NULL); + + bpldlg.dbg = dbg; + + dlg = gtk_dialog_new_with_buttons("Breakpoints", + GTK_WINDOW(dbg->window), + GTK_DIALOG_MODAL, + GTK_STOCK_CLOSE, + GTK_RESPONSE_ACCEPT, + NULL); + + gtk_window_set_default_size(GTK_WINDOW(dlg), -1, 300); + + store = gtk_list_store_new(N_COLUMNS, + G_TYPE_POINTER, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_BOOLEAN); + + bpldlg.dlg = dlg; + bpldlg.store = store; + + vbox = gtk_vbox_new(FALSE, 6); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); + + hbox = gtk_hbox_new(FALSE, 6); + + for (l = dbg->breakpoints; l; l = l->next) { + gtk_list_store_append(store, &iter); + set_iter_from_bp(&bpldlg, &iter, l->data); + } + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), TRUE); + bpldlg.treeview = treeview; + + fixed_tree_view_init(treeview, 0, + COL_TYPE_STR, "MRWX ", + COL_START_STR, "DD:DDDD ", + COL_END_STR, "DD:DDDD ", + COL_ENABLED, TRUE, + -1); + + g_signal_connect(treeview, "row-activated", + G_CALLBACK(row_activated), &bpldlg); + + /* Enabled/type column */ + + col = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(col, "Type"); + gtk_tree_view_column_set_sort_column_id(col, COL_TYPE_STR); + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); + + cell = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(col, cell, FALSE); + gtk_tree_view_column_set_attributes(col, cell, + "active", COL_ENABLED, + NULL); + g_signal_connect(cell, "toggled", + G_CALLBACK(enabled_toggled), &bpldlg); + + cell = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(col, cell, TRUE); + gtk_tree_view_column_set_attributes(col, cell, + "text", COL_TYPE_STR, + NULL); + + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col); + + /* Start column */ + + cell = gtk_cell_renderer_text_new(); + col = gtk_tree_view_column_new_with_attributes + ("Start", cell, "text", COL_START_STR, NULL); + gtk_tree_view_column_set_sort_column_id(col, COL_START); + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); + + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col); + + /* End column */ + + cell = gtk_cell_renderer_text_new(); + col = gtk_tree_view_column_new_with_attributes + ("End", cell, "text", COL_END_STR, NULL); + gtk_tree_view_column_set_sort_column_id(col, COL_END); + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); + + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col); + + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(sw), treeview); + + gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0); + + /* Buttons */ + + bbox = gtk_vbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START); + gtk_box_set_spacing(GTK_BOX(bbox), 6); + + btn = gtk_button_new_from_stock(GTK_STOCK_ADD); + g_signal_connect(btn, "clicked", + G_CALLBACK(add_clicked), &bpldlg); + gtk_container_add(GTK_CONTAINER(bbox), btn); + + btn = gtk_button_new_from_stock(GTK_STOCK_REMOVE); + g_signal_connect(btn, "clicked", + G_CALLBACK(remove_clicked), &bpldlg); + gtk_container_add(GTK_CONTAINER(bbox), btn); + bpldlg.remove_btn = btn; + + btn = gtk_button_new_from_stock(GTK_STOCK_EDIT); + g_signal_connect(btn, "clicked", + G_CALLBACK(edit_clicked), &bpldlg); + gtk_container_add(GTK_CONTAINER(bbox), btn); + bpldlg.edit_btn = btn; + + btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR); + g_signal_connect(btn, "clicked", + G_CALLBACK(clear_clicked), &bpldlg); + gtk_container_add(GTK_CONTAINER(bbox), btn); + bpldlg.clear_btn = btn; + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + g_signal_connect(sel, "changed", + G_CALLBACK(selection_changed), &bpldlg); + + update_buttons(&bpldlg); + + gtk_box_pack_start(GTK_BOX(hbox), bbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); + + invalid_cb = gtk_check_button_new_with_mnemonic + ("Break on _invalid instructions"); + undoc_cb = gtk_check_button_new_with_mnemonic + ("Break on _undocumented instructions"); + + tilem_calc_emulator_lock(dbg->emu); + flags = dbg->emu->calc->z80.emuflags; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(invalid_cb), + (flags & TILEM_Z80_BREAK_INVALID)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(undoc_cb), + (flags & TILEM_Z80_BREAK_UNDOCUMENTED)); + tilem_calc_emulator_unlock(dbg->emu); + + gtk_box_pack_start(GTK_BOX(vbox), invalid_cb, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), undoc_cb, FALSE, FALSE, 0); + + gtk_widget_show_all(vbox); + + gtk_widget_grab_focus(treeview); + + vbox2 = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); + gtk_box_pack_start(GTK_BOX(vbox2), vbox, TRUE, TRUE, 0); + + gtk_dialog_run(GTK_DIALOG(dlg)); + + tilem_calc_emulator_lock(dbg->emu); + flags = dbg->emu->calc->z80.emuflags; + flags &= ~(TILEM_Z80_BREAK_INVALID | TILEM_Z80_BREAK_UNDOCUMENTED); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(invalid_cb))) + flags |= TILEM_Z80_BREAK_INVALID; + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(undoc_cb))) + flags |= TILEM_Z80_BREAK_UNDOCUMENTED; + dbg->emu->calc->z80.emuflags = flags; + tilem_calc_emulator_unlock(dbg->emu); + + gtk_widget_destroy(dlg); +} diff --git a/tool/tilem-src/gui/charmap.c b/tool/tilem-src/gui/charmap.c new file mode 100644 index 0000000..c3acf69 --- /dev/null +++ b/tool/tilem-src/gui/charmap.c @@ -0,0 +1,127 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "charmap.h" + +#define UNDEF 0xfffd + +static const unsigned long ti81chars[256] = { + 0, ' ', 0x2192, 0x2191, 0x2193, 0x25B6, '<', 0x2264, + '=', 0x2260, '>', 0x2265, 0x3D20DE, 0x207C, '+', '-', + '*', '/', '^', 0x221A, '(', ')', '[', ']', + '{', '}', '?', '!', ':', ',', 0x2026, 0x207B00B9, + 0x207B, 0xB7, 0x2070, 0xB9, 0xB2, 0xB3, 0x2074, 0x2075, + 0x2076, 0x2077, 0x2078, 0x2079, 'E', 0x2081, 0x2082, 0x2083, + 0x2084, 0x23E8, 0x209C, '"', 0x207B, '.', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9', + 'E', 0x2B3, 0xB0, 0x3B8, 'R', 'T', 0x2E3, 0x2B8, + 0x780305, 0x790305, 0x3A3, 0x3C3, 0x3C0, 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x3B8, + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 0xD7, + 0x2588, 0x219120DE, 0x4120DE, '_', 0x21910332, 0x410332, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, + UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF, UNDEF }; + +static const unsigned long * getmap(int model) +{ + switch (model) { + case TILEM_CALC_TI73: + return ti73_charset; + case TILEM_CALC_TI81: + return ti81chars; + case TILEM_CALC_TI82: + return ti82_charset; + case TILEM_CALC_TI76: + case TILEM_CALC_TI83: + return ti83_charset; + case TILEM_CALC_TI83P: + case TILEM_CALC_TI83P_SE: + case TILEM_CALC_TI84P: + case TILEM_CALC_TI84P_SE: + case TILEM_CALC_TI84P_NSPIRE: + return ti83p_charset; + case TILEM_CALC_TI85: + return ti85_charset; + case TILEM_CALC_TI86: + return ti86_charset; + default: + return ti83p_charset; + } +} + +/* Convert a byte value from the calculator large-font character set + into a printable UTF-8 string. */ +char *ti_to_unicode(int model, unsigned int value) +{ + const unsigned long *map = getmap(model); + unsigned long v; + char buf[12]; + int n; + + v = map[value]; + if (v == 0) + v = 0x2400; /* SYMBOL FOR NULL */ + else if (v == '\n') + v = 0x240A; /* SYMBOL FOR LINE FEED */ + else if (v == ' ') + v = 0x2423; /* OPEN BOX */ + + /* in the ticonv character tables, non-BMP characters are + represented by a surrogate pair */ + if ((v & 0xfc00fc00) == 0xd800dc00) { + v = (((v & 0x3ff0000) >> 6) | (v & 0x3ff)) + 0x10000; + n = g_unichar_to_utf8(v, buf); + } + else if (v & 0xffff0000) { + n = g_unichar_to_utf8(v >> 16, buf); + n += g_unichar_to_utf8(v & 0xffff, buf + n); + } + else { + n = g_unichar_to_utf8(v, buf); + } + + return g_strndup(buf, n); +} diff --git a/tool/tilem-src/gui/charmap.h b/tool/tilem-src/gui/charmap.h new file mode 100644 index 0000000..c383a94 --- /dev/null +++ b/tool/tilem-src/gui/charmap.h @@ -0,0 +1,22 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Convert a byte value from the calculator large-font character set + into a printable UTF-8 string. */ +char *ti_to_unicode(int model, unsigned int value); diff --git a/tool/tilem-src/gui/config.c b/tool/tilem-src/gui/config.c new file mode 100644 index 0000000..4a00827 --- /dev/null +++ b/tool/tilem-src/gui/config.c @@ -0,0 +1,338 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "files.h" +#include "msgbox.h" + +#ifndef CONFIG_FILE +#define CONFIG_FILE "config.ini" +#endif + +#define MAX_RECENT_FILES 10 + +/* Store a filename in a GKeyFile. Any control characters or + non-UTF-8 filenames are stored in octal. Note that + g_key_file_set/get_string() can't be used because they only allow + UTF-8 */ +static void key_file_set_filename(GKeyFile *gkf, const char *group, + const char *key, const char *value) +{ + char *escaped; + const char *p; + char *q; + gunichar uc; + int b; + + q = escaped = g_new(char, strlen(value) * 4 + 1); + + while (*value != 0) { + uc = g_utf8_get_char_validated(value, -1); + if (uc < 0x20 || uc == 0x7F || !g_unichar_validate(uc)) { + b = (unsigned char) *value; + q[0] = '\\'; + q[1] = '0' + (b >> 6); + q[2] = '0' + ((b >> 3) & 7); + q[3] = '0' + (b & 7); + q += 4; + value++; + } + else if (uc == '\\') { + q[0] = q[1] = '\\'; + q += 2; + value++; + } + else { + p = g_utf8_next_char(value); + while (value != p) + *q++ = *value++; + } + } + + *q = 0; + + g_key_file_set_value(gkf, group, key, escaped); + g_free(escaped); +} + +/* Retrieve a filename from a GKeyFile. */ +static char *key_file_get_filename(GKeyFile *gkf, const char *group, + const char *key, GError **error) +{ + char *value, *unescaped; + + value = g_key_file_get_value(gkf, group, key, error); + if (!value) + return NULL; + + unescaped = g_strcompress(value); + g_free(value); + return unescaped; +} + +/* Load and parse the configuration file. */ +static GKeyFile *load_config(gboolean writable) +{ + static gboolean warned; + GKeyFile *gkf; + GKeyFileFlags flags; + char *cfname, *dname; + GError *err = NULL; + + gkf = g_key_file_new(); + + cfname = get_shared_file_path(CONFIG_FILE, NULL); + if (!cfname) + return gkf; + + if (writable) + flags = (G_KEY_FILE_KEEP_COMMENTS + | G_KEY_FILE_KEEP_TRANSLATIONS); + else + flags = 0; + + if (!g_key_file_load_from_file(gkf, cfname, flags, &err)) { + /* don't bother the user more than once */ + if (!warned) { + dname = g_filename_display_name(cfname); + messagebox02(NULL, GTK_MESSAGE_ERROR, + "Unable to read settings", + "An error occurred while reading %s: %s", + dname, err->message); + g_free(dname); + warned = TRUE; + } + g_error_free(err); + } + + g_free(cfname); + return gkf; +} + +/* Save the configuration file. */ +static void save_config(GKeyFile *gkf) +{ + static gboolean warned; + char *cfname, *dname; + char *data; + gsize length; + GError *err = NULL; + + data = g_key_file_to_data(gkf, &length, NULL); + + cfname = get_config_file_path(CONFIG_FILE, NULL); + + if (!g_file_set_contents(cfname, data, length, &err)) { + /* don't bother the user more than once */ + if (!warned) { + dname = g_filename_display_name(cfname); + messagebox02(NULL, GTK_MESSAGE_ERROR, + "Unable to save settings", + "An error occurred while writing %s: %s", + dname, err->message); + g_free(dname); + warned = TRUE; + } + g_error_free(err); + } + + g_free(cfname); + g_free(data); +} + +/* Retrieve settings from the configuration file. */ +void tilem_config_get(const char *group, const char *option, ...) +{ + va_list ap; + GKeyFile *gkf; + const char *type, *defvalue; + GError *err = NULL; + char *key, *p; + char **strp; + int *intp; + double *dblp; + GdkColor *colorp; + + g_return_if_fail(group != NULL); + g_return_if_fail(option != NULL); + + gkf = load_config(FALSE); + + va_start(ap, option); + while (option != NULL) { + type = strrchr(option, '/'); + if (type == NULL || type[1] == 0 + || (type[2] != 0 && type[2] != '=')) { + g_critical("invalid argument\n"); + break; + } + + if (type[2] == '=') + defvalue = &type[3]; + else + defvalue = NULL; + + key = g_strndup(option, type - option); + + if (type[1] == 'f') { + strp = va_arg(ap, char **); + *strp = key_file_get_filename(gkf, group, key, &err); + if (err && defvalue) + *strp = g_strdup(defvalue); + } + else if (type[1] == 's') { + strp = va_arg(ap, char **); + *strp = g_key_file_get_string(gkf, group, key, &err); + if (err && defvalue) + *strp = g_strdup(defvalue); + } + else if (type[1] == 'i') { + intp = va_arg(ap, int *); + *intp = g_key_file_get_integer(gkf, group, key, &err); + if (err && defvalue) + *intp = g_ascii_strtoll(defvalue, NULL, 10); + } + else if (type[1] == 'r') { + dblp = va_arg(ap, double *); + *dblp = g_key_file_get_double(gkf, group, key, &err); + if (err && defvalue) + *dblp = g_ascii_strtod(defvalue, NULL); + } + else if (type[1] == 'b') { + intp = va_arg(ap, int *); + *intp = g_key_file_get_boolean(gkf, group, key, &err); + if (err && defvalue) + *intp = g_ascii_strtoll(defvalue, NULL, 10); + } + else if (type[1] == 'c') { + colorp = va_arg(ap, GdkColor *); + p = g_key_file_get_string(gkf, group, key, &err); + if (p == NULL || !gdk_color_parse(p, colorp)) { + if (defvalue) { + gdk_color_parse(defvalue, colorp); + } + else { + colorp->red = 0; + colorp->green = 0; + colorp->blue = 0; + } + } + g_free(p); + } + else { + g_critical("invalid argument\n"); + g_free(key); + break; + } + + g_clear_error(&err); + g_free(key); + option = va_arg(ap, const char *); + } + va_end(ap); + + g_key_file_free(gkf); +} + +/* Save settings to the configuration file. */ +void tilem_config_set(const char *group, const char *option, ...) +{ + va_list ap; + GKeyFile *gkf; + const char *type; + char *key; + const char *strv; + int intv; + double dblv; + const GdkColor *colorv; + char *p; + + g_return_if_fail(group != NULL); + g_return_if_fail(option != NULL); + + gkf = load_config(TRUE); + + va_start(ap, option); + while (option != NULL) { + type = strrchr(option, '/'); + if (type == NULL || type[1] == 0 || type[2] != 0) { + g_critical("invalid argument\n"); + break; + } + + key = g_strndup(option, type - option); + + if (type[1] == 'f') { + strv = va_arg(ap, const char *); + key_file_set_filename(gkf, group, key, strv); + } + else if (type[1] == 's') { + strv = va_arg(ap, const char *); + g_key_file_set_string(gkf, group, key, strv); + } + else if (type[1] == 'i') { + intv = va_arg(ap, int); + g_key_file_set_integer(gkf, group, key, intv); + } + else if (type[1] == 'r') { + dblv = va_arg(ap, double); + g_key_file_set_double(gkf, group, key, dblv); + } + else if (type[1] == 'b') { + intv = va_arg(ap, int); + g_key_file_set_boolean(gkf, group, key, !!intv); + } + else if (type[1] == 'c') { + colorv = va_arg(ap, const GdkColor *); + p = g_strdup_printf("#%02x%02x%02x", + colorv->red >> 8, + colorv->green >> 8, + colorv->blue >> 8); + g_key_file_set_string(gkf, group, key, p); + g_free(p); + } + else { + g_critical("invalid argument\n"); + g_free(key); + break; + } + + g_free(key); + + option = va_arg(ap, const char *); + } + va_end(ap); + + save_config(gkf); + g_key_file_free(gkf); +} + diff --git a/tool/tilem-src/gui/debugger.c b/tool/tilem-src/gui/debugger.c new file mode 100644 index 0000000..aac8708 --- /dev/null +++ b/tool/tilem-src/gui/debugger.c @@ -0,0 +1,1420 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "disasmview.h" +#include "files.h" +#include "msgbox.h" +#include "fixedtreeview.h" +#include "memmodel.h" + +/* Stack list */ +enum +{ + COL_OFFSET_STK = 0, + COL_VALUE_STK, + NUM_COLS_STK +}; + +/* Indices in reg_entries */ +enum { + R_AF, R_BC, R_DE, R_HL, R_IX, R_SP, + R_AF2, R_BC2, R_DE2, R_HL2, R_IY, R_PC, + R_IM, R_I, + NUM_REGS +}; + +/* Labels for the entries */ +static const char * const reg_labels[] = { + "A_F:", "B_C:", "D_E:", "H_L:", "I_X:", "SP:", + "AF':", "BC':", "DE':", "HL':", "I_Y:", "PC:", + "IM:", "I:" +}; + +/* Labels for the flag buttons */ +static const char flag_labels[][2] = { + "C", "N", "P", "X", "H", "Y", "Z", "S" +}; + +/* Read a word */ +static dword read_mem_word(TilemCalc *calc, dword addr) +{ + dword phys, v; + + phys = (*calc->hw.mem_ltop)(calc, addr & 0xffff); + v = calc->mem[phys]; + phys = (*calc->hw.mem_ltop)(calc, (addr + 1) & 0xffff); + v += calc->mem[phys] << 8; + return v; +} + +/* Determine model name for the purpose of looking up default system + symbols */ +static const char *get_sys_name(const TilemCalc *calc) +{ + g_return_val_if_fail(calc != NULL, NULL); + + switch (calc->hw.model_id) { + case TILEM_CALC_TI83: + case TILEM_CALC_TI76: + return "ti83"; + + case TILEM_CALC_TI83P: + case TILEM_CALC_TI83P_SE: + case TILEM_CALC_TI84P: + case TILEM_CALC_TI84P_SE: + case TILEM_CALC_TI84P_NSPIRE: + return "ti83p"; + + default: + return calc->hw.name; + } +} + +/* Load default system symbols */ +static void load_default_symbols(TilemDebugger *dbg) +{ + char *base, *path, *dname; + const char *errstr; + FILE *symfile; + + base = g_strdup_printf("%s.sym", get_sys_name(dbg->emu->calc)); + path = get_shared_file_path("symbols", base, NULL); + g_free(base); + if (!path) + return; + + symfile = g_fopen(path, "rb"); + if (!symfile) { + errstr = g_strerror(errno); + dname = g_filename_display_name(path); + messagebox02(NULL, GTK_MESSAGE_ERROR, + "Unable to read symbols", + "An error occurred while reading %s: %s", + dname, errstr); + g_free(dname); + g_free(path); + return; + } + + tilem_disasm_read_symbol_file(dbg->dasm, symfile); + + fclose(symfile); + g_free(path); +} + +/* Cancel temporary breakpoint */ +static void cancel_step_bp(TilemDebugger *dbg) +{ + if (!dbg->step_bp) + return; + + g_return_if_fail(dbg->emu->calc != NULL); + tilem_calc_emulator_lock(dbg->emu); + tilem_z80_remove_breakpoint(dbg->emu->calc, dbg->step_bp); + tilem_calc_emulator_unlock(dbg->emu); + dbg->step_bp = 0; +} + +/* Actions */ + +/* Run (but leave debugger window open) */ +static void action_run(G_GNUC_UNUSED GtkAction *a, gpointer data) +{ + TilemDebugger *dbg = data; + cancel_step_bp(dbg); + tilem_calc_emulator_run(dbg->emu); + tilem_debugger_refresh(dbg, TRUE); +} + +/* Pause */ +static void action_pause(G_GNUC_UNUSED GtkAction *a, gpointer data) +{ + TilemDebugger *dbg = data; + tilem_debugger_show(dbg); + cancel_step_bp(dbg); +} + +/* Stepping */ + +static int bptest_step(TilemCalc *calc, dword op, G_GNUC_UNUSED void *data) +{ + /* Single step condition: if calculator is halted, wait until + an interrupt occurs; otherwise, stop after any + instruction. */ + + if (op != 0x76 && (op & ~0x2000) != 0xdd76) + /* not a HALT instruction */ + return 1; + else if (calc->z80.interrupts != 0 && calc->z80.r.iff1) + return 1; + else + return 0; +} + +static int bptest_step_over(TilemCalc *calc, dword op, void *data) +{ + TilemDebugger *dbg = data; + dword destaddr; + + /* Step-over condition: behavior depends on what instruction + is executed. + + For most instructions, stop when we reach the "next line" + as determined by disassembly. This means skipping over + CALLs, RSTs, HALTs, and macros. + + For jump and return instructions, stop at the current PC, + whatever that is. + + In both cases, wait until we actually reach the target PC, + rather than halting immediately; the effect of this is that + if an interrupt has occurred, we also "step over" the + ISR. */ + + if ((op & ~0x20ff) == 0xdd00) + op &= 0xff; + + if (op == 0xc3 /* JP */ + || op == 0xc9 /* RET */ + || op == 0xe9 /* JP HL/IX/IY */ + || (op & ~0x38) == 0 /* JR, DJNZ, NOP, or EX AF,AF' */ + || (op & ~0x38) == 0xc2 /* conditional JP */ + || (op & ~0x38) == 0xc0 /* conditional RET */ + || (op & ~0x38) == 0xed45) /* RETI/RETN */ + destaddr = calc->z80.r.pc.d; + else + destaddr = dbg->step_next_addr; + + destaddr &= 0xffff; + + /* Delete this breakpoint, and replace it with a simple exec + breakpoint at the target address. */ + + tilem_z80_remove_breakpoint(calc, dbg->step_bp); + dbg->step_bp = tilem_z80_add_breakpoint(calc, TILEM_BREAK_MEM_EXEC, + destaddr, destaddr, 0xffff, + NULL, NULL); + return 0; +} + +static int bptest_finish(TilemCalc *calc, dword op, void *data) +{ + dword exitsp = TILEM_PTR_TO_DWORD(data); + byte f; + + /* Finish condition: wait until stack pointer is greater than + a certain value, and we execute a return instruction. JP + HL/IX/IY are also considered return instructions. */ + + if (calc->z80.r.sp.w.l <= exitsp) + return 0; + + if ((op & ~0x20ff) == 0xdd00) + op &= 0xff; + + f = calc->z80.r.af.b.l; + + switch (op) { + case 0xc9: /* RET */ + case 0xe9: /* JP HL/IX/IY */ + case 0xed45: /* RETN */ + case 0xed4d: /* RETI */ + case 0xed55: + case 0xed5d: + case 0xed65: + case 0xed6d: + case 0xed75: + case 0xed7d: + return 1; + + /* conditionals: check if condition was true */ + case 0xc0: return !(f & 0x40); + case 0xc8: return (f & 0x40); + case 0xd0: return !(f & 0x01); + case 0xd8: return (f & 0x01); + case 0xe0: return !(f & 0x04); + case 0xe8: return (f & 0x04); + case 0xf0: return !(f & 0x80); + case 0xf8: return (f & 0x80); + + default: + return 0; + } +} + +static gboolean post_resume_refresh(gpointer data) +{ + TilemDebugger *dbg = data; + tilem_debugger_refresh(dbg, FALSE); + return FALSE; +} + +static void run_with_step_condition(TilemDebugger *dbg, + TilemZ80BreakpointFunc func, + void *data) +{ + tilem_calc_emulator_lock(dbg->emu); + dbg->step_bp = tilem_z80_add_breakpoint(dbg->emu->calc, + TILEM_BREAK_EXECUTE, 0, 0, 0, + func, data); + tilem_calc_emulator_unlock(dbg->emu); + tilem_calc_emulator_run(dbg->emu); + /* Don't refresh right away, to avoid flickering */ + g_timeout_add(10, &post_resume_refresh, dbg); +} + +/* Execute one instruction */ +static void action_step(G_GNUC_UNUSED GtkAction *a, gpointer data) +{ + TilemDebugger *dbg = data; + + if (!dbg->emu->paused) + return; + + g_return_if_fail(dbg->emu->calc != NULL); + + cancel_step_bp(dbg); + + run_with_step_condition(dbg, &bptest_step, NULL); +} + +/* Skip over an instruction */ +static void action_step_over(G_GNUC_UNUSED GtkAction *a, gpointer data) +{ + TilemDebugger *dbg = data; + + if (!dbg->emu->paused) + return; + + g_return_if_fail(dbg->emu->calc != NULL); + + cancel_step_bp(dbg); + + tilem_calc_emulator_lock(dbg->emu); + tilem_disasm_disassemble(dbg->dasm, dbg->emu->calc, 0, + dbg->emu->calc->z80.r.pc.w.l, + &dbg->step_next_addr, + NULL, 0); + tilem_calc_emulator_unlock(dbg->emu); + + run_with_step_condition(dbg, &bptest_step_over, dbg); +} + +/* Run until current subroutine finishes */ +static void action_finish(G_GNUC_UNUSED GtkAction *a, gpointer data) +{ + TilemDebugger *dbg = data; + dword sp; + + if (!dbg->emu->paused) + return; + + g_return_if_fail(dbg->emu->calc != NULL); + + cancel_step_bp(dbg); + + tilem_calc_emulator_lock(dbg->emu); + sp = dbg->emu->calc->z80.r.sp.w.l; + tilem_calc_emulator_unlock(dbg->emu); + + run_with_step_condition(dbg, &bptest_finish, + TILEM_DWORD_TO_PTR(sp)); +} + +/* Toggle breakpoint at selected line */ +static void action_toggle_breakpoint(G_GNUC_UNUSED GtkAction *a, gpointer data) +{ + TilemDebugger *dbg = data; + GtkWidget *focus; + + focus = gtk_window_get_focus(GTK_WINDOW(dbg->window)); + if (TILEM_IS_DISASM_VIEW(focus)) + tilem_disasm_view_toggle_breakpoint(TILEM_DISASM_VIEW(focus)); +} + +/* Edit breakpoints */ +static void action_edit_breakpoints(G_GNUC_UNUSED GtkAction *a, gpointer data) +{ + TilemDebugger *dbg = data; + tilem_debugger_edit_breakpoints(dbg); +} + +/* Close debugger window */ +static void action_close(G_GNUC_UNUSED GtkAction *a, gpointer data) +{ + TilemDebugger *dbg = data; + tilem_debugger_hide(dbg); +} + +static void keypad_dlg_response(G_GNUC_UNUSED GtkDialog *dlg, + G_GNUC_UNUSED int response, + gpointer data) +{ + gtk_toggle_action_set_active(data, FALSE); +} + +/* Show/hide keypad dialog */ +static void action_view_keypad(GtkToggleAction *action, gpointer data) +{ + TilemDebugger *dbg = data; + + if (!dbg->keypad_dialog) { + dbg->keypad_dialog = tilem_keypad_dialog_new(dbg); + g_signal_connect(dbg->keypad_dialog->window, "response", + G_CALLBACK(keypad_dlg_response), action); + } + + if (gtk_toggle_action_get_active(action)) + gtk_window_present(GTK_WINDOW(dbg->keypad_dialog->window)); + else + gtk_widget_hide(dbg->keypad_dialog->window); +} + +/* Set memory addressing mode */ +static void action_mem_mode(GtkRadioAction *action, + G_GNUC_UNUSED GtkRadioAction *current, + gpointer data) +{ + TilemDebugger *dbg = data; + + dbg->mem_logical = gtk_radio_action_get_current_value(action); + + tilem_disasm_view_set_logical(TILEM_DISASM_VIEW(dbg->disasm_view), + dbg->mem_logical); + + tilem_debugger_mem_view_configure(dbg->mem_view, + dbg->emu, dbg->mem_rowsize, + dbg->mem_start, dbg->mem_logical); + + tilem_config_set("debugger", + "mem_logical/b", dbg->mem_logical, + NULL); +} + +/* Prompt for an address to view */ +static void action_go_to_address(G_GNUC_UNUSED GtkAction *action, gpointer data) +{ + TilemDebugger *dbg = data; + TilemDisasmView *dv = TILEM_DISASM_VIEW(dbg->disasm_view); + dword addr; + gboolean addr_set, logical; + + addr_set = tilem_disasm_view_get_cursor(dv, &addr, &logical); + + if (!tilem_prompt_address(dbg, GTK_WINDOW(dbg->window), + "Go to Address", "Address:", + &addr, !logical, addr_set)) + return; + + tilem_disasm_view_go_to_address(dv, addr, logical); + gtk_widget_grab_focus(dbg->disasm_view); +} + +/* Jump to address at given stack index */ +static void go_to_stack_pos(TilemDebugger *dbg, int pos) +{ + dword addr; + GtkTreePath *path; + GtkTreeSelection *sel; + + dbg->stack_index = pos; + + tilem_calc_emulator_lock(dbg->emu); + if (pos < 0) + addr = dbg->emu->calc->z80.r.pc.d; + else + addr = read_mem_word(dbg->emu->calc, + dbg->emu->calc->z80.r.sp.d + 2 * pos); + tilem_calc_emulator_unlock(dbg->emu); + + tilem_disasm_view_go_to_address(TILEM_DISASM_VIEW(dbg->disasm_view), + addr, TRUE); + gtk_widget_grab_focus(dbg->disasm_view); + + if (pos >= 0) { + path = gtk_tree_path_new_from_indices(pos, -1); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(dbg->stack_view), + path, NULL, FALSE); + gtk_tree_path_free(path); + } + else { + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dbg->stack_view)); + gtk_tree_selection_unselect_all(sel); + } + + gtk_action_set_sensitive(dbg->prev_stack_action, (pos >= 0)); +} + +/* Jump to current PC */ +static void action_go_to_pc(G_GNUC_UNUSED GtkAction *action, gpointer data) +{ + TilemDebugger *dbg = data; + go_to_stack_pos(dbg, -1); +} + +/* Jump to previous stack entry */ +static void action_prev_stack_entry(G_GNUC_UNUSED GtkAction *action, + gpointer data) +{ + TilemDebugger *dbg = data; + if (dbg->stack_index >= 0) + go_to_stack_pos(dbg, dbg->stack_index - 1); +} + +/* Jump to next stack entry */ +static void action_next_stack_entry(G_GNUC_UNUSED GtkAction *action, + gpointer data) +{ + TilemDebugger *dbg = data; + go_to_stack_pos(dbg, dbg->stack_index + 1); +} + + +static const GtkActionEntry run_action_ents[] = + {{ "pause", GTK_STOCK_MEDIA_PAUSE, "_Pause", "Escape", + "Pause emulation", G_CALLBACK(action_pause) }}; + +static const GtkActionEntry paused_action_ents[] = + {{ "run", GTK_STOCK_MEDIA_PLAY, "_Run", "F5", + "Resume emulation", G_CALLBACK(action_run) }, + { "step", "tilem-db-step", "_Step", "F7", + "Execute one instruction", G_CALLBACK(action_step) }, + { "step-over", "tilem-db-step-over", "Step _Over", "F8", + "Run to the next line (skipping over subroutines)", + G_CALLBACK(action_step_over) }, + { "finish", "tilem-db-finish", "_Finish Subroutine", "F9", + "Run to end of the current subroutine", G_CALLBACK(action_finish) }, + { "toggle-breakpoint", NULL, "Toggle Breakpoint", "F2", + "Enable or disable breakpoint at the selected address", + G_CALLBACK(action_toggle_breakpoint) }, + { "edit-breakpoints", NULL, "_Breakpoints", "B", + "Add, remove, or modify breakpoints", + G_CALLBACK(action_edit_breakpoints) }, + { "go-to-address", GTK_STOCK_JUMP_TO, "_Address...", "L", + "Jump to an address", + G_CALLBACK(action_go_to_address) }, + { "go-to-pc", NULL, "Current P_C", "Home", + "Jump to the current program counter", + G_CALLBACK(action_go_to_pc) }, + { "prev-stack-entry", GTK_STOCK_GO_UP, "_Previous Stack Entry", "Page_Up", + "Jump to the previous address in the stack", + G_CALLBACK(action_prev_stack_entry) }, + { "next-stack-entry", GTK_STOCK_GO_DOWN, "_Next Stack Entry", "Page_Down", + "Jump to the next address in the stack", + G_CALLBACK(action_next_stack_entry) }}; + +static const GtkRadioActionEntry mem_mode_ents[] = + {{ "view-logical", 0, "_Logical Addresses", 0, + "Show contents of the current Z80 address space", 1 }, + { "view-absolute", 0, "_Absolute Addresses", 0, + "Show all memory contents", 0 }}; + +static const GtkActionEntry misc_action_ents[] = + {{ "debug-menu", 0, "_Debug", 0, 0, 0 }, + { "view-menu", 0, "_View", 0, 0, 0 }, + { "go-menu", 0, "_Go", 0, 0, 0 }, + { "close", GTK_STOCK_CLOSE, 0, 0, + "Close the debugger", G_CALLBACK(action_close) }}; + +static const GtkToggleActionEntry misc_toggle_ents[] = + {{ "view-keypad", 0, "_Keypad", 0, + "Show the calculator keypad state", + G_CALLBACK(action_view_keypad), FALSE }}; + +/* Callbacks */ + +/* Register edited */ +static void reg_edited(GtkEntry *ent, gpointer data) +{ + TilemDebugger *dbg = data; + TilemCalc *calc; + const char *text; + char *end; + dword value; + int i; + + if (dbg->refreshing) + return; + + calc = dbg->emu->calc; + g_return_if_fail(calc != NULL); + + text = gtk_entry_get_text(ent); + value = strtol(text, &end, 16); + + for (i = 0; i < NUM_REGS; i++) + if (ent == (GtkEntry*) dbg->reg_entries[i]) + break; + + tilem_calc_emulator_lock(dbg->emu); + switch (i) { + case R_AF: calc->z80.r.af.d = value; break; + case R_BC: calc->z80.r.bc.d = value; break; + case R_DE: calc->z80.r.de.d = value; break; + case R_HL: calc->z80.r.hl.d = value; break; + case R_AF2: calc->z80.r.af2.d = value; break; + case R_BC2: calc->z80.r.bc2.d = value; break; + case R_DE2: calc->z80.r.de2.d = value; break; + case R_HL2: calc->z80.r.hl2.d = value; break; + case R_SP: calc->z80.r.sp.d = value; break; + case R_PC: calc->z80.r.pc.d = value; break; + case R_IX: calc->z80.r.ix.d = value; break; + case R_IY: calc->z80.r.iy.d = value; break; + case R_I: calc->z80.r.ir.b.h = value; break; + } + tilem_calc_emulator_unlock(dbg->emu); + + /* Set the value of the register immediately, but don't + refresh the display: refreshing the registers themselves + while user is trying to edit them would just be obnoxious, + and refreshing stack and disassembly would be at least + distracting. Instead, we'll refresh only when focus + changes. */ + + dbg->delayed_refresh = TRUE; +} + +/* Flag button toggled */ +static void flag_edited(GtkToggleButton *btn, gpointer data) +{ + TilemDebugger *dbg = data; + TilemCalc *calc; + int i; + + if (dbg->refreshing) + return; + + calc = dbg->emu->calc; + g_return_if_fail(calc != NULL); + + for (i = 0; i < 8; i++) + if (btn == (GtkToggleButton*) dbg->flag_buttons[i]) + break; + + tilem_calc_emulator_lock(dbg->emu); + if (gtk_toggle_button_get_active(btn)) + calc->z80.r.af.d |= (1 << i); + else + calc->z80.r.af.d &= ~(1 << i); + tilem_calc_emulator_unlock(dbg->emu); + + /* refresh AF */ + tilem_debugger_refresh(dbg, FALSE); +} + +/* IM edited */ +static void im_edited(GtkEntry *ent, gpointer data) +{ + TilemDebugger *dbg = data; + TilemCalc *calc; + const char *text; + char *end; + int value; + + if (dbg->refreshing) + return; + + calc = dbg->emu->calc; + g_return_if_fail(calc != NULL); + + text = gtk_entry_get_text(ent); + value = strtol(text, &end, 0); + + tilem_calc_emulator_lock(dbg->emu); + if (value >= 0 && value <= 2) + calc->z80.r.im = value; + tilem_calc_emulator_unlock(dbg->emu); + /* no need to refresh */ +} + +/* IFF button toggled */ +static void iff_edited(GtkToggleButton *btn, gpointer data) +{ + TilemDebugger *dbg = data; + TilemCalc *calc; + + if (dbg->refreshing) + return; + + calc = dbg->emu->calc; + g_return_if_fail(calc != NULL); + + tilem_calc_emulator_lock(dbg->emu); + if (gtk_toggle_button_get_active(btn)) + calc->z80.r.iff1 = calc->z80.r.iff2 = 1; + else + calc->z80.r.iff1 = calc->z80.r.iff2 = 0; + tilem_calc_emulator_unlock(dbg->emu); + /* no need to refresh */ +} + +/* Main window's focus widget changed */ +static void focus_changed(G_GNUC_UNUSED GtkWindow *win, + G_GNUC_UNUSED GtkWidget *widget, + gpointer data) +{ + TilemDebugger *dbg = data; + + /* delayed refresh - see reg_edited() above */ + if (dbg->delayed_refresh) + tilem_debugger_refresh(dbg, FALSE); +} + +/* Main window received a "delete" message */ +static gboolean delete_win(G_GNUC_UNUSED GtkWidget *w, + G_GNUC_UNUSED GdkEvent *ev, + gpointer data) +{ + TilemDebugger *dbg = data; + tilem_debugger_hide(dbg); + return TRUE; +} + +/* Create table of widgets for editing registers */ +static GtkWidget *create_registers(TilemDebugger *dbg) +{ + GtkWidget *vbox, *tbl, *lbl, *hbox, *ent, *btn; + int i; + + vbox = gtk_vbox_new(FALSE, 6); + + tbl = gtk_table_new(6, 4, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(tbl), 6); + gtk_table_set_col_spacings(GTK_TABLE(tbl), 6); + gtk_table_set_col_spacing(GTK_TABLE(tbl), 1, 12); + + for (i = 0; i < 12; i++) { + lbl = gtk_label_new_with_mnemonic(reg_labels[i]); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(GTK_TABLE(tbl), lbl, + 2 * (i / 6), 2 * (i / 6) + 1, + (i % 6), (i % 6) + 1, + GTK_FILL, GTK_FILL, 0, 0); + + dbg->reg_entries[i] = ent = gtk_entry_new(); + gtk_entry_set_width_chars(GTK_ENTRY(ent), 5); + g_signal_connect(ent, "changed", G_CALLBACK(reg_edited), dbg); + gtk_table_attach(GTK_TABLE(tbl), ent, + 2 * (i / 6) + 1, 2 * (i / 6) + 2, + (i % 6), (i % 6) + 1, + GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ent); + } + + gtk_box_pack_start(GTK_BOX(vbox), tbl, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(TRUE, 0); + + for (i = 7; i >= 0; i--) { + btn = gtk_toggle_button_new_with_label(flag_labels[i]); + dbg->flag_buttons[i] = btn; + g_signal_connect(btn, "toggled", G_CALLBACK(flag_edited), dbg); + gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0); + } + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 6); + + for (i = 12; i < 14; i++) { + lbl = gtk_label_new(reg_labels[i]); + gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0); + + dbg->reg_entries[i] = ent = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox), ent, TRUE, TRUE, 0); + } + + g_signal_connect(dbg->reg_entries[R_I], "changed", + G_CALLBACK(reg_edited), dbg); + g_signal_connect(dbg->reg_entries[R_IM], "changed", + G_CALLBACK(im_edited), dbg); + + gtk_entry_set_width_chars(GTK_ENTRY(dbg->reg_entries[R_IM]), 2); + gtk_entry_set_width_chars(GTK_ENTRY(dbg->reg_entries[R_I]), 3); + + dbg->iff_checkbox = btn = gtk_check_button_new_with_label("EI"); + g_signal_connect(btn, "toggled", G_CALLBACK(iff_edited), dbg); + gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + return vbox; +} + +/* Create the GtkTreeView to show the stack */ +static GtkWidget *create_stack_view() +{ + GtkCellRenderer *renderer; + GtkWidget *treeview; + GtkTreeViewColumn *column; + + /* Create the stack list tree view and set title invisible */ + treeview = gtk_tree_view_new(); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), + COL_VALUE_STK); + + /* Create the columns */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes + ("ADDR", renderer, "text", COL_OFFSET_STK, NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes + ("VAL", renderer, "text", COL_VALUE_STK, NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + return treeview; +} + +/* Create a new scrolled window with sensible default settings. */ +static GtkWidget *new_scrolled_window(GtkWidget *contents) +{ + GtkWidget *sw; + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(sw), contents); + return sw; +} + +static const char uidesc[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + "" + " " + " " + " " + " " + " " + " " + "" + ""; + +/* Create a new TilemDebugger. */ +TilemDebugger *tilem_debugger_new(TilemCalcEmulator *emu) +{ + TilemDebugger *dbg; + GtkWidget *hbox, *vbox, *vbox2, *vpaned, *sw, *menubar, *toolbar; + GtkUIManager *uimgr; + GtkAccelGroup *accelgrp; + GError *err = NULL; + int defwidth, defheight; + + g_return_val_if_fail(emu != NULL, NULL); + + dbg = g_slice_new0(TilemDebugger); + dbg->emu = emu; + dbg->dasm = tilem_disasm_new(); + + dbg->last_bp_type = TILEM_DB_BREAK_LOGICAL; + dbg->last_bp_mode = TILEM_DB_BREAK_EXEC; + + dbg->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(dbg->window), "TilEm Debugger"); + gtk_window_set_role(GTK_WINDOW(dbg->window), "Debugger"); + + tilem_config_get("debugger", + "width/i", &defwidth, + "height/i", &defheight, + "mem_width/i", &dbg->mem_rowsize, + "mem_start/i", &dbg->mem_start, + "mem_logical/b=1", &dbg->mem_logical, + NULL); + + if (dbg->mem_rowsize <= 0) + dbg->mem_rowsize = 8; + if (dbg->mem_start < 0 || dbg->mem_start >= dbg->mem_rowsize) + dbg->mem_start = 0; + + if (defwidth <= 0 || defheight <= 0) { + defwidth = 600; + defheight = 400; + } + + gtk_window_set_default_size(GTK_WINDOW(dbg->window), + defwidth, defheight); + + g_signal_connect(dbg->window, "set-focus", + G_CALLBACK(focus_changed), dbg); + g_signal_connect(dbg->window, "delete-event", + G_CALLBACK(delete_win), dbg); + + vbox2 = gtk_vbox_new(FALSE, 0); + + /* Actions and menu bar */ + + uimgr = gtk_ui_manager_new(); + dbg->run_actions = gtk_action_group_new("Debug"); + gtk_action_group_add_actions(dbg->run_actions, run_action_ents, + G_N_ELEMENTS(run_action_ents), dbg); + gtk_ui_manager_insert_action_group(uimgr, dbg->run_actions, 0); + + dbg->paused_actions = gtk_action_group_new("Debug"); + gtk_action_group_add_actions(dbg->paused_actions, paused_action_ents, + G_N_ELEMENTS(paused_action_ents), dbg); + gtk_action_group_add_radio_actions(dbg->paused_actions, mem_mode_ents, + G_N_ELEMENTS(mem_mode_ents), + dbg->mem_logical, + G_CALLBACK(action_mem_mode), dbg); + gtk_ui_manager_insert_action_group(uimgr, dbg->paused_actions, 0); + + dbg->misc_actions = gtk_action_group_new("Debug"); + gtk_action_group_add_actions(dbg->misc_actions, misc_action_ents, + G_N_ELEMENTS(misc_action_ents), dbg); + gtk_action_group_add_toggle_actions(dbg->misc_actions, misc_toggle_ents, + G_N_ELEMENTS(misc_toggle_ents), dbg); + gtk_ui_manager_insert_action_group(uimgr, dbg->misc_actions, 0); + + dbg->prev_stack_action = gtk_action_group_get_action(dbg->paused_actions, + "prev-stack-entry"); + + accelgrp = gtk_ui_manager_get_accel_group(uimgr); + gtk_window_add_accel_group(GTK_WINDOW(dbg->window), accelgrp); + + if (!gtk_ui_manager_add_ui_from_string(uimgr, uidesc, -1, &err)) + g_error("Failed to create menus: %s", err->message); + + menubar = gtk_ui_manager_get_widget(uimgr, "/menu-bar"); + gtk_box_pack_start(GTK_BOX(vbox2), menubar, FALSE, FALSE, 0); + + toolbar = gtk_ui_manager_get_widget(uimgr, "/toolbar"); + gtk_box_pack_start(GTK_BOX(vbox2), toolbar, FALSE, FALSE, 0); + + g_object_unref(uimgr); + + hbox = gtk_hbox_new(FALSE, 6); + + vpaned = gtk_vpaned_new(); + + /* Disassembly view */ + + dbg->disasm_view = tilem_disasm_view_new(dbg); + tilem_disasm_view_set_logical(TILEM_DISASM_VIEW(dbg->disasm_view), + dbg->mem_logical); + sw = new_scrolled_window(dbg->disasm_view); + gtk_paned_pack1(GTK_PANED(vpaned), sw, TRUE, TRUE); + + /* Memory view */ + + dbg->mem_view = tilem_debugger_mem_view_new(dbg); + sw = new_scrolled_window(dbg->mem_view); + gtk_paned_pack2(GTK_PANED(vpaned), sw, TRUE, TRUE); + + gtk_box_pack_start(GTK_BOX(hbox), vpaned, TRUE, TRUE, 0); + + vbox = gtk_vbox_new(FALSE, 6); + + /* Registers */ + + dbg->regbox = create_registers(dbg); + gtk_box_pack_start(GTK_BOX(vbox), dbg->regbox, FALSE, FALSE, 0); + + /* Stack view */ + + dbg->stack_view = create_stack_view(); + sw = new_scrolled_window(dbg->stack_view); + gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, TRUE, 0); + gtk_widget_show_all(vbox2); + + gtk_container_add(GTK_CONTAINER(dbg->window), vbox2); + + tilem_debugger_calc_changed(dbg); + + return dbg; +} + +/* Save the dimension for the debugger */ +static void save_debugger_dimension(TilemDebugger *dbg) +{ + gint width, height; + + if (!dbg->window) + return; + + gtk_window_get_size(GTK_WINDOW(dbg->window), &width, &height); + + if (width <= 0 || height <= 0) + return; + + tilem_config_set("debugger", + "width/i", width, + "height/i", height, + NULL); +} + +static void free_all_breakpoints(TilemDebugger *dbg) +{ + GSList *l; + TilemDebugBreakpoint *bp; + + for (l = dbg->breakpoints; l; l = l->next) { + bp = l->data; + g_slice_free(TilemDebugBreakpoint, bp); + } + + g_slist_free(dbg->breakpoints); + dbg->breakpoints = NULL; +} + +/* Free a TilemDebugger. */ +void tilem_debugger_free(TilemDebugger *dbg) +{ + g_return_if_fail(dbg != NULL); + + save_debugger_dimension(dbg); + + if (dbg->emu && dbg->emu->dbg == dbg) + dbg->emu->dbg = NULL; + if (dbg->window) { + gtk_widget_destroy(dbg->window); + } + if (dbg->dasm) + tilem_disasm_free(dbg->dasm); + if (dbg->run_actions) + g_object_unref(dbg->run_actions); + if (dbg->paused_actions) + g_object_unref(dbg->paused_actions); + if (dbg->misc_actions) + g_object_unref(dbg->misc_actions); + + free_all_breakpoints(dbg); + + g_slice_free(TilemDebugger, dbg); +} + +static void entry_printf(GtkWidget *ent, const char *s, ...) +{ + char buf[20]; + va_list ap; + va_start(ap, s); + g_vsnprintf(buf, sizeof(buf), s, ap); + va_end(ap); + gtk_entry_set_text(GTK_ENTRY(ent), buf); +} + +static void refresh_regs(TilemDebugger *dbg) +{ + TilemCalc *calc = dbg->emu->calc; + int i; + GtkToggleButton *btn; + + entry_printf(dbg->reg_entries[R_AF], "%04X", calc->z80.r.af.w.l); + entry_printf(dbg->reg_entries[R_BC], "%04X", calc->z80.r.bc.w.l); + entry_printf(dbg->reg_entries[R_DE], "%04X", calc->z80.r.de.w.l); + entry_printf(dbg->reg_entries[R_HL], "%04X", calc->z80.r.hl.w.l); + entry_printf(dbg->reg_entries[R_AF2], "%04X", calc->z80.r.af2.w.l); + entry_printf(dbg->reg_entries[R_BC2], "%04X", calc->z80.r.bc2.w.l); + entry_printf(dbg->reg_entries[R_DE2], "%04X", calc->z80.r.de2.w.l); + entry_printf(dbg->reg_entries[R_HL2], "%04X", calc->z80.r.hl2.w.l); + entry_printf(dbg->reg_entries[R_SP], "%04X", calc->z80.r.sp.w.l); + entry_printf(dbg->reg_entries[R_PC], "%04X", calc->z80.r.pc.w.l); + entry_printf(dbg->reg_entries[R_IX], "%04X", calc->z80.r.ix.w.l); + entry_printf(dbg->reg_entries[R_IY], "%04X", calc->z80.r.iy.w.l); + entry_printf(dbg->reg_entries[R_I], "%02X", calc->z80.r.ir.b.h); + entry_printf(dbg->reg_entries[R_IM], "%d", calc->z80.r.im); + + for (i = 0; i < 8; i++) { + btn = GTK_TOGGLE_BUTTON(dbg->flag_buttons[i]); + gtk_toggle_button_set_active(btn, calc->z80.r.af.d & (1 << i)); + } + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dbg->iff_checkbox), + calc->z80.r.iff1); +} + +/* Create GtkListStore and attach it */ +static GtkTreeModel* fill_stk_list(TilemDebugger *dbg) +{ + GtkListStore *store; + GtkTreeIter iter; + char stack_offset[10]; + char stack_value[10]; + dword i, v; + int n = 0; + + store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + i = dbg->emu->calc->z80.r.sp.w.l; + while (i < 0x10000 && n < 512) { + g_snprintf(stack_offset, sizeof(stack_offset), "%04X:", i); + + v = read_mem_word(dbg->emu->calc, i); + g_snprintf(stack_value, sizeof(stack_value), "%04X", v); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COL_OFFSET_STK, stack_offset, + COL_VALUE_STK, stack_value, -1); + + i += 0x0002; + n++; + } + + return GTK_TREE_MODEL (store); +} + +static void refresh_stack(TilemDebugger *dbg) +{ + GtkTreeModel *model = fill_stk_list(dbg); + gtk_tree_view_set_model(GTK_TREE_VIEW(dbg->stack_view), model); + g_object_unref(model); + + fixed_tree_view_init(dbg->stack_view, 0, + COL_OFFSET_STK, "DDDD: ", + COL_VALUE_STK, "DDDD ", + -1); +} + +static void unselect_all(GtkTreeView *tv) +{ + GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); + gtk_tree_selection_unselect_all(sel); +} + +static void refresh_all(TilemDebugger *dbg, gboolean updatemem) +{ + TilemCalc *calc; + gboolean paused; + gboolean updatedasm = FALSE; + GtkTreeModel *model; + + dbg->refreshing = TRUE; + dbg->delayed_refresh = FALSE; + + tilem_calc_emulator_lock(dbg->emu); + calc = dbg->emu->calc; + paused = dbg->emu->paused; + + if (calc) { + refresh_regs(dbg); + + if (dbg->lastwrite != calc->z80.lastwrite) + updatemem = TRUE; + + if (updatemem || dbg->lastsp != calc->z80.r.sp.d) + refresh_stack(dbg); + + if (paused && dbg->lastpc != calc->z80.r.pc.d) + updatedasm = TRUE; + + dbg->lastwrite = calc->z80.lastwrite; + dbg->lastsp = calc->z80.r.sp.d; + dbg->lastpc = calc->z80.r.pc.d; + } + + tilem_calc_emulator_unlock(dbg->emu); + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(dbg->mem_view)); + tilem_mem_model_clear_cache(TILEM_MEM_MODEL(model)); + + gtk_widget_queue_draw(dbg->mem_view); + + if (paused != dbg->paused) { + dbg->paused = paused; + gtk_widget_set_sensitive(dbg->regbox, paused); + gtk_widget_set_sensitive(dbg->disasm_view, paused); + gtk_widget_set_sensitive(dbg->mem_view, paused); + gtk_widget_set_sensitive(dbg->stack_view, paused); + gtk_action_group_set_sensitive(dbg->run_actions, !paused); + gtk_action_group_set_sensitive(dbg->paused_actions, paused); + updatedasm = TRUE; /* need to redraw icons */ + } + + if (updatemem || updatedasm) + tilem_disasm_view_refresh(TILEM_DISASM_VIEW(dbg->disasm_view)); + + if (!paused) { + unselect_all(GTK_TREE_VIEW(dbg->disasm_view)); + unselect_all(GTK_TREE_VIEW(dbg->mem_view)); + unselect_all(GTK_TREE_VIEW(dbg->stack_view)); + } + + if (dbg->keypad_dialog) + tilem_keypad_dialog_refresh(dbg->keypad_dialog); + + dbg->refreshing = FALSE; +} + +/* Show debugger, and pause emulator if not already paused. */ +void tilem_debugger_show(TilemDebugger *dbg) +{ + g_return_if_fail(dbg != NULL); + g_return_if_fail(dbg->emu->calc != NULL); + tilem_calc_emulator_pause(dbg->emu); + cancel_step_bp(dbg); + refresh_all(dbg, TRUE); + go_to_stack_pos(dbg, -1); + gtk_window_present(GTK_WINDOW(dbg->window)); +} + +/* Hide debugger, and resume emulation if not already running. */ +void tilem_debugger_hide(TilemDebugger *dbg) +{ + g_return_if_fail(dbg != NULL); + gtk_widget_hide(dbg->window); + tilem_calc_emulator_run(dbg->emu); + save_debugger_dimension(dbg); +} + +/* New calculator loaded. */ +void tilem_debugger_calc_changed(TilemDebugger *dbg) +{ + TilemCalc *calc; + + g_return_if_fail(dbg != NULL); + + tilem_disasm_free(dbg->dasm); + dbg->dasm = tilem_disasm_new(); + + dbg->step_bp = 0; + + free_all_breakpoints(dbg); + + calc = dbg->emu->calc; + if (!calc) + return; + + load_default_symbols(dbg); + + tilem_debugger_mem_view_configure(dbg->mem_view, + dbg->emu, dbg->mem_rowsize, + dbg->mem_start, dbg->mem_logical); + + tilem_debugger_refresh(dbg, TRUE); + + if (dbg->keypad_dialog) + tilem_keypad_dialog_calc_changed(dbg->keypad_dialog); +} + +/* Update display. */ +void tilem_debugger_refresh(TilemDebugger *dbg, gboolean updatemem) +{ + g_return_if_fail(dbg != NULL); + + if (!gtk_widget_get_visible(dbg->window)) + return; + + refresh_all(dbg, updatemem); +} + +/* Breakpoint manipulation */ + +/* Convert debugger type/mode into a core breakpoint type (core + breakpoints have only a single access type, for efficiency, so a + single TilemDebugBreakpoint may correspond to up to 3 core + breakpoints) */ +static int get_core_bp_type(int type, int mode) +{ + switch (type) { + case TILEM_DB_BREAK_LOGICAL: + switch (mode) { + case TILEM_DB_BREAK_READ: + return TILEM_BREAK_MEM_READ; + case TILEM_DB_BREAK_WRITE: + return TILEM_BREAK_MEM_WRITE; + case TILEM_DB_BREAK_EXEC: + return TILEM_BREAK_MEM_EXEC; + } + break; + + case TILEM_DB_BREAK_PHYSICAL: + switch (mode) { + case TILEM_DB_BREAK_READ: + return TILEM_BREAK_MEM_READ | TILEM_BREAK_PHYSICAL; + case TILEM_DB_BREAK_WRITE: + return TILEM_BREAK_MEM_WRITE | TILEM_BREAK_PHYSICAL; + case TILEM_DB_BREAK_EXEC: + return TILEM_BREAK_MEM_EXEC | TILEM_BREAK_PHYSICAL; + } + break; + + case TILEM_DB_BREAK_PORT: + switch (mode) { + case TILEM_DB_BREAK_READ: + return TILEM_BREAK_PORT_READ; + case TILEM_DB_BREAK_WRITE: + return TILEM_BREAK_PORT_WRITE; + } + break; + + case TILEM_DB_BREAK_OPCODE: + return TILEM_BREAK_EXECUTE; + } + + g_return_val_if_reached(0); +} + +/* Install core breakpoint(s) */ +static void set_bp(TilemDebugger *dbg, TilemDebugBreakpoint *bp) +{ + int i, t, n; + + tilem_calc_emulator_lock(dbg->emu); + for (i = 0; i < 3; i++) { + if (!bp->disabled && (bp->mode & (1 << i))) { + t = get_core_bp_type(bp->type, (1 << i)); + n = tilem_z80_add_breakpoint(dbg->emu->calc, t, + bp->start, bp->end, + bp->mask, + NULL, NULL); + bp->id[i] = n; + } + else { + bp->id[i] = 0; + } + } + tilem_calc_emulator_unlock(dbg->emu); +} + +/* Remove core breakpoint(s) */ +static void unset_bp(TilemDebugger *dbg, TilemDebugBreakpoint *bp) +{ + int i; + tilem_calc_emulator_lock(dbg->emu); + for (i = 0; i < 3; i++) { + if (bp->id[i]) + tilem_z80_remove_breakpoint(dbg->emu->calc, bp->id[i]); + bp->id[i] = 0; + } + tilem_calc_emulator_unlock(dbg->emu); +} + +static gboolean is_mem_exec_bp(const TilemDebugBreakpoint *bp) +{ + return ((bp->type == TILEM_DB_BREAK_LOGICAL + || bp->type == TILEM_DB_BREAK_PHYSICAL) + && (bp->mode & TILEM_DB_BREAK_EXEC)); +} + +/* Add a new debugger breakpoint */ +TilemDebugBreakpoint * tilem_debugger_add_breakpoint(TilemDebugger *dbg, + const TilemDebugBreakpoint *bp) +{ + TilemDebugBreakpoint *bp2; + + g_return_val_if_fail(dbg != NULL, NULL); + g_return_val_if_fail(bp != NULL, NULL); + g_return_val_if_fail(bp->mode != 0, NULL); + + bp2 = g_slice_new(TilemDebugBreakpoint); + *bp2 = *bp; + dbg->breakpoints = g_slist_append(dbg->breakpoints, bp2); + set_bp(dbg, bp2); + + if (is_mem_exec_bp(bp) && dbg->disasm_view) + tilem_disasm_view_refresh(TILEM_DISASM_VIEW(dbg->disasm_view)); + + return bp2; +} + +/* Remove a debugger breakpoint */ +void tilem_debugger_remove_breakpoint(TilemDebugger *dbg, + TilemDebugBreakpoint *bp) +{ + gboolean isexec; + + g_return_if_fail(dbg != NULL); + g_return_if_fail(bp != NULL); + g_return_if_fail(g_slist_index(dbg->breakpoints, bp) != -1); + + isexec = is_mem_exec_bp(bp); + + unset_bp(dbg, bp); + dbg->breakpoints = g_slist_remove(dbg->breakpoints, bp); + g_slice_free(TilemDebugBreakpoint, bp); + + if (isexec && dbg->disasm_view) + tilem_disasm_view_refresh(TILEM_DISASM_VIEW(dbg->disasm_view)); +} + +/* Modify a debugger breakpoint */ +void tilem_debugger_change_breakpoint(TilemDebugger *dbg, + TilemDebugBreakpoint *bp, + const TilemDebugBreakpoint *newbp) +{ + gboolean isexec; + + g_return_if_fail(dbg != NULL); + g_return_if_fail(bp != NULL); + g_return_if_fail(newbp != NULL); + g_return_if_fail(g_slist_index(dbg->breakpoints, bp) != -1); + + isexec = (is_mem_exec_bp(bp) || is_mem_exec_bp(newbp)); + + unset_bp(dbg, bp); + *bp = *newbp; + set_bp(dbg, bp); + + if (isexec && dbg->disasm_view) + tilem_disasm_view_refresh(TILEM_DISASM_VIEW(dbg->disasm_view)); +} diff --git a/tool/tilem-src/gui/debugger.h b/tool/tilem-src/gui/debugger.h new file mode 100644 index 0000000..a0e9823 --- /dev/null +++ b/tool/tilem-src/gui/debugger.h @@ -0,0 +1,164 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +typedef struct _TilemDebugBreakpoint { + enum { + TILEM_DB_BREAK_LOGICAL, + TILEM_DB_BREAK_PHYSICAL, + TILEM_DB_BREAK_PORT, + TILEM_DB_BREAK_OPCODE + } type; + + enum { + TILEM_DB_BREAK_READ = 4, + TILEM_DB_BREAK_WRITE = 2, + TILEM_DB_BREAK_EXEC = 1 + } mode; + + dword start; + dword end; + dword mask; + + int disabled; + int id[3]; +} TilemDebugBreakpoint; + +typedef struct _TilemDebugger { + struct _TilemCalcEmulator *emu; + struct _TilemDisasm *dasm; + + /* Debugger widgets */ + GtkWidget *window; /* Main debugger window */ + + GtkWidget *disasm_view; /* Disassembly view */ + GtkWidget *mem_view; /* Memory view */ + GtkWidget *stack_view; /* Stack view */ + + GtkWidget *regbox; /* Box containing registers */ + GtkWidget *reg_entries[14]; /* Entries for registers */ + GtkWidget *iff_checkbox; /* Checkbox for IFF */ + GtkWidget *flag_buttons[8]; /* Buttons for flags */ + + /* Action groups */ + GtkActionGroup *run_actions; + GtkActionGroup *paused_actions; + GtkActionGroup *misc_actions; + + /* Memory settings */ + int mem_rowsize; + int mem_start; + gboolean mem_logical; + + /* Stack navigation */ + GtkAction *prev_stack_action; + int stack_index; + + /* Breakpoints */ + GSList *breakpoints; + int last_bp_type; + int last_bp_mode; + + /* Temporary breakpoint info */ + int step_bp; /* Breakpoint ID */ + dword step_next_addr; /* Target address */ + + dword lastwrite; + dword lastsp; + dword lastpc; + gboolean paused; + gboolean refreshing; + gboolean delayed_refresh; + + /* Other windows */ + struct _TilemKeypadDialog *keypad_dialog; +} TilemDebugger; + +/* Create a new TilemDebugger. */ +TilemDebugger *tilem_debugger_new(TilemCalcEmulator *emu); + +/* Free a TilemDebugger. */ +void tilem_debugger_free(TilemDebugger *dbg); + +/* Show debugger, and pause emulator if not already paused. */ +void tilem_debugger_show(TilemDebugger *dbg); + +/* Hide debugger, and resume emulation if not already running. */ +void tilem_debugger_hide(TilemDebugger *dbg); + +/* New calculator loaded. */ +void tilem_debugger_calc_changed(TilemDebugger *dbg); + +/* Update display. */ +void tilem_debugger_refresh(TilemDebugger *dbg, gboolean updatemem); + +/* Add a new breakpoint. */ +TilemDebugBreakpoint * tilem_debugger_add_breakpoint(TilemDebugger *dbg, + const TilemDebugBreakpoint *bp); + +/* Remove an existing breakpoint. */ +void tilem_debugger_remove_breakpoint(TilemDebugger *dbg, + TilemDebugBreakpoint *bp); + +/* Modify an existing breakpoint. */ +void tilem_debugger_change_breakpoint(TilemDebugger *dbg, + TilemDebugBreakpoint *bp, + const TilemDebugBreakpoint *newbp); + +/* Show a dialog letting the user add, remove, and edit breakpoints. */ +void tilem_debugger_edit_breakpoints(TilemDebugger *dbg); + + +/* Memory view */ + +/* Create the memory view */ +GtkWidget *tilem_debugger_mem_view_new(TilemDebugger *dbg); + +/* Set memory view settings */ +void tilem_debugger_mem_view_configure(GtkWidget *mem_view, + TilemCalcEmulator *emu, + int rowsize, int start, + gboolean logical); + + +/* Keypad dialog */ + +typedef struct _TilemKeypadDialog { + TilemDebugger *dbg; + + gboolean refreshing; + + GtkWidget *window; + GtkWidget *output[7]; + GtkWidget *keys[7][8]; + GtkWidget *input[8]; +} TilemKeypadDialog; + +/* Create a new TilemKeypadDialog. */ +TilemKeypadDialog *tilem_keypad_dialog_new(TilemDebugger *dbg); + +/* Free a TilemKeypadDialog. */ +void tilem_keypad_dialog_free(TilemKeypadDialog *kpdlg); + +/* New calculator loaded. */ +void tilem_keypad_dialog_calc_changed(TilemKeypadDialog *kpdlg); + +/* Refresh key states. */ +void tilem_keypad_dialog_refresh(TilemKeypadDialog *kpdlg); + diff --git a/tool/tilem-src/gui/disasmview.c b/tool/tilem-src/gui/disasmview.c new file mode 100644 index 0000000..a664b90 --- /dev/null +++ b/tool/tilem-src/gui/disasmview.c @@ -0,0 +1,1200 @@ +/* + * TilEm II + * + * Copyright (c) 2011-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "disasmview.h" + +G_DEFINE_TYPE(TilemDisasmView, tilem_disasm_view, GTK_TYPE_TREE_VIEW); + +/* + This is a HORRIBLE kludge. Don't ever do anything like this. ;) + + We want a widget that has the look and feel of a GtkTreeView. But + our "data model" isn't consistent with a GtkTreeModel, since it + changes depending on where we are. + + This widget keeps track of how high each row will be once rendered, + and uses that to construct a GtkListStore with the appropriate + number of rows to fill the window. We also override the move-cursor + signal so that we can handle the boundaries. + */ + +/* Model columns */ +enum { + COL_POSITION, + COL_ADDRESS, + COL_MNEMONIC, + COL_ARGUMENTS, + COL_SHOW_MNEMONIC, + COL_ICON, + NUM_COLUMNS +}; + +static GtkTreeViewClass *parent_class; + +/* We define two "positions" for each actual address; the second is + used if there's a label to be displayed at that address. */ + +#define POS_TO_ADDR(x) ((x) >> 1) +#define ADDR_TO_POS(x) ((x) << 1) + +/* Disassembly */ + +/* Convert physical to logical address; if address is not currently + mapped, use the bank-A address */ +static dword default_ptol(TilemCalc *calc, dword addr) +{ + dword addr_l; + + g_return_val_if_fail(calc != NULL, 0); + + addr_l = (*calc->hw.mem_ptol)(calc, addr); + if (addr_l == 0xffffffff) + addr_l = (addr & 0x3fff) | 0x4000; + + return addr_l; +} + +/* Check for a label at the given address (physical or logical + depending on the mode of the DisasmView) */ +static const char *get_label(TilemDisasmView *dv, TilemCalc *calc, + dword addr) +{ + g_return_val_if_fail(calc != NULL, NULL); + g_return_val_if_fail(dv->dbg->dasm != NULL, NULL); + + if (!dv->use_logical) + addr = default_ptol(calc, addr); + + return tilem_disasm_get_label_at_address(dv->dbg->dasm, addr); +} + +/* Disassemble a line */ +static void disassemble(TilemDisasmView *dv, TilemCalc *calc, dword pos, + dword *nextpos, char **mnemonic, char **args) +{ + dword addr = POS_TO_ADDR(pos); + const char *lbl; + char buf[500], *p; + + g_return_if_fail(calc != NULL); + g_return_if_fail(dv->dbg->dasm != NULL); + + if (!(pos & 1) && (lbl = get_label(dv, calc, addr))) { + if (mnemonic) { + *mnemonic = NULL; + *args = g_strdup_printf("%s:", lbl); + } + + if (nextpos) + *nextpos = pos + 1; + } + else if (mnemonic) { + tilem_disasm_disassemble(dv->dbg->dasm, calc, + !dv->use_logical, addr, + &addr, buf, sizeof(buf)); + + p = strchr(buf, '\t'); + if (p) { + *mnemonic = g_strndup(buf, p - buf); + *args = g_strdup(p + 1); + } + else { + *mnemonic = g_strdup(buf); + *args = NULL; + } + + if (nextpos) + *nextpos = ADDR_TO_POS(addr); + } + else { + tilem_disasm_disassemble(dv->dbg->dasm, calc, + !dv->use_logical, addr, + &addr, NULL, 0); + if (nextpos) + *nextpos = ADDR_TO_POS(addr); + } +} + +/* Get "next" position */ +static dword get_next_pos(TilemDisasmView *dv, TilemCalc *calc, dword pos) +{ + disassemble(dv, calc, pos, &pos, NULL, NULL); + return pos; +} + +/* Get "previous" position */ +static dword get_prev_pos(TilemDisasmView *dv, TilemCalc *calc, dword pos) +{ + dword addr = POS_TO_ADDR(pos); + + g_return_val_if_fail(calc != NULL, 0); + + if (pos & 1) { + return pos - 1; + } + else { + if (addr > 0) + addr--; + else if (dv->use_logical) + addr = 0xffff; + else + addr = (calc->hw.romsize + calc->hw.ramsize - 1); + + if (get_label(dv, calc, addr)) + return ADDR_TO_POS(addr) + 1; + else + return ADDR_TO_POS(addr); + } +} + +/* Convert physical to logical position */ +static dword pos_ptol(TilemCalc *calc, dword pos) +{ + dword addr; + + g_return_val_if_fail(calc != NULL, 0); + + if (pos == (dword) -1) + return pos; + + addr = default_ptol(calc, POS_TO_ADDR(pos)); + return ADDR_TO_POS(addr) + (pos & 1); +} + +/* Convert logical to physical position */ +static dword pos_ltop(TilemCalc *calc, dword pos) +{ + dword addr; + + g_return_val_if_fail(calc != NULL, 0); + + if (pos == (dword) -1) + return pos; + + addr = (*calc->hw.mem_ltop)(calc, POS_TO_ADDR(pos)); + return ADDR_TO_POS(addr) + (pos & 1); +} + +/* Icons */ + +static GdkPixbuf *get_icon(TilemDisasmView *dv, gboolean ispc, gboolean isbp) +{ + const char *name; + + if (ispc && isbp) + name = "tilem-disasm-break-pc"; + else if (isbp) + name = "tilem-disasm-break"; + else if (ispc) + name = "tilem-disasm-pc"; + else + return NULL; + + return gtk_widget_render_icon(GTK_WIDGET(dv), name, + GTK_ICON_SIZE_MENU, NULL); +} + +/* List model management */ + +/* Create a new list store for disassembly */ +static GtkTreeModel * new_dasm_model() +{ + GtkListStore *store; + + g_assert(NUM_COLUMNS == 6); + store = gtk_list_store_new(6, + G_TYPE_INT, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_BOOLEAN, + GDK_TYPE_PIXBUF); + + return GTK_TREE_MODEL(store); +} + +/* Append dummy data to the model; used for sizing */ +static void append_dummy_line(TilemDisasmView *dv, GtkTreeModel *model, + GtkTreeIter *iter) +{ + GtkTreeIter iter1; + GdkPixbuf *icon; + + gtk_list_store_append(GTK_LIST_STORE(model), &iter1); + + icon = get_icon(dv, TRUE, FALSE); + + gtk_list_store_set(GTK_LIST_STORE(model), &iter1, + COL_ICON, icon, + COL_ADDRESS, "DD:DDDD", + COL_MNEMONIC, "ROM_CALL", + COL_ARGUMENTS, "_fnord", + COL_SHOW_MNEMONIC, TRUE, + -1); + + if (icon) + g_object_unref(icon); + + if (iter) + *iter = iter1; +} + +/* Check if given logical address is a breakpoint (according to + current mapping) */ +static TilemDebugBreakpoint *find_bp_logical(TilemDebugger *dbg, + TilemCalc *calc, + dword addr) +{ + GSList *l; + TilemDebugBreakpoint *bp; + dword pa = (*calc->hw.mem_ltop)(calc, addr); + + for (l = dbg->breakpoints; l; l = l->next) { + bp = l->data; + if (!(bp->mode & TILEM_DB_BREAK_EXEC)) + continue; + + if (bp->type == TILEM_DB_BREAK_LOGICAL + && bp->start <= addr + && bp->end >= addr + && !bp->disabled) + return bp; + + if (bp->type == TILEM_DB_BREAK_PHYSICAL + && bp->start <= pa + && bp->end >= pa + && !bp->disabled) + return bp; + } + + return NULL; +} + +/* Check if given physical address is a breakpoint (according to + current mapping) */ +static TilemDebugBreakpoint *find_bp_physical(TilemDebugger *dbg, + TilemCalc *calc, + dword addr) +{ + GSList *l; + TilemDebugBreakpoint *bp; + dword la, pa; + int i, mapped[4]; + + /* NOTE: this assumes that bits 0-13 are unaffected by the + mapping! This is true for all current models, but might + need to be changed in the future */ + for (i = 0; i < 4; i++) { + la = (i << 14) + (addr & 0x3fff); + pa = (*calc->hw.mem_ltop)(calc, la); + mapped[i] = (addr == pa); + } + + for (l = dbg->breakpoints; l; l = l->next) { + bp = l->data; + if (!(bp->mode & TILEM_DB_BREAK_EXEC)) + continue; + + if (bp->type == TILEM_DB_BREAK_PHYSICAL + && bp->start <= addr + && bp->end >= addr + && !bp->disabled) + return bp; + + if (bp->type == TILEM_DB_BREAK_LOGICAL + && !bp->disabled) { + for (i = 0; i < 4; i++) { + la = (i << 14) + (addr & 0x3fff); + if (bp->start <= la + && bp->end >= la + && mapped[i]) + return bp; + } + } + } + + return NULL; +} + +/* Check if line has a breakpoint set */ +static TilemDebugBreakpoint *find_line_bp(TilemDisasmView *dv, dword pos) +{ + TilemDebugBreakpoint *bp; + dword addr = POS_TO_ADDR(pos); + TilemCalc *calc; + + tilem_calc_emulator_lock(dv->dbg->emu); + calc = dv->dbg->emu->calc; + + if (dv->use_logical) + bp = find_bp_logical(dv->dbg, calc, addr); + else + bp = find_bp_physical(dv->dbg, calc, addr); + + tilem_calc_emulator_unlock(dv->dbg->emu); + + return bp; +} + +/* Enable breakpoint on the given line */ +static void enable_line_bp(TilemDisasmView *dv, dword pos) +{ + TilemDebugBreakpoint *bp, tmpbp; + + if ((bp = find_line_bp(dv, pos))) + return; + + tmpbp.type = (dv->use_logical + ? TILEM_DB_BREAK_LOGICAL + : TILEM_DB_BREAK_PHYSICAL); + tmpbp.mode = TILEM_DB_BREAK_EXEC; + tmpbp.start = POS_TO_ADDR(pos); + tmpbp.end = POS_TO_ADDR(pos); + tmpbp.mask = (dv->use_logical ? 0xffff : 0xffffffff); + tmpbp.disabled = 0; + tilem_debugger_add_breakpoint(dv->dbg, &tmpbp); +} + +/* Disable breakpoint on the given line */ +static void disable_line_bp(TilemDisasmView *dv, dword pos) +{ + TilemDebugBreakpoint *bp, tmpbp; + + if (!(bp = find_line_bp(dv, pos))) + return; + + if (bp->mode != TILEM_DB_BREAK_EXEC || bp->start != bp->end) { + /* special breakpoint; do not delete it, just disable it */ + tmpbp = *bp; + tmpbp.disabled = 1; + tilem_debugger_change_breakpoint(dv->dbg, bp, &tmpbp); + } + else { + /* regular breakpoint */ + tilem_debugger_remove_breakpoint(dv->dbg, bp); + } +} + +/* Append a line to the dasm model */ +static void append_dasm_line(TilemDisasmView *dv, TilemCalc *calc, + GtkTreeModel *model, GtkTreeIter *iter, + dword pos, dword *nextpos) +{ + GtkTreeIter iter1; + char *astr, *mnem, *args; + dword addr, pc; + gboolean ispc; + TilemDebugBreakpoint *bp; + GdkPixbuf *icon; + + g_return_if_fail(calc != NULL); + + gtk_list_store_append(GTK_LIST_STORE(model), &iter1); + + addr = POS_TO_ADDR(pos); + astr = tilem_format_addr(dv->dbg, addr, !dv->use_logical); + + disassemble(dv, calc, pos, nextpos, &mnem, &args); + + if (!mnem) + bp = NULL; + else if (dv->use_logical) + bp = find_bp_logical(dv->dbg, calc, addr); + else + bp = find_bp_physical(dv->dbg, calc, addr); + + if (!mnem || !dv->dbg->emu->paused) { + ispc = FALSE; + } + else { + pc = calc->z80.r.pc.w.l; + if (!dv->use_logical) + pc = (*calc->hw.mem_ltop)(calc, pc); + ispc = (addr == pc); + } + + icon = get_icon(dv, ispc, (bp != NULL)); + + gtk_list_store_set(GTK_LIST_STORE(model), &iter1, + COL_POSITION, (int) pos, + COL_ADDRESS, astr, + COL_MNEMONIC, mnem, + COL_SHOW_MNEMONIC, (mnem ? TRUE : FALSE), + COL_ARGUMENTS, args, + COL_ICON, icon, + -1); + + if (icon) + g_object_unref(icon); + + g_free(astr); + g_free(mnem); + g_free(args); + + if (iter) + *iter = iter1; +} + +/* Refresh the view by creating and populating a new model */ +static void refresh_disassembly(TilemDisasmView *dv, dword pos, int nlines, + dword selectpos) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *selectpath = NULL; + TilemCalc *calc; + dword nextpos; + int i; + + model = new_dasm_model(); + + dv->startpos = pos; + + tilem_calc_emulator_lock(dv->dbg->emu); + calc = dv->dbg->emu->calc; + + if (!calc) + nlines = 0; + + for (i = 0; i < nlines; i++) { + append_dasm_line(dv, calc, model, &iter, pos, &nextpos); + + if (pos == selectpos) + selectpath = gtk_tree_model_get_path(model, &iter); + + pos = nextpos; + } + + tilem_calc_emulator_unlock(dv->dbg->emu); + + dv->endpos = pos; + dv->nlines = nlines; + + gtk_tree_view_set_model(GTK_TREE_VIEW(dv), model); + g_object_unref(model); + + if (selectpath) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(dv), selectpath, + NULL, FALSE); + gtk_tree_path_free(selectpath); + } +} + +/* Determine the (absolute) position and (display-relative) line + number of the cursor, if any */ +static gboolean get_cursor_line(TilemDisasmView *dv, dword *pos, + int *linenum) +{ + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeIter iter; + gint *i, p; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(dv), &path, NULL); + if (!path) { + if (pos) *pos = (dword) -1; + if (linenum) *linenum = -1; + return FALSE; + } + + if (pos) { + model = gtk_tree_view_get_model(GTK_TREE_VIEW(dv)); + if (gtk_tree_model_get_iter(model, &iter, path)) { + gtk_tree_model_get(model, &iter, + COL_POSITION, &p, -1); + *pos = p; + } + else { + *pos = (dword) -1; + } + } + + if (linenum) { + i = gtk_tree_path_get_indices(path); + *linenum = i[0]; + } + + gtk_tree_path_free(path); + + return TRUE; +} + +/* Size allocation */ + +/* Get the desired height for the tree view (based on size of the data + we've inserted into the model) */ +static int get_parent_request_height(GtkWidget *w) +{ + GtkRequisition req; + (*GTK_WIDGET_CLASS(parent_class)->size_request)(w, &req); + return req.height; +} + +/* Widget is assigned a size and position */ +static void tilem_disasm_view_size_allocate(GtkWidget *w, + GtkAllocation *alloc) +{ + TilemDisasmView *dv; + GtkTreeModel *model; + dword curpos; + int n, height1, height2; + + g_return_if_fail(TILEM_IS_DISASM_VIEW(w)); + dv = TILEM_DISASM_VIEW(w); + + (*GTK_WIDGET_CLASS(parent_class)->size_allocate)(w, alloc); + + if (alloc->height < 1) + return; + + get_cursor_line(dv, &curpos, NULL); + + /* Calculate line height */ + if (!dv->line_height) { + model = new_dasm_model(); + + append_dummy_line(dv, model, NULL); + gtk_tree_view_set_model(GTK_TREE_VIEW(dv), model); + height1 = get_parent_request_height(w); + + append_dummy_line(dv, model, NULL); + height2 = get_parent_request_height(w); + + dv->line_height = height2 - height1; + dv->base_height = height1 - dv->line_height; + + g_object_unref(model); + + dv->nlines = 0; + + if (dv->line_height <= 0) { + dv->line_height = 0; + return; + } + } + + n = (alloc->height - dv->base_height) / dv->line_height; + + if (n < 1) + n = 1; + + if (n != dv->nlines) + refresh_disassembly(dv, dv->startpos, n, curpos); +} + +/* Get widget's desired size */ +static void tilem_disasm_view_size_request(GtkWidget *w, GtkRequisition *req) +{ + (*GTK_WIDGET_CLASS(parent_class)->size_request)(w, req); + req->height = 100; /* ignore requested height */ +} + +/* Widget style set */ +static void tilem_disasm_view_style_set(GtkWidget *w, GtkStyle *oldstyle) +{ + TilemDisasmView *dv; + GtkTreeModel *model; + GtkTreeIter iter; + GList *cols, *cp; + GtkTreeViewColumn *col; + int width; + + g_return_if_fail(TILEM_IS_DISASM_VIEW(w)); + dv = TILEM_DISASM_VIEW(w); + + (*GTK_WIDGET_CLASS(parent_class)->style_set)(w, oldstyle); + + /* line height must be recalculated */ + dv->line_height = 0; + + /* set column widths based on a dummy model */ + + model = new_dasm_model(); + append_dummy_line(dv, model, &iter); + + cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(dv)); + for (cp = cols; cp; cp = cp->next) { + col = cp->data; + gtk_tree_view_column_cell_set_cell_data(col, model, &iter, + FALSE, FALSE); + gtk_tree_view_column_cell_get_size(col, NULL, NULL, NULL, + &width, NULL); + gtk_tree_view_column_set_fixed_width(col, width + 2); + } + g_list_free(cols); + + g_object_unref(model); +} + +/* Cursor movement commands */ + +/* Move up by COUNT lines */ +static gboolean move_up_lines(TilemDisasmView *dv, int count) +{ + TilemCalc *calc; + dword pos; + int linenum; + + if (!get_cursor_line(dv, NULL, &linenum)) + linenum = 0; + + if (linenum >= count) + return FALSE; + + tilem_calc_emulator_lock(dv->dbg->emu); + calc = dv->dbg->emu->calc; + + pos = dv->startpos; + count -= linenum; + while (count > 0) { + pos = get_prev_pos(dv, calc, pos); + count--; + } + + tilem_calc_emulator_unlock(dv->dbg->emu); + + refresh_disassembly(dv, pos, dv->nlines, pos); + + return TRUE; +} + +/* Move down by COUNT lines */ +static gboolean move_down_lines(TilemDisasmView *dv, int count) +{ + TilemCalc *calc; + dword startpos, selpos; + int linenum; + + if (!get_cursor_line(dv, NULL, &linenum)) + linenum = -1; + + if (linenum + count < dv->nlines) + return FALSE; + + tilem_calc_emulator_lock(dv->dbg->emu); + calc = dv->dbg->emu->calc; + + startpos = get_next_pos(dv, calc, dv->startpos); + selpos = dv->endpos; + count -= dv->nlines - linenum; + + while (count > 0) { + startpos = get_next_pos(dv, calc, startpos); + selpos = get_next_pos(dv, calc, selpos); + count--; + } + + tilem_calc_emulator_unlock(dv->dbg->emu); + + refresh_disassembly(dv, startpos, dv->nlines, selpos); + + return TRUE; +} + +/* Move down by COUNT bytes */ +static void move_bytes(TilemDisasmView *dv, int count) +{ + dword pos, addr; + const TilemCalc *calc = dv->dbg->emu->calc; + + g_return_if_fail(calc != NULL); + + if (!get_cursor_line(dv, &pos, NULL)) + pos = dv->startpos; + + addr = POS_TO_ADDR(pos); + + if (dv->use_logical) + addr = (addr + count) & 0xffff; + else { + addr += calc->hw.romsize + calc->hw.ramsize + count; + addr %= calc->hw.romsize + calc->hw.ramsize; + } + + pos = ADDR_TO_POS(addr); + refresh_disassembly(dv, pos, dv->nlines, pos - 1); +} + +/* Move the cursor (action signal) */ +static gboolean tilem_disasm_view_move_cursor(GtkTreeView *tv, + GtkMovementStep step, + gint count) +{ + TilemDisasmView *dv; + + g_return_val_if_fail(TILEM_IS_DISASM_VIEW(tv), FALSE); + dv = TILEM_DISASM_VIEW(tv); + + if (!dv->dbg->emu->calc) + return FALSE; + + switch (step) { + case GTK_MOVEMENT_DISPLAY_LINES: + if (count < 0) { + if (move_up_lines(dv, -count)) + return TRUE; + } + else { + if (move_down_lines(dv, count)) + return TRUE; + } + break; + + case GTK_MOVEMENT_PARAGRAPHS: + case GTK_MOVEMENT_PARAGRAPH_ENDS: + case GTK_MOVEMENT_PAGES: + /* FIXME: might be better to move by actual "pages" of code */ + move_bytes(dv, count * 0x100); + return TRUE; + + case GTK_MOVEMENT_BUFFER_ENDS: + move_bytes(dv, count * 0x4000); + return TRUE; + + case GTK_MOVEMENT_LOGICAL_POSITIONS: + case GTK_MOVEMENT_VISUAL_POSITIONS: + case GTK_MOVEMENT_WORDS: + case GTK_MOVEMENT_DISPLAY_LINE_ENDS: + case GTK_MOVEMENT_HORIZONTAL_PAGES: + default: + break; + } + + return (*GTK_TREE_VIEW_CLASS(parent_class)->move_cursor)(tv, step, count); +} + +/* Popup menu */ + +static void toggle_bp(G_GNUC_UNUSED GtkCheckMenuItem *item, gpointer data) +{ + TilemDisasmView *dv = data; + tilem_disasm_view_toggle_breakpoint(dv); +} + +void tilem_disasm_view_toggle_breakpoint(TilemDisasmView *dv) +{ + dword curpos; + + g_return_if_fail(TILEM_IS_DISASM_VIEW(dv)); + + get_cursor_line(dv, &curpos, NULL); + if (curpos == (dword) -1) + return; + + if (find_line_bp(dv, curpos)) + disable_line_bp(dv, curpos); + else + enable_line_bp(dv, curpos); +} + +static void prompt_go_to(G_GNUC_UNUSED GtkMenuItem *item, gpointer data) +{ + TilemDisasmView *dv = data; + GtkWidget *window; + dword curpos, addr; + + window = gtk_widget_get_toplevel(GTK_WIDGET(dv)); + + get_cursor_line(dv, &curpos, NULL); + addr = POS_TO_ADDR(curpos); + + if (tilem_prompt_address(dv->dbg, GTK_WINDOW(window), + "Go to Address", "Address:", + &addr, !dv->use_logical, + (curpos != (dword) -1))) + tilem_disasm_view_go_to_address(dv, addr, dv->use_logical); +} + +static void go_to_pc(G_GNUC_UNUSED GtkMenuItem *item, gpointer data) +{ + TilemDisasmView *dv = data; + TilemCalc *calc; + dword pc; + + g_return_if_fail(dv->dbg != NULL); + g_return_if_fail(dv->dbg->emu != NULL); + g_return_if_fail(dv->dbg->emu->calc != NULL); + + tilem_calc_emulator_lock(dv->dbg->emu); + calc = dv->dbg->emu->calc; + pc = calc->z80.r.pc.w.l; + tilem_calc_emulator_unlock(dv->dbg->emu); + + tilem_disasm_view_go_to_address(dv, pc, TRUE); +} + +/* Determine where to pop up menu (if not activated by a mouse event) */ +static void place_menu(GtkMenu *menu, gint *x, gint *y, + gboolean *push_in, gpointer data) +{ + TilemDisasmView *dv = data; + GtkTreePath *path; + GdkRectangle rect; + GdkWindow *win; + GdkScreen *screen; + int n; + + win = gtk_tree_view_get_bin_window(GTK_TREE_VIEW(dv)); + gdk_window_get_origin(win, x, y); + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(dv), &path, NULL); + if (path) { + gtk_tree_view_get_cell_area(GTK_TREE_VIEW(dv), path, NULL, &rect); + gtk_tree_path_free(path); + *y += rect.y + rect.height; + } + + screen = gdk_drawable_get_screen(win); + n = gdk_screen_get_monitor_at_point(screen, *x, *y); + gtk_menu_set_monitor(menu, n); + + *push_in = FALSE; +} + +/* Create and show the popup menu */ +static void show_popup_menu(TilemDisasmView *dv, GdkEventButton *event) +{ + GtkWidget *menu, *item; + dword curpos; + + if (dv->popup_menu) + gtk_widget_destroy(dv->popup_menu); + dv->popup_menu = menu = gtk_menu_new(); + + /* Enable/disable breakpoint */ + + item = gtk_check_menu_item_new_with_mnemonic("_Breakpoint Here"); + + get_cursor_line(dv, &curpos, NULL); + if (curpos == (dword) -1) + gtk_widget_set_sensitive(item, FALSE); + else if (find_line_bp(dv, curpos)) + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE); + + g_signal_connect(item, "toggled", + G_CALLBACK(toggle_bp), dv); + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + item = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + /* Jump to address */ + + item = gtk_menu_item_new_with_mnemonic("_Go to Address..."); + g_signal_connect(item, "activate", G_CALLBACK(prompt_go_to), dv); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + item = gtk_menu_item_new_with_mnemonic("Go to P_C"); + g_signal_connect(item, "activate", G_CALLBACK(go_to_pc), dv); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + if (event) + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + event->button, event->time); + else + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, &place_menu, dv, + 0, gtk_get_current_event_time()); +} + +/* Button pressed */ +static gboolean tilem_disasm_view_button_press(GtkWidget *w, + GdkEventButton *event) +{ + g_return_val_if_fail(TILEM_IS_DISASM_VIEW(w), FALSE); + + (*GTK_WIDGET_CLASS(parent_class)->button_press_event)(w, event); + + if (event->button == 3) + show_popup_menu(TILEM_DISASM_VIEW(w), event); + + return TRUE; +} + +/* Key pressed to activate context menu */ +static gboolean tilem_disasm_view_popup_menu(GtkWidget *w) +{ + g_return_val_if_fail(TILEM_IS_DISASM_VIEW(w), FALSE); + show_popup_menu(TILEM_DISASM_VIEW(w), NULL); + return TRUE; +} + +/* Row activated (double-clicked) */ +static void tilem_disasm_view_row_activated(GtkTreeView *tv, GtkTreePath *path, + GtkTreeViewColumn *col) +{ + TilemDisasmView *dv = TILEM_DISASM_VIEW(tv); + GtkTreeModel *model; + GtkTreeIter iter; + gint pos; + + model = gtk_tree_view_get_model(tv); + if (!gtk_tree_model_get_iter(model, &iter, path)) + return; + + gtk_tree_model_get(model, &iter, COL_POSITION, &pos, -1); + + if (col == dv->icon_column) { + if (find_line_bp(dv, pos)) + disable_line_bp(dv, pos); + else + enable_line_bp(dv, pos); + } +} + +/* Unrealize widget */ +static void tilem_disasm_view_unrealize(GtkWidget *w) +{ + TilemDisasmView *dv = TILEM_DISASM_VIEW(w); + + if (dv->popup_menu) + gtk_widget_destroy(dv->popup_menu); + dv->popup_menu = NULL; + + (*GTK_WIDGET_CLASS(parent_class)->unrealize)(w); +} + +/* Initialize a new TilemDisasmView */ +static void tilem_disasm_view_init(TilemDisasmView *dv) +{ + GtkTreeView *tv = GTK_TREE_VIEW(dv); + GtkCellRenderer *cell; + GtkTreeViewColumn *col; + + dv->use_logical = TRUE; + + gtk_tree_view_set_enable_search(tv, FALSE); + gtk_tree_view_set_fixed_height_mode(tv, TRUE); + gtk_tree_view_set_headers_visible(tv, FALSE); + + cell = gtk_cell_renderer_pixbuf_new(); + col = gtk_tree_view_column_new_with_attributes(NULL, cell, + "pixbuf", COL_ICON, + NULL); + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_append_column(tv, col); + dv->icon_column = col; + + cell = gtk_cell_renderer_text_new(); + col = gtk_tree_view_column_new_with_attributes("Addr", cell, + "text", COL_ADDRESS, + NULL); + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_append_column(tv, col); + + col = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(col, "Disassembly"); + + cell = gtk_cell_renderer_text_new(); + g_object_set(cell, "xpad", 10, NULL); + gtk_tree_view_column_pack_start(col, cell, FALSE); + gtk_tree_view_column_set_attributes(col, cell, + "text", COL_MNEMONIC, + "visible", COL_SHOW_MNEMONIC, + NULL); + + cell = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(col, cell, TRUE); + gtk_tree_view_column_set_attributes(col, cell, + "text", COL_ARGUMENTS, + NULL); + + gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_expand(col, TRUE); + gtk_tree_view_append_column(tv, col); +} + +static const char default_style[] = + "style \"tilem-disasm-default\" { font_name = \"Monospace\" } " + "widget \"*.TilemDisasmView\" style:application \"tilem-disasm-default\""; + +/* Initialize the TilemDisasmView class */ +static void tilem_disasm_view_class_init(TilemDisasmViewClass *klass) +{ + GtkTreeViewClass *tv_class = GTK_TREE_VIEW_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + gtk_rc_parse_string(default_style); + + parent_class = g_type_class_peek_parent(klass); + + widget_class->style_set = &tilem_disasm_view_style_set; + widget_class->size_request = &tilem_disasm_view_size_request; + widget_class->size_allocate = &tilem_disasm_view_size_allocate; + widget_class->button_press_event = &tilem_disasm_view_button_press; + widget_class->popup_menu = &tilem_disasm_view_popup_menu; + widget_class->unrealize = &tilem_disasm_view_unrealize; + tv_class->move_cursor = &tilem_disasm_view_move_cursor; + tv_class->row_activated = &tilem_disasm_view_row_activated; +} + +GtkWidget * tilem_disasm_view_new(TilemDebugger *dbg) +{ + TilemDisasmView *dv; + + g_return_val_if_fail(dbg != NULL, NULL); + + dv = g_object_new(TILEM_TYPE_DISASM_VIEW, NULL); + dv->dbg = dbg; + + return GTK_WIDGET(dv); +} + +/* Select memory addressing mode. */ +void tilem_disasm_view_set_logical(TilemDisasmView *dv, gboolean logical) +{ + dword start, curpos; + TilemCalc *calc; + + g_return_if_fail(TILEM_IS_DISASM_VIEW(dv)); + g_return_if_fail(dv->dbg->emu->calc != NULL); + + get_cursor_line(dv, &curpos, NULL); + + if (logical && !dv->use_logical) { + tilem_calc_emulator_lock(dv->dbg->emu); + calc = dv->dbg->emu->calc; + curpos = pos_ptol(calc, curpos); + start = pos_ptol(calc, dv->startpos); + tilem_calc_emulator_unlock(dv->dbg->emu); + + dv->use_logical = TRUE; + refresh_disassembly(dv, start, dv->nlines, curpos); + } + else if (!logical && dv->use_logical) { + tilem_calc_emulator_lock(dv->dbg->emu); + calc = dv->dbg->emu->calc; + curpos = pos_ltop(calc, curpos); + start = pos_ltop(calc, dv->startpos); + tilem_calc_emulator_unlock(dv->dbg->emu); + + dv->use_logical = FALSE; + refresh_disassembly(dv, start, dv->nlines, curpos); + } +} + +/* Refresh contents of view. */ +void tilem_disasm_view_refresh(TilemDisasmView *dv) +{ + dword curpos; + g_return_if_fail(TILEM_IS_DISASM_VIEW(dv)); + get_cursor_line(dv, &curpos, NULL); + refresh_disassembly(dv, dv->startpos, dv->nlines, curpos); +} + +/* Find tree path for the given position */ +static GtkTreePath *find_path_for_position(GtkTreeModel *model, int pos) +{ + gint p; + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter_first(model, &iter)) + return NULL; + + do { + gtk_tree_model_get(model, &iter, COL_POSITION, &p, -1); + if (p == pos) { + return gtk_tree_model_get_path(model, &iter); + } + } while (gtk_tree_model_iter_next(model, &iter)); + + return NULL; +} + +/* Highlight the specified address. */ +void tilem_disasm_view_go_to_address(TilemDisasmView *dv, dword addr, + gboolean logical) +{ + dword pos; + GtkTreeModel *model; + GtkTreePath *path; + TilemCalc *calc; + + g_return_if_fail(TILEM_IS_DISASM_VIEW(dv)); + + tilem_calc_emulator_lock(dv->dbg->emu); + calc = dv->dbg->emu->calc; + + if (logical) { + addr &= 0xffff; + if (dv->use_logical) + pos = ADDR_TO_POS(addr); + else + pos = pos_ltop(calc, ADDR_TO_POS(addr)); + } + else { + if (dv->use_logical) { + addr = (*calc->hw.mem_ptol)(calc, addr); + if (addr == (dword) -1) { + tilem_calc_emulator_unlock(dv->dbg->emu); + return; + } + } + pos = ADDR_TO_POS(addr); + } + + tilem_calc_emulator_unlock(dv->dbg->emu); + + if (pos >= dv->startpos && pos < dv->endpos) { + model = gtk_tree_view_get_model(GTK_TREE_VIEW(dv)); + path = find_path_for_position(model, pos); + if (path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(dv), path, + NULL, FALSE); + gtk_tree_path_free(path); + return; + } + } + + refresh_disassembly(dv, pos, dv->nlines, pos); +} + +/* Get currently selected address. */ +gboolean tilem_disasm_view_get_cursor(TilemDisasmView *dv, dword *addr, + gboolean *is_logical) +{ + dword pos; + + g_return_val_if_fail(TILEM_IS_DISASM_VIEW(dv), FALSE); + + if (is_logical) *is_logical = dv->use_logical; + + if (!get_cursor_line(dv, &pos, NULL)) + return FALSE; + + if (addr) *addr = POS_TO_ADDR(pos); + return TRUE; +} + diff --git a/tool/tilem-src/gui/disasmview.h b/tool/tilem-src/gui/disasmview.h new file mode 100644 index 0000000..58463e4 --- /dev/null +++ b/tool/tilem-src/gui/disasmview.h @@ -0,0 +1,73 @@ +/* + * TilEm II + * + * Copyright (c) 2011-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +G_BEGIN_DECLS + +#define TILEM_TYPE_DISASM_VIEW (tilem_disasm_view_get_type()) +#define TILEM_DISASM_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TILEM_TYPE_DISASM_VIEW, TilemDisasmView)) +#define TILEM_DISASM_VIEW_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls), TILEM_TYPE_DISASM_VIEW, TilemDisasmViewClass)) +#define TILEM_IS_DISASM_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TILEM_TYPE_DISASM_VIEW)) +#define TILEM_IS_DISASM_VIEW_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE((cls), TILEM_TYPE_DISASM_VIEW)) +#define TILEM_DISASM_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TILEM_TYPE_DISASM_VIEW, TilemDisasmViewClass)) + +typedef struct _TilemDisasmView { + GtkTreeView parent; + + TilemDebugger *dbg; + + gboolean use_logical; + + int base_height; /* base height of tree view */ + int line_height; /* height of each row */ + + dword startpos; /* position at start of window */ + dword endpos; /* position at end of window */ + int nlines; /* number of lines visible */ + + GtkTreeViewColumn *icon_column; + GtkWidget *popup_menu; +} TilemDisasmView; + +typedef struct _TilemDisasmViewClass { + GtkTreeViewClass parent_class; +} TilemDisasmViewClass; + +GType tilem_disasm_view_get_type(void) G_GNUC_CONST; + +/* Create a new TilemDisasmView. */ +GtkWidget * tilem_disasm_view_new(TilemDebugger *dbg); + +/* Select memory addressing mode. */ +void tilem_disasm_view_set_logical(TilemDisasmView *dv, gboolean logical); + +/* Refresh contents of view. */ +void tilem_disasm_view_refresh(TilemDisasmView *dv); + +/* Highlight the specified address. */ +void tilem_disasm_view_go_to_address(TilemDisasmView *dv, dword addr, + gboolean logical); + +/* Get currently selected address. */ +gboolean tilem_disasm_view_get_cursor(TilemDisasmView *dv, dword *addr, + gboolean *is_logical); + +/* Toggle breakpoint at selected address. */ +void tilem_disasm_view_toggle_breakpoint(TilemDisasmView *dv); + +G_END_DECLS diff --git a/tool/tilem-src/gui/emucore.c b/tool/tilem-src/gui/emucore.c new file mode 100644 index 0000000..a5840ab --- /dev/null +++ b/tool/tilem-src/gui/emucore.c @@ -0,0 +1,436 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "emucore.h" + +#define MICROSEC_PER_TICK 10000 + +typedef struct { + TilemCalcEmulator *emu; + TilemTaskMainFunc mainf; + TilemTaskFinishedFunc finishedf; + gpointer userdata; + gboolean cancelled; +} Task; + +/* Add a task to the queue. */ +void tilem_calc_emulator_begin(TilemCalcEmulator *emu, + TilemTaskMainFunc mainf, + TilemTaskFinishedFunc finishedf, + gpointer data) +{ + Task *task; + + g_return_if_fail(emu != NULL); + g_return_if_fail(mainf != NULL); + + task = g_slice_new0(Task); + task->emu = emu; + task->mainf = mainf; + task->finishedf = finishedf; + task->userdata = data; + + tilem_calc_emulator_lock(emu); + g_queue_push_tail(emu->task_queue, task); + tilem_calc_emulator_unlock(emu); +} + +/* Cancel all pending tasks. If a task is currently running, this + will wait for it to finish. */ +void tilem_calc_emulator_cancel_tasks(TilemCalcEmulator *emu) +{ + GQueue *oldqueue; + Task *task; + + tilem_calc_emulator_lock(emu); + emu->task_abort = TRUE; + emu->link_update->cancel = TRUE; + oldqueue = emu->task_queue; + emu->task_queue = g_queue_new(); + tilem_calc_emulator_unlock(emu); + + while ((task = g_queue_pop_head(oldqueue))) { + if (task->finishedf) + (*task->finishedf)(emu, task->userdata, TRUE); + g_slice_free(Task, task); + } + + g_queue_free(oldqueue); + + g_mutex_lock(emu->calc_mutex); + while (emu->task_busy) + g_cond_wait(emu->task_finished_cond, emu->calc_mutex); + emu->task_abort = FALSE; + emu->link_update->cancel = FALSE; + g_cond_broadcast(emu->calc_wakeup_cond); + g_mutex_unlock(emu->calc_mutex); +} + +/* Check if calculator is powered off */ +static gboolean calc_asleep(TilemCalcEmulator *emu) +{ + return (emu->calc->z80.halted + && !emu->calc->z80.interrupts + && !emu->calc->poweronhalt + && !emu->key_queue_timer); +} + +static gboolean refresh_lcd(gpointer data) +{ + TilemCalcEmulator* emu = data; + + if (emu->ewin) + tilem_emulator_window_refresh_lcd(emu->ewin); + + return FALSE; +} + +/* Update screen for display while paused */ +static void update_screen_mono(TilemCalcEmulator *emu) +{ + g_mutex_lock(emu->lcd_mutex); + + tilem_lcd_get_frame(emu->calc, emu->lcd_buffer); + + if (!emu->lcd_update_pending) { + emu->lcd_update_pending = TRUE; + g_idle_add_full(G_PRIORITY_DEFAULT, &refresh_lcd, emu, NULL); + } + + g_mutex_unlock(emu->lcd_mutex); +} + +/* idle callback to update progress bar */ +static gboolean pbar_update(gpointer data) +{ + TilemCalcEmulator *emu = data; + progress_bar_update(emu); + return FALSE; +} + +static void update_progress(TilemCalcEmulator *emu, gboolean force) +{ + if (force || emu->progress_changed) + g_idle_add(&pbar_update, emu); + emu->progress_changed = FALSE; +} + +static gboolean show_debugger(gpointer data) +{ + TilemCalcEmulator* emu = data; + + if (emu->dbg) + tilem_debugger_show(emu->dbg); + + return FALSE; +} + +#define BREAK_MASK (TILEM_STOP_BREAKPOINT \ + | TILEM_STOP_INVALID_INST \ + | TILEM_STOP_UNDOCUMENTED_INST) + +/* Run one iteration of the emulator. */ +dword tilem_em_run(TilemCalcEmulator *emu, int linkmode, + dword events, dword ff_events, gboolean keep_awake, + int timeout, int *elapsed) +{ + dword all_events, ev_auto, ev_user; + int rem; + gulong tcur, delaytime; + + if (emu->exiting || emu->task_abort) { + if (elapsed) *elapsed = 0; + return 0; + } + else if (emu->paused) { + update_screen_mono(emu); + update_progress(emu, TRUE); + g_cond_wait(emu->calc_wakeup_cond, emu->calc_mutex); + update_progress(emu, TRUE); + g_timer_elapsed(emu->timer, &emu->timevalue); + if (elapsed) *elapsed = 0; + return 0; + } + else if (!keep_awake && calc_asleep(emu)) { + update_progress(emu, FALSE); + update_screen_mono(emu); + g_cond_wait(emu->calc_wakeup_cond, emu->calc_mutex); + g_timer_elapsed(emu->timer, &emu->timevalue); + if (elapsed) *elapsed = timeout; + return 0; + } + + update_progress(emu, FALSE); + + all_events = events | BREAK_MASK; + + emu->calc->linkport.linkemu = linkmode; + emu->calc->z80.stop_mask = ~all_events; + + tilem_z80_run_time(emu->calc, timeout, &rem); + + ev_user = emu->calc->z80.stop_reason & events; + ev_auto = emu->calc->z80.stop_reason & ~events; + + if (elapsed) *elapsed = timeout - rem; + + if (ev_auto & BREAK_MASK) { + emu->paused = TRUE; + g_idle_add(&show_debugger, emu); + } + + if (emu->limit_speed + && !(ff_events & ev_user) + && ff_events != TILEM_EM_ALWAYS_FF) { + emu->timevalue += timeout - rem; + g_timer_elapsed(emu->timer, &tcur); + + /* emu->timevalue is the "ideal" time when the + operation should be completed. If emu->timevalue + is greater than tcur, we're running faster than + real time. Try to sleep for (emu->timevalue - + tcur) microseconds. + + If emu->timevalue is less than tcur, we're running + slower than real time. If the difference is small, + just keep going and hope we'll catch up later. + + If the difference is substantial (more than 1/10 + second in either direction), re-synchronize. */ + + delaytime = emu->timevalue - tcur; + + if (delaytime <= (gulong) 100000 + timeout) { + tilem_em_unlock(emu); + g_usleep(delaytime); + tilem_em_lock(emu); + } + else { + tilem_em_check_yield(emu); + if (delaytime < (gulong) -100000) + emu->timevalue = tcur; + } + } + else { + tilem_em_check_yield(emu); + } + + return ev_user; +} + +static gboolean taskfinished(gpointer data) +{ + Task *task = data; + + if (task->finishedf) + (*task->finishedf)(task->emu, task->userdata, task->cancelled); + + g_slice_free(Task, task); + return FALSE; +} + +static void run_task(TilemCalcEmulator *emu, Task *task) +{ + gboolean status; + + emu->task_busy = TRUE; + status = (*task->mainf)(emu, task->userdata); + + g_idle_add(&taskfinished, task); + + if (!status) { + while ((task = g_queue_pop_head(emu->task_queue))) { + task->cancelled = TRUE; + g_idle_add(&taskfinished, task); + } + } + emu->task_busy = FALSE; +} + +/* Main loop */ +gpointer tilem_em_main(gpointer data) +{ + TilemCalcEmulator *emu = data; + Task *task; + + tilem_em_lock(emu); + + g_timer_start(emu->timer); + g_timer_elapsed(emu->timer, &emu->timevalue); + + while (!emu->exiting) { + task = g_queue_pop_head(emu->task_queue); + if (task) { + run_task(emu, task); + } + else if (emu->task_abort) { + g_cond_broadcast(emu->task_finished_cond); + g_cond_wait(emu->calc_wakeup_cond, emu->calc_mutex); + } + else { + tilem_em_run(emu, 0, 0, 0, FALSE, + MICROSEC_PER_TICK, NULL); + } + } + + tilem_em_unlock(emu); + return NULL; +} + +/* Run the calculator for a short time. */ +void tilem_em_delay(TilemCalcEmulator *emu, int timeout, gboolean ff) +{ + int t; + G_GNUC_UNUSED dword events; + + while (!emu->task_abort && timeout > 0) { + t = MIN(MICROSEC_PER_TICK, timeout); + events = tilem_em_run(emu, 0, 0, + (ff ? TILEM_EM_ALWAYS_FF : 0), TRUE, + t, &t); + timeout -= t; + } +} + +#define LINK_EVENTS (TILEM_STOP_LINK_READ_BYTE \ + | TILEM_STOP_LINK_WRITE_BYTE \ + | TILEM_STOP_LINK_ERROR) + +static int run_until_ready(TilemCalcEmulator *emu, int timeout, gboolean ff) +{ + int t; + dword events; + + emu->calc->linkport.linkemu = TILEM_LINK_EMULATOR_GRAY; + while (!emu->task_abort && timeout > 0) { + if (tilem_linkport_graylink_ready(emu->calc)) + return 0; + + t = MIN(MICROSEC_PER_TICK, timeout); + events = tilem_em_run(emu, TILEM_LINK_EMULATOR_GRAY, + LINK_EVENTS, (ff ? LINK_EVENTS : 0), TRUE, + t, &t); + + timeout -= t; + if (events & TILEM_STOP_LINK_ERROR) + break; + } + return -1; +} + +/* Send a byte to the calculator. */ +int tilem_em_send_byte(TilemCalcEmulator *emu, unsigned value, + int timeout, gboolean ff) +{ + if (run_until_ready(emu, timeout, ff)) + return -1; + if (tilem_linkport_graylink_send_byte(emu->calc, value)) + return -1; + if (run_until_ready(emu, timeout, ff)) + return -1; + return 0; +} + +/* Receive a byte from the calculator. */ +int tilem_em_get_byte(TilemCalcEmulator *emu, int timeout, gboolean ff) +{ + int t, v; + dword events; + + while (!emu->task_abort && timeout > 0) { + v = tilem_linkport_graylink_get_byte(emu->calc); + if (v >= 0) + return v; + + t = MIN(MICROSEC_PER_TICK, timeout); + events = tilem_em_run(emu, TILEM_LINK_EMULATOR_GRAY, + LINK_EVENTS, (ff ? LINK_EVENTS : 0), FALSE, + t, &t); + timeout -= t; + if (events & TILEM_STOP_LINK_ERROR) + break; + } + return -1; +} + +/* Wake up calculator if currently turned off. */ +void tilem_em_wake_up(TilemCalcEmulator *emu, gboolean ff) +{ + tilem_em_delay(emu, 1000000, ff); + + if (!calc_asleep(emu)) + return; + + tilem_keypad_press_key(emu->calc, TILEM_KEY_ON); + tilem_em_delay(emu, 500000, ff); + tilem_keypad_release_key(emu->calc, TILEM_KEY_ON); + tilem_em_delay(emu, 500000, ff); +} + +/* Set progress window title. Set TITLE to NULL to disable progress + window. */ +void tilem_em_set_progress_title(TilemCalcEmulator *emu, const char *title) +{ + g_mutex_lock(emu->pbar_mutex); + g_free(emu->pbar_title); + g_free(emu->pbar_status); + emu->pbar_title = title ? g_strdup(title) : NULL; + emu->pbar_status = NULL; + emu->pbar_progress = 0.0; + if (!emu->pbar_update_pending) + emu->progress_changed = TRUE; + emu->pbar_update_pending = TRUE; + g_mutex_unlock(emu->pbar_mutex); +} + +/* Set current progress information. FRAC is the estimated fraction + of the task completed; STATUS is a text description of the current + operation. */ +void tilem_em_set_progress(TilemCalcEmulator *emu, gdouble frac, + const char *status) +{ + g_mutex_lock(emu->pbar_mutex); + + if (!emu->pbar_status || !status + || strcmp(status, emu->pbar_status)) { + g_free(emu->pbar_status); + emu->pbar_status = status ? g_strdup(status) : NULL; + } + + emu->pbar_progress = frac; + + if (!emu->pbar_update_pending) + emu->progress_changed = TRUE; + emu->pbar_update_pending = TRUE; + + g_mutex_unlock(emu->pbar_mutex); +} diff --git a/tool/tilem-src/gui/emucore.h b/tool/tilem-src/gui/emucore.h new file mode 100644 index 0000000..391b17f --- /dev/null +++ b/tool/tilem-src/gui/emucore.h @@ -0,0 +1,95 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* IMPORTANT: The following functions may ONLY be used within + tilem_em_main() or a task function, and must be called while + holding the emulator lock. */ + +#define TILEM_EM_ALWAYS_FF 0xffffffff + +/* Run one iteration of the emulator. LINKMODE is the link port + emulation mode. EVENTS is a mask of events we are interested in; + emulation will stop early if any of these events occur, or possibly + for other reasons (e.g., a breakpoint being hit.) + + FF_EVENTS is a mask of events we want to fast-forward through + (i.e., not apply speed limiting even if enabled.) If FF_EVENTS is + set to the constant ALWAYS_FF, speed limiting is completely + disabled. + + If KEEP_AWAKE is FALSE and the calculator CPU is turned off, this + function may block until another thread wakes it up; in that case, + no timer events will be triggered, and the elapsed time cannot be + measured in any meaningful way. If KEEP_AWAKE is TRUE, the + emulator continues running even when the CPU is turned off. + + TIMEOUT is the length of time (microseconds) to run the emulator. + If ELAPSED is non-null, *ELAPSED will be set to the actual number + of microseconds elapsed. + + The return value is a mask indicating which of the requested events + occurred. */ +dword tilem_em_run(TilemCalcEmulator *emu, int linkmode, + dword events, dword ff_events, gboolean keep_awake, + int timeout, int *elapsed); + +/* Main loop */ +gpointer tilem_em_main(gpointer data); + +/* Run the calculator for a short time. */ +void tilem_em_delay(TilemCalcEmulator *emu, int timeout, gboolean ff); + +/* Send a byte to the calculator. */ +int tilem_em_send_byte(TilemCalcEmulator *emu, unsigned value, + int timeout, gboolean ff); + +/* Receive a byte from the calculator. */ +int tilem_em_get_byte(TilemCalcEmulator *emu, int timeout, gboolean ff); + +/* Wake up calculator if currently turned off. */ +void tilem_em_wake_up(TilemCalcEmulator *emu, gboolean ff); + +/* Set progress window title. Set TITLE to NULL to disable progress + window. */ +void tilem_em_set_progress_title(TilemCalcEmulator *emu, const char *title); + +/* Set current progress information. FRAC is the estimated fraction + of the task completed; STATUS is a text description of the current + operation. */ +void tilem_em_set_progress(TilemCalcEmulator *emu, gdouble frac, + const char *status); + +/* Lock emulator. */ +#define tilem_em_lock(emu) \ + g_mutex_lock(emu->calc_mutex) + +/* Unlock temporarily if another thread is waiting. */ +#define tilem_em_check_yield(emu) \ + do { \ + if (g_atomic_int_get(&emu->calc_lock_waiting)) \ + g_cond_wait(emu->calc_wakeup_cond, emu->calc_mutex); \ + } while (0) + +/* Unlock emulator. */ +#define tilem_em_unlock(emu) \ + do { \ + tilem_em_check_yield(emu); \ + g_mutex_unlock(emu->calc_mutex); \ + } while (0) + diff --git a/tool/tilem-src/gui/emulator.c b/tool/tilem-src/gui/emulator.c new file mode 100644 index 0000000..1cd68a3 --- /dev/null +++ b/tool/tilem-src/gui/emulator.c @@ -0,0 +1,899 @@ +/* + * TilEm II + * + * Copyright (c) 2011-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "emucore.h" +#include "msgbox.h" +#include "filedlg.h" + +#define MILLISEC_PER_FRAME 30 +#define MICROSEC_PER_FRAME (MILLISEC_PER_FRAME * 1000) + +#define GRAY_WINDOW_SIZE 4 +#define GRAY_SAMPLE_INT 200 + + +/* Lock emulator. Notify the core loop that we wish to do so - note + that if the core is running at full speed, it keeps the mutex + locked almost all the time, so we need to explicitly ask it to + yield. (This may not be necessary with all mutex implementations, + but definitely is necessary with some.) */ +void tilem_calc_emulator_lock(TilemCalcEmulator *emu) +{ + g_atomic_int_inc(&emu->calc_lock_waiting); + g_mutex_lock(emu->calc_mutex); +} + +/* Unlock emulator and (if no other threads are waiting to lock it) + wake up the core thread. This also serves to resume emulation if + the calculator has been powered off. */ +void tilem_calc_emulator_unlock(TilemCalcEmulator *emu) +{ + if (g_atomic_int_dec_and_test(&emu->calc_lock_waiting)) + g_cond_signal(emu->calc_wakeup_cond); + g_mutex_unlock(emu->calc_mutex); +} + +static gboolean refresh_lcd(gpointer data) +{ + TilemCalcEmulator* emu = data; + + if (emu->ewin) + tilem_emulator_window_refresh_lcd(emu->ewin); + + return FALSE; +} + +static void tmr_screen_update(TilemCalc *calc, void *data) +{ + TilemCalcEmulator *emu = data; + + g_mutex_lock(emu->lcd_mutex); + + if (emu->glcd) + tilem_gray_lcd_get_frame(emu->glcd, emu->lcd_buffer); + else + tilem_lcd_get_frame(calc, emu->lcd_buffer); + + if (emu->anim) { + if (emu->anim_grayscale) { + tilem_animation_append_frame(emu->anim, + emu->lcd_buffer, + MILLISEC_PER_FRAME); + } + else { + tilem_lcd_get_frame(calc, emu->tmp_lcd_buffer); + tilem_animation_append_frame(emu->anim, + emu->tmp_lcd_buffer, + MILLISEC_PER_FRAME); + } + } + + if (!emu->lcd_update_pending) { + emu->lcd_update_pending = TRUE; + g_idle_add_full(G_PRIORITY_DEFAULT, &refresh_lcd, emu, NULL); + } + + g_mutex_unlock(emu->lcd_mutex); +} + +static void cancel_animation(TilemCalcEmulator *emu) +{ + if (emu->anim) + g_object_unref(emu->anim); + emu->anim = NULL; +} + +static GtkWidget *get_toplevel(TilemCalcEmulator *emu) +{ + if (emu->ewin) + return emu->ewin->window; + else + return NULL; +} + +static void link_update_nop() +{ +} + +TilemCalcEmulator *tilem_calc_emulator_new() +{ + TilemCalcEmulator *emu = g_new0(TilemCalcEmulator, 1); + CalcUpdate *update; + + emu->calc_mutex = g_mutex_new(); + emu->calc_wakeup_cond = g_cond_new(); + emu->lcd_mutex = g_mutex_new(); + + tilem_config_get("emulation", + "grayscale/b=1", &emu->grayscale, + "limit_speed/b=1", &emu->limit_speed, + NULL); + + emu->task_queue = g_queue_new(); + emu->task_finished_cond = g_cond_new(); + + emu->timer = g_timer_new(); + + emu->pbar_mutex = g_mutex_new(); + + update = g_new0(CalcUpdate, 1); + update->start = &link_update_nop; + update->stop = &link_update_nop; + update->refresh = &link_update_nop; + update->pbar = &link_update_nop; + update->label = &link_update_nop; + emu->link_update = update; + + return emu; +} + +void tilem_calc_emulator_free(TilemCalcEmulator *emu) +{ + g_return_if_fail(emu != NULL); + + tilem_calc_emulator_cancel_tasks(emu); + + tilem_calc_emulator_lock(emu); + cancel_animation(emu); + emu->exiting = TRUE; + tilem_calc_emulator_unlock(emu); + + if (emu->z80_thread) + g_thread_join(emu->z80_thread); + + g_free(emu->key_queue); + + g_free(emu->rom_file_name); + g_free(emu->state_file_name); + + g_mutex_free(emu->calc_mutex); + g_mutex_free(emu->lcd_mutex); + g_cond_free(emu->calc_wakeup_cond); + + g_cond_free(emu->task_finished_cond); + g_queue_free(emu->task_queue); + + g_timer_destroy(emu->timer); + + g_mutex_free(emu->pbar_mutex); + g_free(emu->link_update); + + if (emu->lcd_buffer) + tilem_lcd_buffer_free(emu->lcd_buffer); + if (emu->tmp_lcd_buffer) + tilem_lcd_buffer_free(emu->tmp_lcd_buffer); + if (emu->glcd) + tilem_gray_lcd_free(emu->glcd); + if (emu->calc) + tilem_calc_free(emu->calc); + + g_free(emu); +} + +static char *get_sav_name(const char *romname) +{ + char *dname, *bname, *sname, *suff; + + dname = g_path_get_dirname(romname); + bname = g_path_get_basename(romname); + + if ((suff = strrchr(bname, '.'))) + *suff = 0; + sname = g_strconcat(dname, G_DIR_SEPARATOR_S, bname, ".sav", NULL); + + g_free(dname); + g_free(bname); + return sname; +} + +gboolean tilem_calc_emulator_load_state(TilemCalcEmulator *emu, + const char *romfname, + const char *statefname, + int model, GError **err) +{ + const char *modelname; + FILE *romfile, *savfile; + char *rname = NULL, *sname = NULL; + TilemCalc *calc; + char *dname; + int errnum; + + g_return_val_if_fail(emu != NULL, FALSE); + g_return_val_if_fail(err == NULL || *err == NULL, FALSE); + + tilem_calc_emulator_cancel_tasks(emu); + + if (romfname) + rname = g_strdup(romfname); + if (!sname && statefname) + sname = g_strdup(statefname); + + /* Choose ROM file */ + + if (!rname && model) { + modelname = model_to_name(model); + g_return_val_if_fail(modelname != NULL, FALSE); + if (sname) g_free(sname); + tilem_config_get(modelname, + "rom_file/f", &rname, + "state_file/f", &sname, + NULL); + } + + if (!rname) { + g_set_error(err, TILEM_EMULATOR_ERROR, + TILEM_EMULATOR_ERROR_NO_ROM, + "No ROM file specified"); + g_free(rname); + g_free(sname); + return FALSE; + } + + /* Open ROM file */ + + romfile = g_fopen(rname, "rb"); + if (!romfile) { + errnum = errno; + dname = g_filename_display_basename(rname); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Unable to open %s for reading: %s", + dname, g_strerror(errnum)); + g_free(dname); + g_free(rname); + g_free(sname); + return FALSE; + } + + /* Open state file */ + + if (!sname) + sname = get_sav_name(rname); + + savfile = g_fopen(sname, "rb"); + + if (!savfile && errno != ENOENT) { + errnum = errno; + dname = g_filename_display_basename(sname); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Unable to open %s for reading: %s", + dname, g_strerror(errnum)); + g_free(dname); + g_free(rname); + g_free(sname); + fclose(romfile); + return FALSE; + } + + /* Determine model from state file, if possible */ + + if (!model && savfile) + model = tilem_get_sav_type(savfile); + + /* Otherwise, guess from ROM file; ask user if ambiguous */ + + if (!model) { + model = tilem_guess_rom_type(romfile); + if (model) { + model = choose_rom_popup(get_toplevel(emu), + rname, model); + } + else { + dname = g_filename_display_basename(rname); + g_set_error(err, TILEM_EMULATOR_ERROR, + TILEM_EMULATOR_ERROR_INVALID_ROM, + "The file %s is not a recognized" + " calculator ROM file.", + dname); + g_free(dname); + } + } + + if (!model) { + fclose(romfile); + if (savfile) fclose(savfile); + g_free(rname); + g_free(sname); + return FALSE; + } + + /* Create new calc, and load state */ + + calc = tilem_calc_new(model); + if (tilem_calc_load_state(calc, romfile, savfile)) { + g_set_error(err, TILEM_EMULATOR_ERROR, + TILEM_EMULATOR_ERROR_INVALID_STATE, + "The specified ROM or state file is invalid."); + fclose(romfile); + if (savfile) fclose(savfile); + g_free(rname); + g_free(sname); + return FALSE; + } + + if (!savfile) { + /* save model as default for the future */ + savfile = g_fopen(sname, "wb"); + if (savfile) + fprintf(savfile, "MODEL = %s\n", calc->hw.name); + } + + fclose(romfile); + if (savfile) fclose(savfile); + + /* Switch to new calc */ + + tilem_calc_emulator_lock(emu); + + cancel_animation(emu); + + if (emu->glcd) + tilem_gray_lcd_free(emu->glcd); + if (emu->calc) + tilem_calc_free(emu->calc); + + emu->calc = calc; + emu->lcd_buffer = tilem_lcd_buffer_new(); + emu->tmp_lcd_buffer = tilem_lcd_buffer_new(); + + if (emu->grayscale) + emu->glcd = tilem_gray_lcd_new(calc, GRAY_WINDOW_SIZE, + GRAY_SAMPLE_INT); + else + emu->glcd = NULL; + + tilem_z80_add_timer(calc, MICROSEC_PER_FRAME, + MICROSEC_PER_FRAME, 1, + &tmr_screen_update, emu); + + tilem_calc_emulator_unlock(emu); + + if (emu->rom_file_name) + g_free(emu->rom_file_name); + emu->rom_file_name = rname; + + if (emu->state_file_name) + g_free(emu->state_file_name); + emu->state_file_name = sname; + + tilem_keybindings_init(emu, calc->hw.name); + + if (emu->ewin) + tilem_emulator_window_calc_changed(emu->ewin); + if (emu->dbg) + tilem_debugger_calc_changed(emu->dbg); + + if (emu->rcvdlg) + tilem_receive_dialog_free(emu->rcvdlg); + emu->rcvdlg = NULL; + + return TRUE; +} + +gboolean tilem_calc_emulator_revert_state(TilemCalcEmulator *emu, GError **err) +{ + FILE *romfile, *savfile; + char *dname; + int errnum = 0; + gboolean status = TRUE; + + g_return_val_if_fail(emu != NULL, FALSE); + g_return_val_if_fail(emu->calc != NULL, FALSE); + g_return_val_if_fail(emu->rom_file_name != NULL, FALSE); + g_return_val_if_fail(emu->state_file_name != NULL, FALSE); + g_return_val_if_fail(err == NULL || *err == NULL, FALSE); + + /* Open ROM file */ + + if (emu->calc->hw.flags & TILEM_CALC_HAS_FLASH) { + romfile = g_fopen(emu->rom_file_name, "rb"); + if (!romfile) { + errnum = errno; + dname = g_filename_display_basename(emu->rom_file_name); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Unable to open %s for reading: %s", + dname, g_strerror(errnum)); + g_free(dname); + return FALSE; + } + } + else { + romfile = NULL; + } + + /* Open state file */ + + savfile = g_fopen(emu->state_file_name, "rb"); + if (!savfile) { + errnum = errno; + dname = g_filename_display_basename(emu->state_file_name); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Unable to open %s for reading: %s", + dname, g_strerror(errnum)); + g_free(dname); + if (romfile) fclose(romfile); + return FALSE; + } + + /* Read state */ + + tilem_calc_emulator_lock(emu); + + if (tilem_calc_load_state(emu->calc, romfile, savfile)) { + g_set_error(err, TILEM_EMULATOR_ERROR, + TILEM_EMULATOR_ERROR_INVALID_STATE, + "The specified ROM or state file is invalid."); + status = FALSE; + } + + tilem_calc_emulator_unlock(emu); + + if (emu->dbg) + tilem_debugger_refresh(emu->dbg, TRUE); + + if (romfile) fclose(romfile); + fclose(savfile); + return status; +} + +gboolean tilem_calc_emulator_save_state(TilemCalcEmulator *emu, GError **err) +{ + FILE *romfile, *savfile; + char *dname; + int errnum = 0; + + g_return_val_if_fail(emu != NULL, FALSE); + g_return_val_if_fail(emu->calc != NULL, FALSE); + g_return_val_if_fail(emu->rom_file_name != NULL, FALSE); + g_return_val_if_fail(emu->state_file_name != NULL, FALSE); + g_return_val_if_fail(err == NULL || *err == NULL, FALSE); + + /* Open ROM file */ + + if (emu->calc->hw.flags & TILEM_CALC_HAS_FLASH) { + romfile = g_fopen(emu->rom_file_name, "r+b"); + if (!romfile) { + errnum = errno; + dname = g_filename_display_basename(emu->rom_file_name); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Unable to open %s for writing: %s", + dname, g_strerror(errnum)); + g_free(dname); + return FALSE; + } + } + else { + romfile = NULL; + } + + /* Open state file */ + + savfile = g_fopen(emu->state_file_name, "wb"); + if (!savfile) { + errnum = errno; + dname = g_filename_display_basename(emu->state_file_name); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Unable to open %s for writing: %s", + dname, g_strerror(errnum)); + g_free(dname); + if (romfile) fclose(romfile); + return FALSE; + } + + /* Write state */ + + tilem_calc_emulator_lock(emu); + + if (romfile && tilem_calc_save_state(emu->calc, romfile, NULL)) + errnum = errno; + if (romfile && fclose(romfile)) + errnum = errno; + + if (errnum) { + dname = g_filename_display_basename(emu->rom_file_name); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Error writing %s: %s", + dname, g_strerror(errnum)); + g_free(dname); + fclose(savfile); + tilem_calc_emulator_unlock(emu); + return FALSE; + } + + if (tilem_calc_save_state(emu->calc, NULL, savfile)) + errnum = errno; + if (fclose(savfile)) + errnum = errno; + + tilem_calc_emulator_unlock(emu); + + if (errnum) { + dname = g_filename_display_basename(emu->state_file_name); + g_set_error(err, G_FILE_ERROR, + g_file_error_from_errno(errnum), + "Error writing %s: %s", + dname, g_strerror(errnum)); + g_free(dname); + return FALSE; + } + + return TRUE; +} + +void tilem_calc_emulator_reset(TilemCalcEmulator *emu) +{ + g_return_if_fail(emu != NULL); + g_return_if_fail(emu->calc != NULL); + + tilem_calc_emulator_lock(emu); + tilem_calc_reset(emu->calc); + tilem_calc_emulator_unlock(emu); + + if (emu->dbg) + tilem_debugger_refresh(emu->dbg, TRUE); +} + +void tilem_calc_emulator_pause(TilemCalcEmulator *emu) +{ + g_return_if_fail(emu != NULL); + + tilem_calc_emulator_lock(emu); + emu->paused = TRUE; + tilem_calc_emulator_unlock(emu); +} + +void tilem_calc_emulator_run(TilemCalcEmulator *emu) +{ + g_return_if_fail(emu != NULL); + g_return_if_fail(emu->calc != NULL); + + tilem_calc_emulator_lock(emu); + emu->paused = FALSE; + tilem_calc_emulator_unlock(emu); + + if (!emu->z80_thread) + emu->z80_thread = g_thread_create(&tilem_em_main, emu, TRUE, NULL); +} + +void tilem_calc_emulator_set_limit_speed(TilemCalcEmulator *emu, + gboolean limit) +{ + emu->limit_speed = limit; +} + +void tilem_calc_emulator_set_grayscale(TilemCalcEmulator *emu, + gboolean grayscale) +{ + emu->grayscale = grayscale; + + if (grayscale && emu->calc && !emu->glcd) { + tilem_calc_emulator_lock(emu); + emu->glcd = tilem_gray_lcd_new(emu->calc, GRAY_WINDOW_SIZE, + GRAY_SAMPLE_INT); + tilem_calc_emulator_unlock(emu); + } + else if (!grayscale && emu->glcd) { + tilem_calc_emulator_lock(emu); + tilem_gray_lcd_free(emu->glcd); + emu->glcd = NULL; + tilem_calc_emulator_unlock(emu); + } +} + +/* If currently recording a macro, record a keypress */ +static void record_key(TilemCalcEmulator* emu, int code) +{ + char* codechar; + int type = 0; + + if (emu->isMacroRecording) { + codechar = g_strdup_printf("%04d", code); + tilem_macro_add_action(emu->macro, type, codechar); + g_free(codechar); + } +} + +void tilem_calc_emulator_press_key(TilemCalcEmulator *emu, int key) +{ + g_return_if_fail(emu != NULL); + + if (key == 0) + return; + + tilem_calc_emulator_lock(emu); + tilem_keypad_press_key(emu->calc, key); + tilem_calc_emulator_unlock(emu); + + record_key(emu, key); + + if (emu->dbg && emu->dbg->keypad_dialog) + tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog); +} + +void tilem_calc_emulator_release_key(TilemCalcEmulator *emu, int key) +{ + g_return_if_fail(emu != NULL); + + if (key == 0) + return; + + tilem_calc_emulator_lock(emu); + tilem_keypad_release_key(emu->calc, key); + tilem_calc_emulator_unlock(emu); + + if (emu->dbg && emu->dbg->keypad_dialog) + tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog); +} + +static gboolean refresh_kpd(gpointer data) +{ + TilemCalcEmulator *emu = data; + + if (emu->dbg && emu->dbg->keypad_dialog) + tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog); + + return FALSE; +} + +/* Timer callback for key sequences */ +static void tmr_key_queue(TilemCalc* calc, void* data) +{ + TilemCalcEmulator *emu = data; + int nextkey; + + if (emu->key_queue_pressed) { + if (emu->key_queue_len > 0 || !emu->key_queue_hold) { + tilem_keypad_release_key(calc, emu->key_queue_cur); + emu->key_queue_pressed = 0; + emu->key_queue_cur = 0; + tilem_z80_set_timer(calc, emu->key_queue_timer, + 50000, 0, 1); + } + else { + tilem_z80_remove_timer(calc, emu->key_queue_timer); + emu->key_queue_timer = 0; + } + } + else { + if (emu->key_queue_len > 0) { + nextkey = emu->key_queue[--emu->key_queue_len]; + tilem_keypad_press_key(calc, nextkey); + emu->key_queue_pressed = 1; + emu->key_queue_cur = nextkey; + tilem_z80_set_timer(calc, emu->key_queue_timer, + 20000, 0, 1); + } + else { + tilem_z80_remove_timer(calc, emu->key_queue_timer); + emu->key_queue_timer = 0; + } + } + + g_idle_add(&refresh_kpd, emu); +} + +static void queue_keys(TilemCalcEmulator *emu, const byte *keys, int nkeys) +{ + byte *q; + int i; + + q = g_new(byte, emu->key_queue_len + nkeys); + + for (i = 0; i < nkeys; i++) { + q[nkeys - i - 1] = keys[i]; + record_key(emu, keys[i]); + } + + if (emu->key_queue_len) + memcpy(q + nkeys, emu->key_queue, emu->key_queue_len); + + g_free(emu->key_queue); + emu->key_queue = q; + emu->key_queue_len += nkeys; + emu->key_queue_hold = 1; + + if (!emu->key_queue_timer) { + emu->key_queue_timer + = tilem_z80_add_timer(emu->calc, 1, 0, 1, + &tmr_key_queue, emu); + } +} + +void tilem_calc_emulator_queue_keys(TilemCalcEmulator *emu, + const byte *keys, int nkeys) +{ + g_return_if_fail(emu != NULL); + g_return_if_fail(keys != NULL); + g_return_if_fail(nkeys > 0); + + tilem_calc_emulator_lock(emu); + queue_keys(emu, keys, nkeys); + tilem_calc_emulator_unlock(emu); +} + +void tilem_calc_emulator_release_queued_key(TilemCalcEmulator *emu) +{ + g_return_if_fail(emu != NULL); + + tilem_calc_emulator_lock(emu); + if (emu->key_queue_timer) { + emu->key_queue_hold = 0; + } + else if (emu->key_queue_pressed) { + tilem_keypad_release_key(emu->calc, emu->key_queue_cur); + emu->key_queue_pressed = 0; + emu->key_queue_cur = 0; + } + tilem_calc_emulator_unlock(emu); + + if (emu->dbg && emu->dbg->keypad_dialog) + tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog); +} + +gboolean tilem_calc_emulator_press_or_queue(TilemCalcEmulator *emu, + int key) +{ + byte b; + gboolean status; + + g_return_val_if_fail(emu != NULL, FALSE); + + tilem_calc_emulator_lock(emu); + + if (emu->key_queue_timer) { + b = key; + queue_keys(emu, &b, 1); + status = TRUE; + } + else { + tilem_keypad_press_key(emu->calc, key); + status = FALSE; + } + tilem_calc_emulator_unlock(emu); + + if (emu->dbg && emu->dbg->keypad_dialog) + tilem_keypad_dialog_refresh(emu->dbg->keypad_dialog); + + return status; +} + +TilemAnimation * tilem_calc_emulator_get_screenshot(TilemCalcEmulator *emu, + gboolean grayscale) +{ + TilemAnimation *anim; + + g_return_val_if_fail(emu != NULL, NULL); + g_return_val_if_fail(emu->calc != NULL, NULL); + + anim = tilem_animation_new(emu->calc->hw.lcdwidth, + emu->calc->hw.lcdheight); + + tilem_calc_emulator_lock(emu); + + if (grayscale && emu->glcd) + tilem_gray_lcd_get_frame(emu->glcd, emu->tmp_lcd_buffer); + else + tilem_lcd_get_frame(emu->calc, emu->tmp_lcd_buffer); + + tilem_animation_append_frame(anim, emu->tmp_lcd_buffer, 1); + + tilem_calc_emulator_unlock(emu); + + return anim; +} + +void tilem_calc_emulator_begin_animation(TilemCalcEmulator *emu, + gboolean grayscale) +{ + g_return_if_fail(emu != NULL); + g_return_if_fail(emu->calc != NULL); + + tilem_calc_emulator_lock(emu); + cancel_animation(emu); + emu->anim = tilem_animation_new(emu->calc->hw.lcdwidth, + emu->calc->hw.lcdheight); + emu->anim_grayscale = grayscale; + tilem_calc_emulator_unlock(emu); +} + +TilemAnimation * tilem_calc_emulator_end_animation(TilemCalcEmulator *emu) +{ + TilemAnimation *anim; + + g_return_val_if_fail(emu != NULL, NULL); + g_return_val_if_fail(emu->anim != NULL, NULL); + + tilem_calc_emulator_lock(emu); + anim = emu->anim; + emu->anim = NULL; + tilem_calc_emulator_unlock(emu); + + return anim; +} + +/* Prompt for a ROM file to open */ +int tilem_calc_emulator_prompt_open_rom(TilemCalcEmulator *emu) +{ + char *dir, *filename; + GError *err = NULL; + const char *modelname; + + if (emu->rom_file_name) + dir = g_path_get_dirname(emu->rom_file_name); + else + dir = g_get_current_dir(); + + filename = prompt_open_file("Open Calculator", GTK_WINDOW(get_toplevel(emu)), + dir, "ROM files", "*.rom;*.clc;*.bin", + "All files", "*", NULL); + g_free(dir); + if (!filename) + return 0; + + if (tilem_calc_emulator_load_state(emu, filename, NULL, + 0, &err)) { + modelname = emu->calc->hw.name; + tilem_config_set(modelname, + "rom_file/f", emu->rom_file_name, + "state_file/f", emu->state_file_name, + NULL); + tilem_config_set("recent", "last_model/s", modelname, NULL); + } + g_free(filename); + + if (err) { + messagebox01(GTK_WINDOW(get_toplevel(emu)), GTK_MESSAGE_ERROR, + "Unable to load calculator state", + "%s", err->message); + g_error_free(err); + return -1; + } + else { + return 1; + } +} + +/* Run slowly to play macro (used instead run_with_key() function) */ +void run_with_key_slowly(TilemCalc* calc, int key) +{ + tilem_z80_run_time(calc, 5000000, NULL); /* Wait */ + tilem_keypad_press_key(calc, key); /* Press */ + tilem_z80_run_time(calc, 10000, NULL); /* Wait (don't forget to wait) */ + tilem_keypad_release_key(calc, key); /* Release */ + tilem_z80_run_time(calc, 50, NULL); /* Wait */ +} diff --git a/tool/tilem-src/gui/emulator.h b/tool/tilem-src/gui/emulator.h new file mode 100644 index 0000000..640006f --- /dev/null +++ b/tool/tilem-src/gui/emulator.h @@ -0,0 +1,263 @@ +/* + * TilEm II + * + * Copyright (c) 2011-2012 Benjamin Moody + * Copyright (c) 2011 Duponchelle Thibault + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Key binding */ +typedef struct _TilemKeyBinding { + unsigned int keysym; /* host keysym value */ + unsigned int modifiers; /* modifier mask */ + int nscancodes; /* number of calculator scancodes */ + byte *scancodes; /* calculator scancodes */ +} TilemKeyBinding; + +/* A single action */ +typedef struct _TilemMacroAtom { + char* value; + int type; +} TilemMacroAtom; + +/* All the actions */ +typedef struct _TilemMacro { + TilemMacroAtom** actions; + int n; +} TilemMacro; + + + +typedef struct _TilemCalcEmulator { + GThread *z80_thread; + + /* Mutex controlling access to the calc. Use + tilem_calc_emulator_lock()/unlock() rather than + g_mutex_lock()/unlock() directly. */ + GMutex *calc_mutex; + int calc_lock_waiting; + + GCond *calc_wakeup_cond; + TilemCalc *calc; + gboolean paused; + gboolean exiting; + gboolean limit_speed; /* limit to actual speed */ + + /* Timer used for speed limiting */ + GTimer *timer; + gulong timevalue; + + /* Queue of tasks to be performed */ + GQueue *task_queue; + gboolean task_busy; + gboolean task_abort; + GCond *task_finished_cond; + + /* Sequence of keys to be pressed */ + byte *key_queue; + int key_queue_len; + int key_queue_timer; + int key_queue_pressed; + int key_queue_cur; + int key_queue_hold; + + GMutex *lcd_mutex; + TilemLCDBuffer *lcd_buffer; + TilemLCDBuffer *tmp_lcd_buffer; + TilemGrayLCD *glcd; + gboolean grayscale; + gboolean lcd_update_pending; + + TilemAnimation *anim; /* animation being recorded */ + gboolean anim_grayscale; /* use grayscale in animation */ + + char *rom_file_name; + char *state_file_name; + + /* List of key bindings */ + TilemKeyBinding *keybindings; + int nkeybindings; + + struct _TilemMacro *macro; + + /* Link transfer state */ + gboolean ilp_active; + CalcUpdate *link_update; /* CalcUpdate (status and callbacks for ticalcs) */ + GMutex *pbar_mutex; + char *pbar_title; + char *pbar_status; + gdouble pbar_progress; + gboolean pbar_update_pending; + gboolean progress_changed; + + /* GUI widgets */ + struct _TilemDebugger *dbg; + struct _TilemEmulatorWindow *ewin; + struct _TilemScreenshotDialog *ssdlg; + struct _TilemReceiveDialog *rcvdlg; + struct _TilemLinkProgress *linkpb; + + + FILE * macro_file; /* The macro file */ + gboolean isMacroRecording; /* A flag to know everywhere that macro is recording */ + +} TilemCalcEmulator; + +/* Errors */ +#define TILEM_EMULATOR_ERROR g_quark_from_static_string("tilem-emulator-error") +enum { + TILEM_EMULATOR_ERROR_NO_ROM, + TILEM_EMULATOR_ERROR_INVALID_ROM, + TILEM_EMULATOR_ERROR_INVALID_STATE +}; + +/* Create a new TilemCalcEmulator. */ +TilemCalcEmulator *tilem_calc_emulator_new(void); + +/* Free a TilemCalcEmulator. */ +void tilem_calc_emulator_free(TilemCalcEmulator *emu); + +/* Lock calculator so we can directly access it from outside the core + thread. */ +void tilem_calc_emulator_lock(TilemCalcEmulator *emu); + +/* Unlock calculator and allow emulation to continue. */ +void tilem_calc_emulator_unlock(TilemCalcEmulator *emu); + +/* Load the calculator state from the given ROM file (and accompanying + sav file, if any.) */ +gboolean tilem_calc_emulator_load_state(TilemCalcEmulator *emu, + const char *romfname, + const char *statefname, + int model, GError **err); + +/* Reload the calculator state from the most recently loaded file. */ +gboolean tilem_calc_emulator_revert_state(TilemCalcEmulator *emu, + GError **err); + +/* Save the calculator state. */ +gboolean tilem_calc_emulator_save_state(TilemCalcEmulator *emu, + GError **err); + +/* Reset the calculator. */ +void tilem_calc_emulator_reset(TilemCalcEmulator *emu); + +/* Pause emulation (if currently running.) */ +void tilem_calc_emulator_pause(TilemCalcEmulator *emu); + +/* Resume emulation (if currently paused.) */ +void tilem_calc_emulator_run(TilemCalcEmulator *emu); + +/* Enable/disable speed limiting (TRUE means attempt to run at the + actual CPU speed; FALSE means run as fast as we can.) */ +void tilem_calc_emulator_set_limit_speed(TilemCalcEmulator *emu, + gboolean limit); + +/* Enable/disable grayscale */ +void tilem_calc_emulator_set_grayscale(TilemCalcEmulator *emu, + gboolean grayscale); + +/* Press a single key. */ +void tilem_calc_emulator_press_key(TilemCalcEmulator *emu, int key); + +/* Release a single key. */ +void tilem_calc_emulator_release_key(TilemCalcEmulator *emu, int key); + +/* Add keys to the input queue. */ +void tilem_calc_emulator_queue_keys(TilemCalcEmulator *emu, + const byte *keys, int nkeys); + +/* Release final key in input queue. */ +void tilem_calc_emulator_release_queued_key(TilemCalcEmulator *emu); + +/* If input queue is empty, press key immediately; otherwise, add to + the input queue. Return TRUE if key was added to the queue. */ +gboolean tilem_calc_emulator_press_or_queue(TilemCalcEmulator *emu, int key); + +/* Retrieve a static screenshot of current calculator screen. + Returned object has a reference count of 1 (free it with + g_object_unref().) */ +TilemAnimation * tilem_calc_emulator_get_screenshot(TilemCalcEmulator *emu, + gboolean grayscale); + +/* Begin recording an animated screenshot. */ +void tilem_calc_emulator_begin_animation(TilemCalcEmulator *emu, + gboolean grayscale); + +/* Finish recording an animated screenshot. Returned object has a + reference count of 1 (free it with g_object_unref().) */ +TilemAnimation * tilem_calc_emulator_end_animation(TilemCalcEmulator *emu); + +/* Prompt for a ROM file to open */ +int tilem_calc_emulator_prompt_open_rom(TilemCalcEmulator *emu); + + +/* Run slowly to play macro */ +void run_with_key_slowly(TilemCalc* calc, int key); + + +/* Task handling */ + +typedef gboolean (*TilemTaskMainFunc)(TilemCalcEmulator *emu, gpointer data); +typedef void (*TilemTaskFinishedFunc)(TilemCalcEmulator *emu, gpointer data, + gboolean cancelled); + +/* Add a task to the queue. MAINF is a function to perform in the + core thread. If it returns FALSE, all further tasks will be + cancelled. Tasks can also be cancelled early by calling + tilem_calc_emulator_cancel_tasks(). + + After the task finishes or is cancelled, FINISHEDF will be called + in the GUI thread. Task-finished functions might not be called in + the same order the tasks were originally added to the queue. */ +void tilem_calc_emulator_begin(TilemCalcEmulator *emu, + TilemTaskMainFunc taskf, + TilemTaskFinishedFunc finishedf, + gpointer data); + +/* Cancel all pending tasks. If a task is currently running, this + will attempt to cancel it and wait for it to exit. */ +void tilem_calc_emulator_cancel_tasks(TilemCalcEmulator *emu); + + +/* Macros */ + +/* Start to record a macro */ +void tilem_macro_start(TilemCalcEmulator *emu); + + +/* Add an action to the macro */ +void tilem_macro_add_action(TilemMacro* macro, int type, char * value); + + +/* Stop recording a macro */ +void tilem_macro_stop(TilemCalcEmulator *emu); + + +/* Print the macro (debug) */ +void tilem_macro_print(TilemMacro *macro); + + +/* Write a macro file */ +void tilem_macro_write_file(TilemCalcEmulator *emu); + + +/* Play a macro (loaded or recorded before) */ +void tilem_macro_play(TilemCalcEmulator *emu); + + +/* Load a macro from filename or if filename == NULL prompt user */ +void tilem_macro_load(TilemCalcEmulator *emu, char* filename); + diff --git a/tool/tilem-src/gui/emuwin.c b/tool/tilem-src/gui/emuwin.c new file mode 100644 index 0000000..68c563e --- /dev/null +++ b/tool/tilem-src/gui/emuwin.c @@ -0,0 +1,621 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "gui.h" +#include "files.h" +#include "msgbox.h" + +/* Set size hints for the toplevel window */ +static void set_size_hints(GtkWidget *widget, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + + /* Don't use gtk_window_set_geometry_hints() (which would + appear to do what we want) because, in addition to setting + the hints we want, that function causes GTK+ to argue with + the window manager. + + Instead, we call this function after the check-resize + signal (which is when GTK+ itself would normally set the + hints.) + + FIXME: check that this works as desired on Win32/Quartz. */ + + if (gtk_widget_get_window(widget)) + gdk_window_set_geometry_hints(gtk_widget_get_window(widget), + &ewin->geomhints, + ewin->geomhintmask); +} + +static void window_state_changed(G_GNUC_UNUSED GtkWidget *w, + GdkEventWindowState *event, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + ewin->window_state = event->new_window_state; +} + +static gboolean window_maximized(TilemEmulatorWindow *ewin) +{ + return (ewin->window_state & (GDK_WINDOW_STATE_MAXIMIZED + | GDK_WINDOW_STATE_FULLSCREEN)); +} + +static gboolean screen_repaint(GtkWidget *w, GdkEventExpose *ev G_GNUC_UNUSED, + TilemEmulatorWindow *ewin) +{ + GtkAllocation alloc; + GdkWindow *win; + GtkStyle *style; + + gtk_widget_get_allocation(w, &alloc); + + /* If image buffer is not the correct size, allocate a new one */ + + if (!ewin->lcd_image_buf + || alloc.width != ewin->lcd_image_width + || alloc.height != ewin->lcd_image_height) { + ewin->lcd_image_width = alloc.width; + ewin->lcd_image_height = alloc.height; + g_free(ewin->lcd_image_buf); + ewin->lcd_image_buf = g_new(byte, alloc.width * alloc.height); + } + + /* Draw LCD contents into the image buffer */ + + g_mutex_lock(ewin->emu->lcd_mutex); + ewin->emu->lcd_update_pending = FALSE; + tilem_draw_lcd_image_indexed(ewin->emu->lcd_buffer, + ewin->lcd_image_buf, + alloc.width, alloc.height, alloc.width, + (ewin->lcd_smooth_scale + ? TILEM_SCALE_SMOOTH + : TILEM_SCALE_FAST)); + g_mutex_unlock(ewin->emu->lcd_mutex); + + /* Render buffer to the screen */ + + win = gtk_widget_get_window(w); + style = gtk_widget_get_style(w); + gdk_draw_indexed_image(win, style->fg_gc[GTK_STATE_NORMAL], 0, 0, + alloc.width, alloc.height, GDK_RGB_DITHER_NONE, + ewin->lcd_image_buf, alloc.width, + ewin->lcd_cmap); + return TRUE; +} + +/* Set the color palette for drawing the emulated LCD. */ +static void screen_restyle(GtkWidget* w, GtkStyle* oldstyle G_GNUC_UNUSED, + TilemEmulatorWindow* ewin) +{ + dword* palette; + GtkStyle *style; + int r_dark, g_dark, b_dark; + int r_light, g_light, b_light; + double gamma = 2.2; + + if (!ewin->skin) { + /* no skin -> use standard GTK colors */ + + style = gtk_widget_get_style(w); + + r_dark = style->text[GTK_STATE_NORMAL].red / 257; + g_dark = style->text[GTK_STATE_NORMAL].green / 257; + b_dark = style->text[GTK_STATE_NORMAL].blue / 257; + + r_light = style->base[GTK_STATE_NORMAL].red / 257; + g_light = style->base[GTK_STATE_NORMAL].green / 257; + b_light = style->base[GTK_STATE_NORMAL].blue / 257; + } + else { + /* use skin colors */ + + r_dark = ((ewin->skin->lcd_black >> 16) & 0xff); + g_dark = ((ewin->skin->lcd_black >> 8) & 0xff); + b_dark = (ewin->skin->lcd_black & 0xff); + + r_light = ((ewin->skin->lcd_white >> 16) & 0xff); + g_light = ((ewin->skin->lcd_white >> 8) & 0xff); + b_light = (ewin->skin->lcd_white & 0xff); + } + + /* Generate a new palette, and convert it into GDK format */ + + if (ewin->lcd_cmap) + gdk_rgb_cmap_free(ewin->lcd_cmap); + + palette = tilem_color_palette_new(r_light, g_light, b_light, + r_dark, g_dark, b_dark, gamma); + ewin->lcd_cmap = gdk_rgb_cmap_new(palette, 256); + tilem_free(palette); + + gtk_widget_queue_draw(ewin->lcd); +} + +static void skin_size_allocate(GtkWidget *widget, GtkAllocation *alloc, + gpointer data) +{ + TilemEmulatorWindow *ewin = data; + int rawwidth, rawheight, scaledwidth, scaledheight; + int lcdleft, lcdright, lcdtop, lcdbottom; + double rx, ry, r; + + g_return_if_fail(ewin->skin != NULL); + + rawwidth = gdk_pixbuf_get_width(ewin->skin->raw); + rawheight = gdk_pixbuf_get_height(ewin->skin->raw); + + rx = (double) alloc->width / rawwidth; + ry = (double) alloc->height / rawheight; + r = MIN(rx, ry); + + scaledwidth = rawwidth * r; + scaledheight = rawheight * r; + + if (ewin->skin->width == scaledwidth + && ewin->skin->height == scaledheight) + return; + + ewin->skin->width = scaledwidth; + ewin->skin->height = scaledheight; + ewin->skin->sx = ewin->skin->sy = 1.0 / r; + + if (ewin->skin->image) + g_object_unref(ewin->skin->image); + ewin->skin->image = gdk_pixbuf_scale_simple(ewin->skin->raw, + scaledwidth, + scaledheight, + GDK_INTERP_BILINEAR); + + gtk_image_set_from_pixbuf(GTK_IMAGE(ewin->background), + ewin->skin->image); + + lcdleft = ewin->skin->lcd_pos.left * r + 0.5; + lcdright = ewin->skin->lcd_pos.right * r + 0.5; + lcdtop = ewin->skin->lcd_pos.top * r + 0.5; + lcdbottom = ewin->skin->lcd_pos.bottom * r + 0.5; + + gtk_widget_set_size_request(ewin->lcd, + MAX(lcdright - lcdleft, 1), + MAX(lcdbottom - lcdtop, 1)); + + gtk_layout_move(GTK_LAYOUT(widget), ewin->lcd, + lcdleft, lcdtop); + + ewin->zoom_factor = r / ewin->base_zoom; + + if (ewin->zoom_factor <= 1.0) + ewin->zoom_factor = 1.0; +} + +static void noskin_size_allocate(G_GNUC_UNUSED GtkWidget *widget, + GtkAllocation *alloc, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + int lcdwidth, lcdheight; + + g_return_if_fail(ewin->emu->calc != NULL); + + lcdwidth = ewin->emu->calc->hw.lcdwidth; + lcdheight = ewin->emu->calc->hw.lcdheight; + + if (alloc->width > alloc->height) + ewin->zoom_factor = (gdouble) alloc->width / lcdwidth; + else + ewin->zoom_factor = (gdouble) alloc->height / lcdheight; + + if (ewin->zoom_factor <= 1.0) + ewin->zoom_factor = 1.0; +} + +/* Used when you load another skin */ +void redraw_screen(TilemEmulatorWindow *ewin) +{ + GtkWidget *pImage; + GtkWidget *emuwin; + int lcdwidth, lcdheight; + int screenwidth, screenheight; + int minwidth, minheight, defwidth, defheight, + curwidth, curheight; + double sx, sy, s, a1, a2; + GError *err = NULL; + + if (ewin->skin) { + skin_unload(ewin->skin); + g_free(ewin->skin); + } + + if (ewin->skin_disabled || !ewin->skin_file_name) { + ewin->skin = NULL; + } + else { + ewin->skin = g_new0(SKIN_INFOS, 1); + if (skin_load(ewin->skin, ewin->skin_file_name, &err)) { + skin_unload(ewin->skin); + ewin->skin = NULL; + } + } + + if (ewin->emu->calc) { + lcdwidth = ewin->emu->calc->hw.lcdwidth; + lcdheight = ewin->emu->calc->hw.lcdheight; + } + else { + lcdwidth = lcdheight = 1; + } + + if (ewin->lcd) + gtk_widget_destroy(ewin->lcd); + if (ewin->background) + gtk_widget_destroy(ewin->background); + if (ewin->layout) + gtk_widget_destroy(ewin->layout); + + /* create LCD widget */ + ewin->lcd = gtk_drawing_area_new(); + gtk_widget_set_double_buffered(ewin->lcd, FALSE); + + /* create background image and layout */ + if (ewin->skin) { + ewin->layout = gtk_layout_new(NULL, NULL); + + pImage = gtk_image_new(); + gtk_layout_put(GTK_LAYOUT(ewin->layout), pImage, 0, 0); + ewin->background = pImage; + + screenwidth = (ewin->skin->lcd_pos.right + - ewin->skin->lcd_pos.left); + screenheight = (ewin->skin->lcd_pos.bottom + - ewin->skin->lcd_pos.top); + + gtk_widget_set_size_request(ewin->lcd, + screenwidth, screenheight); + gtk_layout_put(GTK_LAYOUT(ewin->layout), ewin->lcd, + ewin->skin->lcd_pos.left, + ewin->skin->lcd_pos.top); + + g_signal_connect(ewin->layout, "size-allocate", + G_CALLBACK(skin_size_allocate), ewin); + + emuwin = ewin->layout; + + defwidth = ewin->skin->width; + defheight = ewin->skin->height; + + sx = (double) lcdwidth / screenwidth; + sy = (double) lcdheight / screenheight; + s = MAX(sx, sy); + minwidth = defwidth * s + 0.5; + minheight = defheight * s + 0.5; + ewin->base_zoom = s; + } + else { + ewin->layout = NULL; + ewin->background = NULL; + + emuwin = ewin->lcd; + + g_signal_connect(ewin->lcd, "size-allocate", + G_CALLBACK(noskin_size_allocate), ewin); + + defwidth = minwidth = lcdwidth; + defheight = minheight = lcdheight; + ewin->base_zoom = 1.0; + } + + curwidth = defwidth * ewin->base_zoom * ewin->zoom_factor + 0.5; + curheight = defheight * ewin->base_zoom * ewin->zoom_factor + 0.5; + + gtk_widget_set_can_focus(emuwin, TRUE); + + gtk_widget_add_events(emuwin, (GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK + | GDK_DROP_FINISHED + | GDK_DRAG_MOTION)); + + g_signal_connect(ewin->lcd, "expose-event", + G_CALLBACK(screen_repaint), ewin); + g_signal_connect(ewin->lcd, "style-set", + G_CALLBACK(screen_restyle), ewin); + + g_signal_connect(emuwin, "button-press-event", + G_CALLBACK(mouse_press_event), ewin); + g_signal_connect(emuwin, "motion-notify-event", + G_CALLBACK(pointer_motion_event), ewin); + g_signal_connect(emuwin, "button-release-event", + G_CALLBACK(mouse_release_event), ewin); + g_signal_connect(emuwin, "popup-menu", + G_CALLBACK(popup_menu_event), ewin); + + /* FIXME: this is rather broken; GTK_DEST_DEFAULT_ALL sends a + successful "finished" message to any drop that matches the + list of targets. Files/URIs we can't accept should be + rejected, and we shouldn't send the "finished" message + until it's actually finished. */ + gtk_drag_dest_set(emuwin, GTK_DEST_DEFAULT_ALL, + NULL, 0, GDK_ACTION_COPY); + gtk_drag_dest_add_uri_targets(emuwin); + g_signal_connect(emuwin, "drag-data-received", + G_CALLBACK(drag_data_received), ewin); + + + /* Hint calculation assumes the emulator is the only thing in + the window; if other widgets are added, this will have to + change accordingly + */ + ewin->geomhints.min_width = minwidth; + ewin->geomhints.min_height = minheight; + a1 = (double) minwidth / minheight; + a2 = (double) defwidth / defheight; + ewin->geomhints.min_aspect = MIN(a1, a2) - 0.0001; + ewin->geomhints.max_aspect = MAX(a1, a2) + 0.0001; + ewin->geomhintmask = (GDK_HINT_MIN_SIZE | GDK_HINT_ASPECT); + + /* If the window is already realized, set the hints now, so + that the WM will see the new hints before we try to resize + the window */ + set_size_hints(ewin->window, ewin); + + gtk_widget_set_size_request(emuwin, minwidth, minheight); + gtk_container_add(GTK_CONTAINER(ewin->window), emuwin); + + if (!window_maximized(ewin)) + gtk_window_resize(GTK_WINDOW(ewin->window), + curwidth, curheight); + + gtk_widget_show_all(emuwin); + + if (err) { + messagebox01(GTK_WINDOW(ewin->window), GTK_MESSAGE_ERROR, + "Unable to load skin", "%s", err->message); + g_error_free(err); + } +} + +static void window_destroy(G_GNUC_UNUSED GtkWidget *w, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + + if (!window_maximized(ewin)) + tilem_config_set("settings", + "zoom/r", ewin->zoom_factor, + NULL); + + ewin->window = ewin->layout = ewin->lcd = ewin->background = NULL; +} + +TilemEmulatorWindow *tilem_emulator_window_new(TilemCalcEmulator *emu) +{ + TilemEmulatorWindow *ewin; + + g_return_val_if_fail(emu != NULL, NULL); + + ewin = g_slice_new0(TilemEmulatorWindow); + ewin->emu = emu; + + tilem_config_get("settings", + "skin_disabled/b", &ewin->skin_disabled, + "smooth_scaling/b=1", &ewin->lcd_smooth_scale, + "zoom/r=2.0", &ewin->zoom_factor, + NULL); + + if (ewin->zoom_factor <= 1.0) + ewin->zoom_factor = 1.0; + + /* Create the window */ + ewin->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + g_signal_connect(ewin->window, "window-state-event", + G_CALLBACK(window_state_changed), ewin); + g_signal_connect(ewin->window, "destroy", + G_CALLBACK(window_destroy), ewin); + + g_signal_connect_after(ewin->window, "check-resize", + G_CALLBACK(set_size_hints), ewin); + + gtk_widget_add_events(ewin->window, (GDK_KEY_PRESS_MASK + | GDK_KEY_RELEASE_MASK)); + + g_signal_connect(ewin->window, "key-press-event", + G_CALLBACK(key_press_event), ewin); + g_signal_connect(ewin->window, "key-release-event", + G_CALLBACK(key_release_event), ewin); + + build_menu(ewin); + + tilem_emulator_window_calc_changed(ewin); + + return ewin; +} + +void tilem_emulator_window_free(TilemEmulatorWindow *ewin) +{ + g_return_if_fail(ewin != NULL); + + if (ewin->lcd) + gtk_widget_destroy(ewin->lcd); + if (ewin->background) + gtk_widget_destroy(ewin->background); + if (ewin->layout) + gtk_widget_destroy(ewin->layout); + if (ewin->window) + gtk_widget_destroy(ewin->window); + if (ewin->popup_menu) + gtk_widget_destroy(ewin->popup_menu); + if (ewin->actions) + g_object_unref(ewin->actions); + + g_free(ewin->lcd_image_buf); + + g_free(ewin->skin_file_name); + if (ewin->skin) { + skin_unload(ewin->skin); + g_free(ewin->skin); + } + + if (ewin->lcd_cmap) + gdk_rgb_cmap_free(ewin->lcd_cmap); + + g_slice_free(TilemEmulatorWindow, ewin); +} + +void tilem_emulator_window_set_skin(TilemEmulatorWindow *ewin, + const char *filename) +{ + g_return_if_fail(ewin != NULL); + + g_free(ewin->skin_file_name); + if (filename) + ewin->skin_file_name = g_strdup(filename); + else + ewin->skin_file_name = NULL; + redraw_screen(ewin); +} + +/* Switch between skin and LCD-only mode */ +void tilem_emulator_window_set_skin_disabled(TilemEmulatorWindow *ewin, + gboolean disabled) +{ + g_return_if_fail(ewin != NULL); + + if (ewin->skin_disabled != !!disabled) { + ewin->skin_disabled = !!disabled; + redraw_screen(ewin); + } +} + +void tilem_emulator_window_calc_changed(TilemEmulatorWindow *ewin) +{ + const char *model; + char *name = NULL, *path; + + g_return_if_fail(ewin != NULL); + g_return_if_fail(ewin->emu != NULL); + + g_free(ewin->skin_file_name); + ewin->skin_file_name = NULL; + + if (!ewin->emu->calc) + return; + + model = ewin->emu->calc->hw.name; + + tilem_config_get(model, + "skin/f", &name, + NULL); + + if (!name) + name = g_strdup_printf("%s.skn", model); + + if (!g_path_is_absolute(name)) { + path = get_shared_file_path("skins", name, NULL); + tilem_emulator_window_set_skin(ewin, path); + g_free(path); + } + else { + tilem_emulator_window_set_skin(ewin, name); + } + + g_free(name); +} + +void tilem_emulator_window_refresh_lcd(TilemEmulatorWindow *ewin) +{ + g_return_if_fail(ewin != NULL); + if (ewin->lcd) + gtk_widget_queue_draw(ewin->lcd); +} + + + + +/* Display the lcd image into the terminal */ +void display_lcdimage_into_terminal(TilemEmulatorWindow *ewin) +{ + + int width, height; + guchar* lcddata; + int x, y; + char c; + width = ewin->emu->calc->hw.lcdwidth; + height = ewin->emu->calc->hw.lcdheight; + FILE* lcd_content_file; + /* Alloc mmem */ + lcddata = g_new(guchar, (width / 8) * height); + + /* Get the lcd content using the function 's pointer from Benjamin's core */ + (*ewin->emu->calc->hw.get_lcd)(ewin->emu->calc, lcddata); + + /* Print a little demo just for fun;) */ + printf("\n\n\n"); + printf(" r rr r rrrrr rrr r rrrrr r r rr r rr r \n"); + printf(" r r r r r r r r rr rr r r r r r r r \n"); + printf(" r r r r r r r r r r r r r r r r r r \n"); + printf("rrrrr r r r r r r rrrr r r r r r r r rrrrr rrrrr rrrrr \n"); + printf(" r r r r r r r r r r rrr r r r r r r \n"); + printf(" r r r r r r r r r r r r r r r r \n"); + printf(" r rr r r rrr rrrrr rrrrr r r r rr r \n"); + printf("\n(Here is just a sample...)\n\n"); + + /* Request user to know which char user wants */ + + printf("Which char to display FOR BLACK?\n"); + scanf("%c", &c); /* Choose wich char for the black */ + + //printf("Which char to display FOR GRAY ?\n"); + //scanf("%c", &b); /* Choose wich char for the black */ + + lcd_content_file = g_fopen("lcd_content.txt", "w"); + + printf("\n\n\n### LCD CONTENT ###\n\n\n\n"); + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + /*printf("%d ", lcddata[y * width + x]); */ + if (lcddata[(y * width + x) / 8] & (0x80 >> (x % 8))) { + printf("%c", c); + if(lcd_content_file != NULL) + fprintf(lcd_content_file,"%c", c); + } else { + printf("%c", ' '); + if(lcd_content_file != NULL) + fprintf(lcd_content_file,"%c", ' '); + } + } + printf("\n"); + if(lcd_content_file != NULL) + fprintf(lcd_content_file,"%c", '\n'); + } + if(lcd_content_file != NULL) { + fclose(lcd_content_file); + printf("\n### END ###\n\nSaved into lcd_content.txt\n"); + } + +} diff --git a/tool/tilem-src/gui/emuwin.h b/tool/tilem-src/gui/emuwin.h new file mode 100644 index 0000000..74fb3ca --- /dev/null +++ b/tool/tilem-src/gui/emuwin.h @@ -0,0 +1,78 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Root window view (widgets and flags) */ +typedef struct _TilemEmulatorWindow { + TilemCalcEmulator *emu; + + GtkWidget *window; /* The top level window */ + GtkWidget *layout; /* Layout */ + GtkWidget *lcd; + GtkWidget *background; + GtkWidget *popup_menu; + + GtkActionGroup *actions; + + GdkGeometry geomhints; + GdkWindowHints geomhintmask; + + byte* lcd_image_buf; + int lcd_image_width; + int lcd_image_height; + GdkRgbCmap* lcd_cmap; + gboolean lcd_smooth_scale; + + char *skin_file_name; + SKIN_INFOS *skin; + gboolean skin_disabled; /* A flag to know if skinless or not */ + gdouble base_zoom; + gdouble zoom_factor; + GdkWindowState window_state; + + int mouse_key; /* Key currently pressed by mouse button */ + + /* Host keycode used to activate each key, if any */ + int keypress_keycodes[64]; + int sequence_keycode; + +} TilemEmulatorWindow; + +/* Create a new TilemEmulatorWindow. */ +TilemEmulatorWindow *tilem_emulator_window_new(TilemCalcEmulator *emu); + +/* Free a TilemEmulatorWindow. */ +void tilem_emulator_window_free(TilemEmulatorWindow *ewin); + +/* Load a skin file. */ +void tilem_emulator_window_set_skin(TilemEmulatorWindow *ewin, + const char *filename); + +/* Enable or disable skin. */ +void tilem_emulator_window_set_skin_disabled(TilemEmulatorWindow *ewin, + gboolean disabled); + +/* New calculator loaded. */ +void tilem_emulator_window_calc_changed(TilemEmulatorWindow *ewin); + +/* Redraw LCD contents. */ +void tilem_emulator_window_refresh_lcd(TilemEmulatorWindow *ewin); + +/* Prompt for a ROM file to open */ +gboolean tilem_emulator_window_prompt_open_rom(TilemEmulatorWindow *ewin); diff --git a/tool/tilem-src/gui/event.c b/tool/tilem-src/gui/event.c new file mode 100644 index 0000000..33fd478 --- /dev/null +++ b/tool/tilem-src/gui/event.c @@ -0,0 +1,436 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "files.h" +#include "filedlg.h" + +/* Table for translating skin-file key number (based on actual + position, and defined by the VTI/TiEmu file formats) into a + scancode. Note that the TILEM_KEY_* constants are named according + to the TI-83 keypad layout; other models use different names for + the keys, but the same scancodes. */ +static const int keycode_map[] = + { TILEM_KEY_YEQU, + TILEM_KEY_WINDOW, + TILEM_KEY_ZOOM, + TILEM_KEY_TRACE, + TILEM_KEY_GRAPH, + + TILEM_KEY_2ND, + TILEM_KEY_MODE, + TILEM_KEY_DEL, + TILEM_KEY_LEFT, + TILEM_KEY_RIGHT, + TILEM_KEY_UP, + TILEM_KEY_DOWN, + TILEM_KEY_ALPHA, + TILEM_KEY_GRAPHVAR, + TILEM_KEY_STAT, + + TILEM_KEY_MATH, + TILEM_KEY_MATRIX, + TILEM_KEY_PRGM, + TILEM_KEY_VARS, + TILEM_KEY_CLEAR, + + TILEM_KEY_RECIP, + TILEM_KEY_SIN, + TILEM_KEY_COS, + TILEM_KEY_TAN, + TILEM_KEY_POWER, + + TILEM_KEY_SQUARE, + TILEM_KEY_COMMA, + TILEM_KEY_LPAREN, + TILEM_KEY_RPAREN, + TILEM_KEY_DIV, + + TILEM_KEY_LOG, + TILEM_KEY_7, + TILEM_KEY_8, + TILEM_KEY_9, + TILEM_KEY_MUL, + + TILEM_KEY_LN, + TILEM_KEY_4, + TILEM_KEY_5, + TILEM_KEY_6, + TILEM_KEY_SUB, + + TILEM_KEY_STORE, + TILEM_KEY_1, + TILEM_KEY_2, + TILEM_KEY_3, + TILEM_KEY_ADD, + + TILEM_KEY_ON, + TILEM_KEY_0, + TILEM_KEY_DECPNT, + TILEM_KEY_CHS, + TILEM_KEY_ENTER }; + +/* Find the keycode for the key (if any) at the given position. If + the keys overlap, choose the "nearest" (according to Manhattan + distance to the midpoint.) */ +static int scan_click(const SKIN_INFOS* skin, double x, double y) +{ + guint ix, iy, nearest = 0, i; + int dx, dy, d, best_d = G_MAXINT; + + if (!skin) + return 0; + + ix = (x * skin->sx + 0.5); + iy = (y * skin->sy + 0.5); + + for (i = 0; i < G_N_ELEMENTS(keycode_map); i++) { + if (ix >= skin->keys_pos[i].left + && ix < skin->keys_pos[i].right + && iy >= skin->keys_pos[i].top + && iy < skin->keys_pos[i].bottom) { + dx = (skin->keys_pos[i].left + skin->keys_pos[i].right + - 2 * ix); + dy = (skin->keys_pos[i].top + skin->keys_pos[i].bottom + - 2 * iy); + d = ABS(dx) + ABS(dy); + + if (d < best_d) { + best_d = d; + nearest = keycode_map[i]; + } + } + } + + return nearest; +} + +/* Retrieve pointer coordinates for an input device. */ +static void get_device_pointer(GdkWindow *win, GdkDevice *dev, + gdouble *x, gdouble *y, GdkModifierType *mask) +{ + gdouble *axes; + int i; + + axes = g_new(gdouble, dev->num_axes); + gdk_device_get_state(dev, win, axes, mask); + + for (i = 0; i < dev->num_axes; i++) { + if (x && dev->axes[i].use == GDK_AXIS_X) + *x = axes[i]; + else if (y && dev->axes[i].use == GDK_AXIS_Y) + *y = axes[i]; + } + + g_free(axes); +} + +/* Show a nice GtkAboutDialog */ +void show_about() +{ + GtkWidget *dialog = gtk_about_dialog_new(); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), "2.0"); + gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), "(c) Benjamin Moody\n(c) Thibault Duponchelle\n(c) Luc Bruant\n"); + gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), "TilEm is a TI Linux Emulator.\n It emulates all current z80 models.\n TI73, TI76, TI81, TI82, TI83(+)(SE), TI84+(SE), TI85 and TI86 ;D"); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://lpg.ticalc.org/prj_tilem/"); + + /* Add the logo */ + char* tilem_ban = get_shared_file_path("pixs", "tilem_ban.png", NULL); + if(tilem_ban) { + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(tilem_ban, NULL); + gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf); + g_object_unref(pixbuf), pixbuf = NULL; + } + + gtk_dialog_run(GTK_DIALOG (dialog)); + gtk_widget_destroy(dialog); + +} + + +void launch_debugger(TilemEmulatorWindow *ewin) +{ + if (!ewin->emu->dbg) + ewin->emu->dbg = tilem_debugger_new(ewin->emu); + tilem_debugger_show(ewin->emu->dbg); +} + +/* Press a key, ensuring that at most one key is "pressed" at a time + due to this function (if pointer moves or is released, we don't + want the old key held down.) + + FIXME: on multi-pointer displays, allow each input device to act + separately */ +static void press_mouse_key(TilemEmulatorWindow* ewin, int key) +{ + if (ewin->mouse_key == key) + return; + + tilem_calc_emulator_release_key(ewin->emu, ewin->mouse_key); + tilem_calc_emulator_press_key(ewin->emu, key); + ewin->mouse_key = key; +} + +/* Mouse button pressed */ +gboolean mouse_press_event(G_GNUC_UNUSED GtkWidget* w, GdkEventButton *event, + gpointer data) +{ + TilemEmulatorWindow* ewin = data; + int key; + + key = scan_click(ewin->skin, event->x, event->y); + + if (event->button == 1) { + /* button 1: press key until button is released or + pointer moves away */ + press_mouse_key(ewin, key); + return TRUE; + } + else if (event->button == 2) { + /* button 2: hold key down permanently */ + tilem_calc_emulator_press_key(ewin->emu, key); + return TRUE; + } + else if (event->button == 3) { + /* button 3: popup menu */ + gtk_menu_popup(GTK_MENU(ewin->popup_menu), + NULL, NULL, NULL, NULL, + event->button, event->time); + return TRUE; + } + else + return FALSE; +} + +/* Mouse pointer moved */ +gboolean pointer_motion_event(G_GNUC_UNUSED GtkWidget* w, GdkEventMotion *event, + gpointer data) +{ + TilemEmulatorWindow* ewin = data; + int key; + + if (event->is_hint) + get_device_pointer(event->window, event->device, + &event->x, &event->y, &event->state); + + if (event->state & GDK_BUTTON1_MASK) + key = scan_click(ewin->skin, event->x, event->y); + else + key = 0; + + press_mouse_key(ewin, key); + + return FALSE; +} + +/* Mouse button released */ +gboolean mouse_release_event(G_GNUC_UNUSED GtkWidget* w, GdkEventButton *event, + gpointer data) +{ + TilemEmulatorWindow* ewin = data; + + if (event->button == 1) + press_mouse_key(ewin, 0); + + return FALSE; +} + +/* Find key binding for the given keysym and modifiers */ +static TilemKeyBinding* find_key_binding_for_keysym(TilemCalcEmulator* emu, + guint keyval, + GdkModifierType mods) +{ + int i; + + for (i = 0; i < emu->nkeybindings; i++) + if (keyval == emu->keybindings[i].keysym + && mods == emu->keybindings[i].modifiers) + return &emu->keybindings[i]; + + return NULL; +} + +/* Find key binding matching the given event */ +static TilemKeyBinding* find_key_binding(TilemCalcEmulator* emu, + GdkEventKey* event) +{ + GdkDisplay *dpy; + GdkKeymap *km; + guint keyval; + GdkModifierType consumed, mods; + TilemKeyBinding *kb; + + dpy = gdk_drawable_get_display(event->window); + km = gdk_keymap_get_for_display(dpy); + + /* determine the relevant set of modifiers */ + + gdk_keymap_translate_keyboard_state(km, event->hardware_keycode, + event->state, event->group, + &keyval, NULL, NULL, &consumed); + + mods = (event->state & ~consumed + & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)); + + if (event->state & GDK_LOCK_MASK + && (kb = find_key_binding_for_keysym(emu, keyval, + mods | GDK_LOCK_MASK))) { + return kb; + } + + return find_key_binding_for_keysym(emu, keyval, mods); +} + +/* Key-press event */ +gboolean key_press_event(G_GNUC_UNUSED GtkWidget* w, GdkEventKey* event, + gpointer data) +{ + TilemEmulatorWindow *ewin = data; + TilemKeyBinding *kb; + int i, key; + unsigned int hwkey; + + /* Ignore repeating keys */ + for (i = 0; i < 64; i++) + if (ewin->keypress_keycodes[i] == event->hardware_keycode) + return TRUE; + if (ewin->sequence_keycode == event->hardware_keycode) + return TRUE; + + if (!(kb = find_key_binding(ewin->emu, event))) + return FALSE; + + hwkey = event->hardware_keycode; + + if (kb->nscancodes == 1) { + /* if queue is empty, just press the key; otherwise + add it to the queue */ + key = kb->scancodes[0]; + if (tilem_calc_emulator_press_or_queue(ewin->emu, key)) + ewin->sequence_keycode = hwkey; + else + ewin->keypress_keycodes[key] = hwkey; + } + else { + tilem_calc_emulator_queue_keys(ewin->emu, kb->scancodes, + kb->nscancodes); + ewin->sequence_keycode = hwkey; + } + + return TRUE; +} + +/* Key-release event */ +gboolean key_release_event(G_GNUC_UNUSED GtkWidget* w, GdkEventKey* event, + gpointer data) +{ + TilemEmulatorWindow *ewin = data; + int i; + + /* Check if the key that was just released was one that + activated a calculator keypress. (Do not try to look up + event->keyval; modifiers may have changed since the key was + pressed.) */ + for (i = 0; i < 64; i++) { + if (ewin->keypress_keycodes[i] == event->hardware_keycode) { + tilem_calc_emulator_release_key(ewin->emu, i); + ewin->keypress_keycodes[i] = 0; + } + } + + if (ewin->sequence_keycode == event->hardware_keycode) { + tilem_calc_emulator_release_queued_key(ewin->emu); + ewin->sequence_keycode = 0; + } + + return FALSE; +} + +static void place_menu(GtkMenu *menu, gint *x, gint *y, + gboolean *push_in, gpointer data) +{ + GtkWidget *w = data; + GdkWindow *win; + GdkScreen *screen; + int n; + + win = gtk_widget_get_window(w); + gdk_window_get_origin(win, x, y); + + screen = gdk_drawable_get_screen(win); + n = gdk_screen_get_monitor_at_point(screen, *x, *y); + gtk_menu_set_monitor(menu, n); + + *push_in = FALSE; +} + +/* Pop up menu on main window */ +gboolean popup_menu_event(GtkWidget* w, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + + gtk_menu_popup(GTK_MENU(ewin->popup_menu), + NULL, NULL, &place_menu, w, + 0, gtk_get_current_event_time()); + + return TRUE; +} + +/* Callback function for the drag and drop event */ +void drag_data_received(G_GNUC_UNUSED GtkWidget *win, + G_GNUC_UNUSED GdkDragContext *dc, + G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, + GtkSelectionData *seldata, + G_GNUC_UNUSED guint info, G_GNUC_UNUSED guint t, + gpointer data) +{ + TilemEmulatorWindow *ewin = data; + gchar **uris, **filenames; + gint i, j, n; + + uris = gtk_selection_data_get_uris(seldata); + if (!uris) + return; + + n = g_strv_length(uris); + filenames = g_new0(gchar *, n + 1); + + for (i = j = 0; i < n; i++) { + filenames[j] = g_filename_from_uri(uris[i], NULL, NULL); + if (filenames[j]) + j++; + } + filenames[j] = NULL; + + load_files(ewin, filenames); + g_strfreev(filenames); +} diff --git a/tool/tilem-src/gui/filedlg.c b/tool/tilem-src/gui/filedlg.c new file mode 100644 index 0000000..0aefa6d --- /dev/null +++ b/tool/tilem-src/gui/filedlg.c @@ -0,0 +1,1039 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * Copyright (c) 2011 Thibault Duponchelle // FIXME : My work is based on yours benjamin. Should I put "portions"?! Or something else ? + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "gtk-compat.h" +#include "filedlg.h" + +#ifdef GDK_WINDOWING_WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# include +# include +# include +# include + +# ifndef OPENFILENAME_SIZE_VERSION_400 +# define OPENFILENAME_SIZE_VERSION_400 sizeof(OPENFILENAMEA) +# endif + +struct fcinfo { + const char *title; + gboolean save; + HWND parent_window; + char *filename; + char *dirname; + char *extension; + const char *filters; + unsigned int flags; +}; + +#define BUFFER_SIZE 32768 + +static char * file_chooser_main(const struct fcinfo *fci) +{ + if (G_WIN32_HAVE_WIDECHAR_API()) { + OPENFILENAMEW ofnw; + wchar_t *titlew, *filterw, *initdirw, *defextw; + wchar_t filenamew[BUFFER_SIZE + 1]; + wchar_t *p; + int result; + int i; + + titlew = g_utf8_to_utf16(fci->title, -1, 0, 0, 0); + + filterw = g_utf8_to_utf16(fci->filters, -1, 0, 0, 0); + for (i = 0; filterw[i]; i++) + if (filterw[i] == '\n') filterw[i] = 0; + + memset(&ofnw, 0, sizeof(ofnw)); + ofnw.lStructSize = OPENFILENAME_SIZE_VERSION_400; + + ofnw.hwndOwner = fci->parent_window; + ofnw.lpstrTitle = titlew; + ofnw.lpstrFilter = filterw; + ofnw.nFilterIndex = 1; + ofnw.lpstrFile = filenamew; + ofnw.nMaxFile = BUFFER_SIZE; + + memset(filenamew, 0, sizeof(filenamew)); + + if (fci->filename) { + p = g_utf8_to_utf16(fci->filename, -1, 0, 0, 0); + if (p) { + wcsncpy(filenamew, p, BUFFER_SIZE); + g_free(p); + } + } + + if (fci->dirname) + initdirw = g_utf8_to_utf16(fci->dirname, -1, 0, 0, 0); + else + initdirw = NULL; + + if (fci->extension) + defextw = g_utf8_to_utf16(fci->extension, -1, 0, 0, 0); + else + defextw = NULL; + + ofnw.lpstrInitialDir = initdirw; + ofnw.lpstrDefExt = defextw; + + ofnw.Flags = fci->flags; + + result = (fci->save + ? GetSaveFileNameW(&ofnw) + : GetOpenFileNameW(&ofnw)); + + g_free(titlew); + g_free(filterw); + g_free(initdirw); + g_free(defextw); + + if (!result) + return NULL; + + if ((fci->flags & OFN_ALLOWMULTISELECT)) { + for (i = 0; i < BUFFER_SIZE; i++) { + if (filenamew[i] == 0 && filenamew[i + 1] == 0) + break; + else if (filenamew[i] == '/') + filenamew[i] = '\\'; + else if (filenamew[i] == 0) + filenamew[i] = '/'; + } + } + + return g_utf16_to_utf8(filenamew, -1, 0, 0, 0); + } + else { + OPENFILENAMEA ofna; + char *titlel, *filterl, *initdirl, *defextl; + char filenamel[BUFFER_SIZE + 1]; + char *p; + int result; + int i; + + titlel = g_locale_from_utf8(fci->title, -1, 0, 0, 0); + + filterl = g_locale_from_utf8(fci->filters, -1, 0, 0, 0); + for (i = 0; filterl[i]; i++) + if (filterl[i] == '\n') filterl[i] = 0; + + memset(&ofna, 0, sizeof(ofna)); + ofna.lStructSize = OPENFILENAME_SIZE_VERSION_400; + + ofna.hwndOwner = fci->parent_window; + ofna.lpstrTitle = titlel; + ofna.lpstrFilter = filterl; + ofna.nFilterIndex = 1; + ofna.lpstrFile = filenamel; + ofna.nMaxFile = BUFFER_SIZE; + + memset(filenamel, 0, sizeof(filenamel)); + + if (fci->filename) { + p = g_locale_from_utf8(fci->filename, -1, 0, 0, 0); + if (p) { + strncpy(filenamel, p, BUFFER_SIZE); + g_free(p); + } + } + + if (fci->dirname) + initdirl = g_locale_from_utf8(fci->dirname, -1, 0, 0, 0); + else + initdirl = NULL; + + if (fci->extension) + defextl = g_locale_from_utf8(fci->extension, -1, 0, 0, 0); + else + defextl = NULL; + + ofna.lpstrInitialDir = initdirl; + ofna.lpstrDefExt = defextl; + + ofna.Flags = fci->flags; + + result = (fci->save + ? GetSaveFileNameA(&ofna) + : GetOpenFileNameA(&ofna)); + + g_free(titlel); + g_free(filterl); + g_free(initdirl); + g_free(defextl); + + if (!result) + return NULL; + + if ((fci->flags & OFN_ALLOWMULTISELECT)) { + for (i = 0; i < BUFFER_SIZE; i++) { + if (filenamel[i] == 0 && filenamel[i + 1] == 0) + break; + else if (filenamel[i] == '/') + filenamel[i] = '\\'; + else if (filenamel[i] == 0) + filenamel[i] = '/'; + } + } + + return g_locale_to_utf8(filenamel, -1, 0, 0, 0); + } +} + +static gboolean wakeup(G_GNUC_UNUSED gpointer data) +{ + gtk_main_quit(); + return FALSE; +} + +static gpointer file_chooser_thread(gpointer data) +{ + struct fcinfo *fci = data; + gpointer res = file_chooser_main(fci); + g_idle_add(wakeup, NULL); + return res; +} + +static char * build_filter_string(const char *desc1, + const char *pattern1, + va_list ap) +{ + GString *str = g_string_new(NULL); + + while (desc1 && pattern1) { + if (pattern1[0]) { + g_string_append(str, desc1); + g_string_append_c(str, '\n'); + g_string_append(str, pattern1); + g_string_append_c(str, '\n'); + } + + desc1 = va_arg(ap, char *); + if (!desc1) break; + pattern1 = va_arg(ap, char *); + } + + return g_string_free(str, FALSE); +} + +static char ** run_file_chooser1(const char *title, + GtkWindow *parent, + gboolean save, + gboolean multiple, + const char *suggest_name, + const char *suggest_dir, + const char *filters) +{ + struct fcinfo fci; + GThread *thread; + GtkWidget *dummy; + GdkWindow *pwin; + char *fname, *p, *dir; + char **result; + int i; + + if (!g_thread_supported()) + g_thread_init(NULL); + + fci.title = title; + fci.save = save; + + if (parent && (pwin = gtk_widget_get_window(GTK_WIDGET(parent)))) + fci.parent_window = GDK_WINDOW_HWND(pwin); + else + fci.parent_window = NULL; + + if (suggest_name && suggest_dir) { + fci.filename = g_build_filename(suggest_dir, + suggest_name, NULL); + fci.dirname = NULL; + } + else if (suggest_name) { + fci.filename = g_strdup(suggest_name); + fci.dirname = NULL; + } + else if (suggest_dir) { + fci.filename = NULL; + fci.dirname = g_strdup(suggest_dir); + } + else { + fci.filename = fci.dirname = NULL; + } + + if (suggest_name && (p = strrchr(suggest_name, '.'))) + fci.extension = g_strdup(p + 1); + else + fci.extension = NULL; + + fci.filters = filters; + + fci.flags = (OFN_HIDEREADONLY | OFN_EXPLORER); + + if (save) + fci.flags |= OFN_OVERWRITEPROMPT; + else { + fci.flags |= OFN_FILEMUSTEXIST; + if (multiple) + fci.flags |= OFN_ALLOWMULTISELECT; + } + + if ((thread = g_thread_create(file_chooser_thread, &fci, TRUE, NULL))) { + dummy = gtk_invisible_new(); + gtk_grab_add(dummy); + gtk_main(); + fname = g_thread_join(thread); + gtk_widget_destroy(dummy); + } + else { + fname = file_chooser_main(&fci); + } + + g_free(fci.filename); + g_free(fci.dirname); + g_free(fci.extension); + + if (!fname) { + return NULL; + } + else if (multiple && (p = strchr(fname, '/'))) { + dir = g_strndup(fname, p - fname); + result = g_strsplit(p + 1, "/", -1); + + for (i = 0; result[i]; i++) { + p = result[i]; + result[i] = g_build_filename(dir, p, NULL); + g_free(p); + } + + g_free(fname); + return result; + } + else { + result = g_new(char *, 2); + result[0] = fname; + result[1] = NULL; + return result; + } +} + +static char ** run_file_chooser(const char *title, + GtkWindow *parent, + gboolean save, + gboolean multiple, + const char *suggest_name, + const char *suggest_dir, + const char *desc1, + const char *pattern1, + va_list ap) +{ + char *filters; + char **result; + filters = build_filter_string(desc1, pattern1, ap); + result = run_file_chooser1(title, parent, save, multiple, + suggest_name, suggest_dir, filters); + g_free(filters); + return result; +} + +struct dcinfo { + const char *title; + HWND parent_window; + wchar_t *suggest_dir_w; + char *suggest_dir_l; +}; + +static int CALLBACK dir_chooser_callback(HWND hwnd, UINT uMsg, + G_GNUC_UNUSED LPARAM lParam, + LPARAM lpData) +{ + const struct dcinfo *dci = (struct dcinfo*) lpData; + + if (uMsg != BFFM_INITIALIZED) + return 0; + + if (G_WIN32_HAVE_WIDECHAR_API()) + SendMessageW(hwnd, BFFM_SETSELECTIONW, + TRUE, (LPARAM) dci->suggest_dir_w); + else + SendMessageA(hwnd, BFFM_SETSELECTIONA, + TRUE, (LPARAM) dci->suggest_dir_l); + return 0; +} + +static char * dir_chooser_main(const struct dcinfo *dci) +{ + LPITEMIDLIST idl; + char *result = NULL; + + CoInitialize(NULL); + + if (G_WIN32_HAVE_WIDECHAR_API()) { + BROWSEINFOW bifw; + wchar_t dirnamew[MAX_PATH + 1]; + + memset(&bifw, 0, sizeof(bifw)); + bifw.hwndOwner = dci->parent_window; + bifw.lpszTitle = g_utf8_to_utf16(dci->title, -1, 0, 0, 0); + bifw.ulFlags = (BIF_RETURNONLYFSDIRS | BIF_USENEWUI); + bifw.lpfn = &dir_chooser_callback; + bifw.lParam = (LPARAM) dci; + + idl = SHBrowseForFolderW(&bifw); + if (idl && SHGetPathFromIDListW(idl, dirnamew)) + result = g_utf16_to_utf8(dirnamew, -1, 0, 0, 0); + } + else { + BROWSEINFOA bifa; + char dirnamel[MAX_PATH + 1]; + + memset(&bifa, 0, sizeof(bifa)); + bifa.hwndOwner = dci->parent_window; + bifa.lpszTitle = g_locale_from_utf8(dci->title, -1, 0, 0, 0); + bifa.ulFlags = (BIF_RETURNONLYFSDIRS | BIF_USENEWUI); + bifa.lpfn = &dir_chooser_callback; + bifa.lParam = (LPARAM) dci; + + idl = SHBrowseForFolderA(&bifa); + if (idl && SHGetPathFromIDListA(idl, dirnamel)) + result = g_locale_to_utf8(dirnamel, -1, 0, 0, 0); + } + + if (idl) + CoTaskMemFree(idl); + + CoUninitialize(); + + return result; +} + +static gpointer dir_chooser_thread(gpointer data) +{ + struct dcinfo *dci = data; + gpointer res = dir_chooser_main(dci); + g_idle_add(wakeup, NULL); + return res; +} + +static char* run_dir_chooser(G_GNUC_UNUSED const char *title, + GtkWindow *parent, + G_GNUC_UNUSED gboolean save, + const char *suggest_dir) +{ + struct dcinfo dci; + GdkWindow *pwin; + GThread *thread; + GtkWidget *dummy; + char *dname; + + if (!g_thread_supported()) + g_thread_init(NULL); + + dci.title = "Select a folder to save received files."; + + if (parent && (pwin = gtk_widget_get_window(GTK_WIDGET(parent)))) + dci.parent_window = GDK_WINDOW_HWND(pwin); + else + dci.parent_window = NULL; + + if (suggest_dir) { + dci.suggest_dir_w = g_utf8_to_utf16(suggest_dir, -1, 0, 0, 0); + dci.suggest_dir_l = g_locale_from_utf8(suggest_dir, -1, 0, 0, 0); + } + else { + dci.suggest_dir_w = NULL; + dci.suggest_dir_l = NULL; + } + + if ((thread = g_thread_create(dir_chooser_thread, &dci, TRUE, NULL))) { + dummy = gtk_invisible_new(); + gtk_grab_add(dummy); + gtk_main(); + dname = g_thread_join(thread); + gtk_widget_destroy(dummy); + } + else { + dname = dir_chooser_main(&dci); + } + + g_free(dci.suggest_dir_w); + g_free(dci.suggest_dir_l); + + return dname; +} + +#else /* ! GDK_WINDOWING_WIN32 */ + +/* Case insensitive filter function */ +static gboolean filter_lowercase(const GtkFileFilterInfo *info, + gpointer data) +{ + GSList *list = data; + const char *base; + char *lowercase, *reversed; + int length; + gboolean matched = FALSE; + + if ((base = strrchr(info->filename, G_DIR_SEPARATOR))) + base++; + else + base = info->filename; + + lowercase = g_ascii_strdown(base, -1); + length = strlen(lowercase); + reversed = g_memdup(lowercase, length + 1); + g_strreverse(reversed); + + while (list) { + if (g_pattern_match(list->data, length, + lowercase, reversed)) { + matched = TRUE; + break; + } + list = list->next; + } + + g_free(lowercase); + g_free(reversed); + return matched; +} + +static void free_filter_info(gpointer data) +{ + GSList *list = data, *l; + for (l = list; l; l = l->next) + g_pattern_spec_free(l->data); + g_slist_free(list); +} + +static void setup_file_filters(GtkFileChooser *chooser, + const char *desc1, + const char *pattern1, + va_list ap) +{ + GtkFileFilter *ffilt; + char **pats; + GPatternSpec *pspec; + GSList *pspeclist; + int i; + + while (desc1 && pattern1) { + if (pattern1[0]) { + ffilt = gtk_file_filter_new(); + gtk_file_filter_set_name(ffilt, desc1); + + pats = g_strsplit(pattern1, ";", -1); + pspeclist = NULL; + for (i = 0; pats && pats[i]; i++) { + pspec = g_pattern_spec_new(pats[i]); + pspeclist = g_slist_prepend(pspeclist, pspec); + } + g_strfreev(pats); + + gtk_file_filter_add_custom(ffilt, GTK_FILE_FILTER_FILENAME, + &filter_lowercase, + pspeclist, + &free_filter_info); + + gtk_file_chooser_add_filter(chooser, ffilt); + } + + desc1 = va_arg(ap, char *); + if (!desc1) break; + pattern1 = va_arg(ap, char *); + } +} + +static gboolean prompt_overwrite(const char *fname, + GtkWindow *parent) +{ + GtkWidget *dlg; + GtkWidget *button; + char *p, *q; + + if (!g_file_test(fname, G_FILE_TEST_EXISTS)) + return TRUE; + + if (!g_file_test(fname, G_FILE_TEST_IS_REGULAR)) + return FALSE; + + p = g_filename_display_basename(fname); + dlg = gtk_message_dialog_new(parent, + GTK_DIALOG_MODAL, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + "A file named \"%s\" already exists. " + "Do you want to replace it?", + p); + g_free(p); + + p = g_path_get_dirname(fname); + q = g_filename_display_basename(p); + gtk_message_dialog_format_secondary_markup + (GTK_MESSAGE_DIALOG(dlg), + "The file already exists in \"%s\". Replacing it will " + "overwrite its contents.", q); + g_free(p); + g_free(q); + + gtk_dialog_add_button(GTK_DIALOG(dlg), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + + button = gtk_button_new_with_mnemonic("_Replace"); + gtk_widget_set_can_default(button, TRUE); + gtk_button_set_image(GTK_BUTTON(button), + gtk_image_new_from_stock(GTK_STOCK_SAVE, + GTK_ICON_SIZE_BUTTON)); + gtk_widget_show(button); + gtk_dialog_add_action_widget(GTK_DIALOG(dlg), button, + GTK_RESPONSE_ACCEPT); + + gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + + if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_ACCEPT) { + gtk_widget_destroy(dlg); + return TRUE; + } + + gtk_widget_destroy(dlg); + return FALSE; +} + +static char ** run_file_chooser(const char *title, + GtkWindow *parent, + gboolean save, + gboolean multiple, + const char *suggest_name, + const char *suggest_dir, + const char *desc1, + const char *pattern1, + va_list ap) +{ + GtkWidget *filesel; + GSList *filelist, *l; + char *fname; + char **fnames; + int i, n; + + filesel = gtk_file_chooser_dialog_new(title, parent, + (save + ? GTK_FILE_CHOOSER_ACTION_SAVE + : GTK_FILE_CHOOSER_ACTION_OPEN), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + (save + ? GTK_STOCK_SAVE + : GTK_STOCK_OPEN), + GTK_RESPONSE_ACCEPT, + NULL); + + gtk_dialog_set_alternative_button_order(GTK_DIALOG(filesel), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + + gtk_dialog_set_default_response(GTK_DIALOG(filesel), + GTK_RESPONSE_ACCEPT); + + if (suggest_dir) + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filesel), + suggest_dir); + + if (suggest_name) + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filesel), + suggest_name); + + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filesel), + multiple); + + setup_file_filters(GTK_FILE_CHOOSER(filesel), desc1, pattern1, ap); + + while (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) { + if (save) { + fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel)); + if (!fname || !prompt_overwrite(fname, GTK_WINDOW(filesel))) { + g_free(fname); + continue; + } + + fnames = g_new(char *, 2); + fnames[0] = fname; + fnames[1] = NULL; + + gtk_widget_destroy(filesel); + return fnames; + } + else { + filelist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(filesel)); + if (!filelist) + continue; + + n = g_slist_length(filelist); + fnames = g_new(char *, n + 1); + i = 0; + for (l = filelist; l; l = l->next) + fnames[i++] = l->data; + g_slist_free(filelist); + fnames[n] = NULL; + + for (i = 0; i < n; i++) + if (!g_file_test(fnames[i], + G_FILE_TEST_IS_REGULAR)) + break; + if (i < n) { + g_strfreev(fnames); + continue; + } + + gtk_widget_destroy(filesel); + return fnames; + } + } + + gtk_widget_destroy(filesel); + return NULL; +} + +static char* run_dir_chooser(const char *title, + GtkWindow *parent, + gboolean save, + const char *suggest_dir) +{ + GtkWidget *filesel; + char *fname; + + filesel = gtk_file_chooser_dialog_new(title, parent, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + (save + ? GTK_STOCK_SAVE + : GTK_STOCK_OPEN), + GTK_RESPONSE_ACCEPT, + NULL); + + gtk_dialog_set_alternative_button_order(GTK_DIALOG(filesel), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + + gtk_dialog_set_default_response(GTK_DIALOG(filesel), + GTK_RESPONSE_ACCEPT); + + if (suggest_dir) + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filesel), + suggest_dir); + + while (gtk_dialog_run(GTK_DIALOG(filesel)) == GTK_RESPONSE_ACCEPT) { + fname = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(filesel)); + if (!fname) { + g_free(fname); + continue; + } + + gtk_widget_destroy(filesel); + return fname; + } + + gtk_widget_destroy(filesel); + return NULL; +} + +#endif /* ! GDK_WINDOWING_WIN32 */ + +char * prompt_open_file(const char *title, + GtkWindow *parent, + const char *suggest_dir, + const char *desc1, + const char *pattern1, + ...) +{ + char **result, *fname; + va_list ap; + + va_start(ap, pattern1); + result = run_file_chooser(title, parent, FALSE, FALSE, + NULL, suggest_dir, + desc1, pattern1, ap); + va_end(ap); + + if (!result || !result[0] || result[1]) { + g_strfreev(result); + return NULL; + } + else { + fname = result[0]; + g_free(result); + return fname; + } +} + +char ** prompt_open_files(const char *title, + GtkWindow *parent, + const char *suggest_dir, + const char *desc1, + const char *pattern1, + ...) +{ + char **result; + va_list ap; + + va_start(ap, pattern1); + result = run_file_chooser(title, parent, FALSE, TRUE, + NULL, suggest_dir, + desc1, pattern1, ap); + va_end(ap); + return result; +} + +char * prompt_save_file(const char *title, + GtkWindow *parent, + const char *suggest_name, + const char *suggest_dir, + const char *desc1, + const char *pattern1, + ...) +{ + char **result, *fname; + va_list ap; + + va_start(ap, pattern1); + result = run_file_chooser(title, parent, TRUE, FALSE, + suggest_name, suggest_dir, + desc1, pattern1, ap); + va_end(ap); + + if (!result || !result[0] || result[1]) { + g_strfreev(result); + return NULL; + } + else { + fname = result[0]; + g_free(result); + return fname; + } +} + +char * prompt_select_dir(const char *title, GtkWindow *parent, const char *suggest_dir) +{ + char *dirname; + + dirname = run_dir_chooser(title, parent, TRUE, suggest_dir); + + if (!dirname) { + return NULL; + } else { + return dirname; + } +} + + + +/**************** File entry ****************/ + +#ifdef GDK_WINDOWING_WIN32 + +typedef struct _FileEntry { + GtkHBox parent; + GtkWidget *entry; + GtkWidget *button; + char *title; + char *filters; + char *filename; +} FileEntry; + +typedef struct _FileEntryClass { + GtkHBoxClass parent; +} FileEntryClass; + +static guint selection_changed_signal = 0; + +G_DEFINE_TYPE(FileEntry, file_entry, GTK_TYPE_HBOX); + +static void file_entry_finalize(GObject *obj) +{ + FileEntry *fe = (FileEntry*) obj; + g_free(fe->title); + g_free(fe->filters); + g_free(fe->filename); +} + +void file_entry_set_filename(GtkWidget *entry, + const char *filename) +{ + FileEntry *fe = (FileEntry*) entry; + + if (filename && filename[0]) { + if (!fe->filename || strcmp(filename, fe->filename)) { + g_free(fe->filename); + fe->filename = g_strdup(filename); + gtk_entry_set_text(GTK_ENTRY(fe->entry), filename); + g_signal_emit(fe, selection_changed_signal, 0, NULL); + } + } + else if (fe->filename) { + g_free(fe->filename); + fe->filename = NULL; + g_signal_emit(fe, selection_changed_signal, 0, NULL); + } +} + +char * file_entry_get_filename(GtkWidget *entry) +{ + FileEntry *fe = (FileEntry*) entry; + if (fe->filename) + return g_strdup(fe->filename); + else + return NULL; +} + +static void focus_changed(G_GNUC_UNUSED GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + gpointer data) +{ + FileEntry *fe = data; + const char *text; + text = gtk_entry_get_text(GTK_ENTRY(fe->entry)); + file_entry_set_filename(GTK_WIDGET(fe), text); +} + +static void browse_for_files(G_GNUC_UNUSED GtkButton *btn, gpointer data) +{ + FileEntry *fe = data; + GtkWidget *parent; + char **result; + char *bname, *dname; + + parent = gtk_widget_get_toplevel(GTK_WIDGET(fe)); + + if (fe->filename) { + bname = g_path_get_basename(fe->filename); + dname = g_path_get_dirname(fe->filename); + } + else { + bname = dname = NULL; + } + + result = run_file_chooser1(fe->title, GTK_WINDOW(parent), FALSE, FALSE, + bname, dname, fe->filters); + g_free(bname); + g_free(dname); + + if (result && result[0]) + file_entry_set_filename(GTK_WIDGET(fe), result[0]); + + g_strfreev(result); +} + +static void file_entry_init(FileEntry *fe) +{ + gtk_box_set_spacing(GTK_BOX(fe), 6); + + fe->entry = gtk_entry_new(); + fe->button = gtk_button_new_with_label("Browse..."); + gtk_box_pack_start(GTK_BOX(fe), fe->entry, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(fe), fe->button, FALSE, FALSE, 0); + gtk_widget_show(fe->entry); + gtk_widget_show(fe->button); + + g_signal_connect(fe->entry, "notify::is-focus", + G_CALLBACK(focus_changed), fe); + g_signal_connect(fe->button, "clicked", + G_CALLBACK(browse_for_files), fe); +} + +static void file_entry_class_init(FileEntryClass *class) +{ + GObjectClass *obj_class; + + obj_class = G_OBJECT_CLASS(class); + obj_class->finalize = file_entry_finalize; + + selection_changed_signal = + g_signal_new("selection-changed", + G_OBJECT_CLASS_TYPE(obj_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +GtkWidget * file_entry_new(const char *title, + const char *desc1, + const char *pattern1, + ...) +{ + FileEntry *fe = g_object_new(file_entry_get_type(), NULL); + va_list ap; + + fe->title = g_strdup(title); + + va_start(ap, pattern1); + fe->filters = build_filter_string(desc1, pattern1, ap); + va_end(ap); + + return GTK_WIDGET(fe); +} + + +#else /* ! GDK_WINDOWING_WIN32 */ + +GtkWidget * file_entry_new(const char *title, + const char *desc1, + const char *pattern1, + ...) +{ + GtkWidget *btn; + va_list ap; + + btn = gtk_file_chooser_button_new(title, GTK_FILE_CHOOSER_ACTION_OPEN); + + va_start(ap, pattern1); + setup_file_filters(GTK_FILE_CHOOSER(btn), desc1, pattern1, ap); + va_end(ap); + + return btn; +} + +void file_entry_set_filename(GtkWidget *fe, + const char *filename) +{ + gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(fe), filename); +} + +char * file_entry_get_filename(GtkWidget *fe) +{ + return gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fe)); +} + +#endif /* ! GDK_WINDOWING_WIN32 */ diff --git a/tool/tilem-src/gui/filedlg.h b/tool/tilem-src/gui/filedlg.h new file mode 100644 index 0000000..10e672f --- /dev/null +++ b/tool/tilem-src/gui/filedlg.h @@ -0,0 +1,91 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Run a file chooser dialog, allowing user to select a single + existing file to open. + + TITLE is the title of the dialog (UTF-8.) + + PARENT is the "parent" (transient-for) window, if any. + + SUGGEST_DIR is the directory to start in (GLib filename encoding.) + + Remaining arguments are a series of pairs of strings describing the + permitted file types. First string in each pair is the + description; second is a pattern set (consisting of one or more + glob-style patterns, separated by semicolons.) Patterns must be + lowercase; they will be checked case-insensitively. The list is + terminated by NULL. + + A pattern may be the empty string (""); if so, that file type is + disabled. + + Result is NULL if dialog was cancelled; otherwise, a string in + filename encoding, which must be freed with g_free(). + */ +char * prompt_open_file(const char *title, /* UTF-8 */ + GtkWindow *parent, + const char *suggest_dir, /* filename encoding */ + const char *desc1, /* UTF-8 */ + const char *pattern1, /* ASCII */ + ...) + G_GNUC_NULL_TERMINATED; + +/* Run a file chooser dialog, allowing user to select one or more + files to open. Result is either NULL or an array of strings, which + must be freed with g_strfreev(). */ +char ** prompt_open_files(const char *title, /* UTF-8 */ + GtkWindow *parent, + const char *suggest_dir, /* filename encoding */ + const char *desc1, /* UTF-8 */ + const char *pattern1, /* ASCII */ + ...) + G_GNUC_NULL_TERMINATED; + +/* Run a file chooser dialog, allowing user to enter a new filename to + be created. SUGGEST_NAME is a suggested name for the new file; + note that this is UTF-8. */ +char * prompt_save_file(const char *title, /* UTF-8 */ + GtkWindow *parent, + const char *suggest_name, /* UTF-8 (!) */ + const char *suggest_dir, /* filename encoding */ + const char *desc1, /* UTF-8 */ + const char *pattern1, /* ASCII */ + ...) + G_GNUC_NULL_TERMINATED; + +/* Create a file entry or file-chooser button widget, allowing user to + select a single existing file to open. */ +GtkWidget * file_entry_new(const char *title, /* UTF-8 */ + const char *desc1, /* UTF-8 */ + const char *pattern1, /* ASCII */ + ...) + G_GNUC_NULL_TERMINATED; + +/* Set filename in a file entry. */ +void file_entry_set_filename(GtkWidget *fe, + const char *filename); /* filename encoding */ + +/* Get filename in a file entry. Result is NULL if no file is + selected; otherwise, a string in filename encoding, which must be + freed with g_free(). */ +char * file_entry_get_filename(GtkWidget *fe); + +/* Run a directory chooser dialog, allowing user to select a directory. */ +char * prompt_select_dir(const char *title, GtkWindow *parent, const char *suggest_dir); diff --git a/tool/tilem-src/gui/files.c b/tool/tilem-src/gui/files.c new file mode 100644 index 0000000..ec06d72 --- /dev/null +++ b/tool/tilem-src/gui/files.c @@ -0,0 +1,270 @@ +/* + * TilEm II + * + * Copyright (c) 2011-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#ifdef G_OS_WIN32 +# include +#endif + +#include "files.h" + +static char *program_dir; + +/* Set the name used to invoke this program */ +void set_program_path(const char *path) +{ + if (path && strchr(path, G_DIR_SEPARATOR)) + program_dir = g_path_get_dirname(path); +} + +/* Build a filename out of varargs */ +static char *build_filenamev(const char *start, va_list rest) +{ + char *args[10]; + int i; + + args[0] = (char*) start; + for (i = 1; i < 10; i++) { + args[i] = (char*) va_arg(rest, const char *); + if (!args[i]) + break; + } + g_assert(i < 10); + + return g_build_filenamev(args); +} + +#ifdef G_OS_WIN32 +static char * get_special_folder(int csidl) +{ + char lpath[MAX_PATH+1]; + wchar_t wpath[MAX_PATH+1]; + LPITEMIDLIST pidl = NULL; + gchar *s = NULL; + + if (SHGetSpecialFolderLocation(NULL, csidl, &pidl)) + return NULL; + + if (G_WIN32_HAVE_WIDECHAR_API()) { + if (SHGetPathFromIDListW(pidl, wpath)) + s = g_utf16_to_utf8(wpath, -1, NULL, NULL, NULL); + } + else { + if (SHGetPathFromIDListA(pidl, lpath)) + s = g_locale_to_utf8(lpath, -1, NULL, NULL, NULL); + } + + CoTaskMemFree(pidl); + return s; +} +#endif + +/* Get the default configuration directory. + + On Unix, this is $XDG_CONFIG_HOME/tilem2 (where $XDG_CONFIG_HOME + defaults to $HOME/.config/ if not set.) + + On Windows, this is $CSIDL_LOCAL_APPDATA\tilem2 (where + $CSIDL_LOCAL_APPDATA is typically "Local Settings\Application Data" + in the user's profile.) + + Result is cached and should not be freed. */ +static char * get_default_config_dir() +{ + static char *result; + + if (!result) { +#ifdef G_OS_WIN32 + /* Do not use g_get_user_config_dir() on Windows, + because the behavior of that function is not + consistent across versions of GLib. */ + char *s = get_special_folder(CSIDL_LOCAL_APPDATA); + if (s) + result = g_build_filename(s, "tilem2", NULL); + g_free(s); +#else + result = g_build_filename(g_get_user_config_dir(), + "tilem2", NULL); +#endif + } + + return result; +} + +/* Search for an existing file. + + The default package configuration directory (defined above) is + searched first; if the file is not found there, try to find the + file that was installed along with the package, or (in case the + package hasn't yet been installed) the copy included in the source + package. */ +static char * find_filev(GFileTest test, const char *name, va_list rest) +{ + char *fullname, *dname, *path; + const char *userdir; + const char * const *sysdirs; + + fullname = build_filenamev(name, rest); + + dname = get_default_config_dir(); + path = g_build_filename(dname, fullname, NULL); + if (g_file_test(path, test)) { + g_free(fullname); + return path; + } + g_free(path); + +#ifdef G_OS_WIN32 + if ((dname = g_win32_get_package_installation_directory(NULL, NULL))) { + path = g_build_filename(dname, "share", "tilem2", fullname, NULL); + g_free(dname); + if (g_file_test(path, test)) { + g_free(fullname); + return path; + } + g_free(path); + } +#endif + +#ifdef UNINSTALLED_SHARE_DIR + if (program_dir) { + path = g_build_filename(program_dir, UNINSTALLED_SHARE_DIR, + fullname, NULL); + if (g_file_test(path, test)) { + g_free(fullname); + return path; + } + g_free(path); + } +#endif + +#ifdef SHARE_DIR + path = g_build_filename(SHARE_DIR, fullname, NULL); + if (g_file_test(path, test)) { + g_free(fullname); + return path; + } + g_free(path); +#endif + + userdir = g_get_user_data_dir(); + if (userdir) { + path = g_build_filename(userdir, "tilem2", fullname, NULL); + if (g_file_test(path, test)) { + g_free(fullname); + return path; + } + } + + sysdirs = g_get_system_data_dirs(); + while (sysdirs && sysdirs[0]) { + path = g_build_filename(sysdirs[0], "tilem2", fullname, NULL); + if (g_file_test(path, test)) { + g_free(fullname); + return path; + } + sysdirs++; + } + + g_free(fullname); + return NULL; +} + +/* Locate an existing configuration or data file */ +char * get_shared_file_path(const char *name, ...) +{ + va_list ap; + char *path; + va_start(ap, name); + path = find_filev(G_FILE_TEST_IS_REGULAR, name, ap); + va_end(ap); + return path; +} + +/* Locate an existing configuration or data directory */ +char * get_shared_dir_path(const char *name, ...) +{ + va_list ap; + char *path; + va_start(ap, name); + path = find_filev(G_FILE_TEST_IS_DIR, name, ap); + va_end(ap); + return path; +} + +/* Return the path to the user's configuration directory, where any + new or modified config files should be written. Result is cached + and should not be freed. */ +static char * get_config_dir() +{ + static char *result; + char *fname; + FILE *f; + + if (result) + return result; + + /* If config.ini already exists, in any of the standard + locations, and is writable, use the directory containing + it. This will allow building the package as a relocatable + bundle. */ + fname = get_shared_file_path("config.ini", NULL); + if (fname) { + f = g_fopen(fname, "r+"); + if (f) { + result = g_path_get_dirname(fname); + fclose(f); + } + g_free(fname); + } + + /* Otherwise use default config directory */ + if (!result) + result = g_strdup(get_default_config_dir()); + + return result; +} + +/* Get path for writing a new or modified configuration file */ +char * get_config_file_path(const char *name, ...) +{ + va_list ap; + const char *cfgdir; + char *fullname, *path; + + cfgdir = get_config_dir(); + g_mkdir_with_parents(cfgdir, 0775); + + va_start(ap, name); + fullname = build_filenamev(name, ap); + va_end(ap); + path = g_build_filename(cfgdir, fullname, NULL); + g_free(fullname); + return path; +} + diff --git a/tool/tilem-src/gui/files.h b/tool/tilem-src/gui/files.h new file mode 100644 index 0000000..9772d36 --- /dev/null +++ b/tool/tilem-src/gui/files.h @@ -0,0 +1,42 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Set the name used to invoke this program. Data files can be + located relative to this path if the package is not installed */ +void set_program_path(const char *path); + +/* Locate an existing configuration or data file. Arguments will be + concatenated, separated by / or \, as with g_build_filename(). + NULL is returned if the file isn't found. Free result with + g_free(). */ +char * get_shared_file_path(const char *name, ...) + G_GNUC_NULL_TERMINATED; + +/* Locate an existing configuration or data directory. NULL is + returned if the file isn't found. Free result with g_free(). */ +char * get_shared_dir_path(const char *name, ...) + G_GNUC_NULL_TERMINATED; + +/* Get the full path where a configuration file should be written; + attempt to create the directory if it doesn't exist. This function + will always return a valid filename (although it may not actually + be writable.) Free result with g_free(). */ +char * get_config_file_path(const char *name, ...) + G_GNUC_NULL_TERMINATED; + diff --git a/tool/tilem-src/gui/fixedtreeview.c b/tool/tilem-src/gui/fixedtreeview.c new file mode 100644 index 0000000..74c384c --- /dev/null +++ b/tool/tilem-src/gui/fixedtreeview.c @@ -0,0 +1,165 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "fixedtreeview.h" + +/* Style set on tree view; update column sizes */ +static void ftv_style_set(GtkWidget *treeview, + G_GNUC_UNUSED GtkStyle *oldstyle, + G_GNUC_UNUSED gpointer data) +{ + GtkTreeModel *template; + GtkTreeIter iter; + GList *cols, *cp; + GtkTreeViewColumn *col; + int width; + + template = g_object_get_data(G_OBJECT(treeview), "ftv-template"); + if (!template) + return; + + if (!gtk_tree_model_get_iter_first(template, &iter)) + return; + + cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(treeview)); + for (cp = cols; cp; cp = cp->next) { + col = cp->data; + gtk_tree_view_column_cell_set_cell_data(col, template, &iter, + FALSE, FALSE); + gtk_tree_view_column_cell_get_size(col, NULL, NULL, NULL, + &width, NULL); + gtk_tree_view_column_set_fixed_width(col, width + 2); + } + g_list_free(cols); +} + +/* Widget destroyed */ +static void ftv_destroy(GtkWidget *treeview, G_GNUC_UNUSED gpointer data) +{ + GtkTreeModel *template; + + template = g_object_get_data(G_OBJECT(treeview), "ftv-template"); + if (template) + g_object_unref(template); + g_object_set_data(G_OBJECT(treeview), "ftv-template", NULL); +} + +void fixed_tree_view_init_with_template(GtkWidget *treeview, + GtkTreeModel *template) +{ + GtkTreeModel *oldtemplate; + + if (template) + g_object_ref_sink(template); + + oldtemplate = g_object_get_data(G_OBJECT(treeview), "ftv-template"); + if (oldtemplate) { + g_object_unref(oldtemplate); + } + else { + g_signal_connect(treeview, "style-set", + G_CALLBACK(ftv_style_set), NULL); + g_signal_connect(treeview, "destroy", + G_CALLBACK(ftv_destroy), NULL); + } + g_object_set_data(G_OBJECT(treeview), "ftv-template", template); + + if (template && GTK_WIDGET_REALIZED(treeview)) + ftv_style_set(treeview, NULL, NULL); +} + +void fixed_tree_view_init(GtkWidget *treeview, int colgroupsize, ...) +{ + GtkTreeModel *real_model; + int ncols, i, col; + GType *types; + GtkListStore *store; + GtkTreeIter iter; + GValue value; + gchar *error = NULL; + va_list ap; + + g_return_if_fail(GTK_IS_TREE_VIEW(treeview)); + g_return_if_fail(colgroupsize >= 0); + + real_model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); + g_return_if_fail(real_model != NULL); + + ncols = gtk_tree_model_get_n_columns(real_model); + g_return_if_fail(ncols > 0); + + if (colgroupsize == 0) + colgroupsize = ncols; + + g_return_if_fail(ncols % colgroupsize == 0); + + types = g_new(GType, ncols); + for (i = 0; i < ncols; i++) { + types[i] = gtk_tree_model_get_column_type(real_model, i); + if (i > colgroupsize) + g_return_if_fail(types[i] == types[i - colgroupsize]); + } + store = gtk_list_store_newv(ncols, types); + + va_start(ap, colgroupsize); + gtk_list_store_append(store, &iter); + + memset(&value, 0, sizeof(value)); + + col = va_arg(ap, int); + while (col != -1) { + if (col < 0 || col >= colgroupsize) { + g_critical("missing sentinel"); + break; + } + + g_value_init(&value, types[col]); + + G_VALUE_COLLECT(&value, ap, 0, &error); + + if (error) { + g_critical("%s", error); + g_free(error); + break; + } + + for (i = col; i < ncols; i += colgroupsize) + gtk_list_store_set_value(store, &iter, i, &value); + + g_value_unset(&value); + + col = va_arg(ap, int); + } + + va_end(ap); + + g_free(types); + + fixed_tree_view_init_with_template(treeview, GTK_TREE_MODEL(store)); +} diff --git a/tool/tilem-src/gui/fixedtreeview.h b/tool/tilem-src/gui/fixedtreeview.h new file mode 100644 index 0000000..f2292bf --- /dev/null +++ b/tool/tilem-src/gui/fixedtreeview.h @@ -0,0 +1,40 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Set up a tree view with fixed-size columns, where the column sizes + are determined automatically based on a template. The template + model must contain the same number and types of data columns as the + tree view's data model. The first row of the template model will + be used to compute column sizes; any additional rows are + ignored. */ +void fixed_tree_view_init_with_template(GtkWidget *treeview, + GtkTreeModel *template); + +/* As above, but the template model is constructed automatically. The + tree view must have a data model attached already. + + Arguments following COLGROUPSIZE are a sequence of (column, data) + pairs, as you would pass to gtk_list_store_set(). The list must be + terminated with -1. + + If COLGROUPSIZE is a positive integer N, then the template will be + constructed by repeating the first N columns as many times as + necessary. In this case, columns K and K+N must always have the + same type. */ +void fixed_tree_view_init(GtkWidget *treeview, int colgroupsize, ...); diff --git a/tool/tilem-src/gui/gifencod.c b/tool/tilem-src/gui/gifencod.c new file mode 100644 index 0000000..e622c8a --- /dev/null +++ b/tool/tilem-src/gui/gifencod.c @@ -0,0 +1,326 @@ +/* Fast GIF encoder + * taken from http://www.msg.net/utility/whirlgif/gifencod.html - go there for the algorithm explanation + * + * gifencode.c + * + * Copyright (c) 1997,1998,1999 by Hans Dinsen-Hansen + * The algorithms are inspired by those of gifcode.c + * Copyright (c) 1995,1996 Michael A. Mayer + * All rights reserved. + * + * This software may be freely copied, modified and redistributed + * without fee provided that above copyright notices are preserved + * intact on all copies and modified copies. + * + * There is no warranty or other guarantee of fitness of this software. + * It is provided solely "as is". The author(s) disclaim(s) all + * responsibility and liability with respect to this software's usage + * or its effect upon hardware or computer systems. + * + * The Graphics Interchange format (c) is the Copyright property of + * Compuserve Incorporated. Gif(sm) is a Service Mark property of + * Compuserve Incorporated. + * + * + * Implements GIF encoding by means of a tree search. + * -------------------------------------------------- + * + * - The string table may be thought of being stored in a "b-tree of + * steroids," or more specifically, a {256,128,...,4}-tree, depending on + * the size of the color map. + * - Each (non-NULL) node contains the string table index (or code) and + * {256,128,...,4} pointers to other nodes. + * - For example, the index associated with the string 0-3-173-25 would be + * stored in: + * first->node[0]->node[3]->node[173]->node[25]->code + * + * - Speed and effectivity considerations, however, have made this + * implementation somewhat obscure, because it is costly to initialize + * a node-array where most elements will never be used. + * - Initially, a new node will be marked as terminating, TERMIN. + * If this node is used at a later stage, its mark will be changed. + * - Only nodes with several used nodes will be associated with a + * node-array. Such nodes are marked LOOKUP. + * - The remaining nodes are marked SEARCH. They are linked together + * in a search-list, where a field, NODE->alt, points at an alternative + * following color. + * - It is hardly feasible exactly to predict which nodes will have most + * used node pointers. The theory here is that the very first node as + * well as the first couple of nodes which need at least one alternative + * color, will be among the ones with many nodes ("... whatever that + * means", as my tutor in Num. Analysis and programming used to say). + * - The number of possible LOOKUP nodes depends on the size of the color + * map. Large color maps will have many SEARCH nodes; small color maps + * will probably have many LOOKUP nodes. +*/ + +#include +#include +#include +#ifdef MEMDBG +#include +#endif + +#define BLOKLEN 255 +#define BUFLEN 1000 +#define TERMIN 'T' +#define LOOKUP 'L' +#define SEARCH 'S' +#define noOfArrays 20 +/* defines the amount of memory set aside in the encoding for the + * LOOKUP type nodes; for a 256 color GIF, the number of LOOKUP + * nodes will be <= noOfArrays, for a 128 color GIF the number of + * LOOKUP nodes will be <= 2 * noOfArrays, etc. */ + +typedef struct GifTree { + char typ; /* terminating, lookup, or search */ + int code; /* the code to be output */ + unsigned char ix; /* the color map index */ + struct GifTree **node, *nxt, *alt; +} GifTree; + +char *AddCodeToBuffer(int, short, char *); +void ClearTree(int, GifTree *); + +extern unsigned int debugFlag; +extern int count; + +int chainlen = 0, maxchainlen = 0, nodecount = 0, lookuptypes = 0, nbits; +short need = 8; +GifTree *empty[256], GifRoot = {LOOKUP, 0, 0, empty, NULL, NULL}, + *topNode, *baseNode, **nodeArray, **lastArray; + + +void GifEncode(FILE *fout, unsigned char *pixels, int depth, int siz) + +{ + GifTree *first = &GifRoot, *newNode, *curNode; + unsigned char *end; + int cc, eoi, next, tel=0; //, dbw=0; + short cLength; + + char *pos, *buffer; + + empty[0] = NULL; + need = 8; + nodeArray = empty; + memmove(++nodeArray, empty, 255*sizeof(GifTree **)); + if (( buffer = (char *)malloc((BUFLEN+1)*sizeof(char))) == NULL ) + printf("No memory for writing"); + buffer++; + + pos = buffer; + buffer[0] = 0x0; + + cc = (depth == 1) ? 0x4 : 1<node = (GifTree **)malloc(256*sizeof(GifTree *)*noOfArrays)) == NULL ) + printf("No memory for search nodes"); + lastArray = nodeArray + ( 256*noOfArrays - cc); + ClearTree(cc, first); + + pos = AddCodeToBuffer(cc, cLength, pos); + + end = pixels+siz; + curNode = first; + while(pixels < end) { + + if ( curNode->node[*pixels] != NULL ) { + curNode = curNode->node[*pixels]; + tel++; + pixels++; + chainlen++; + continue; + } else if ( curNode->typ == SEARCH ) { + newNode = curNode->nxt; + while ( newNode->alt != NULL ) { + if ( newNode->ix == *pixels ) break; + newNode = newNode->alt; + } + if (newNode->ix == *pixels ) { + tel++; + pixels++; + chainlen++; + curNode = newNode; + continue; + } + } + +/* ****************************************************** + * If there is no more thread to follow, we create a new node. If the + * current node is terminating, it will become a SEARCH node. If it is + * a SEARCH node, and if we still have room, it will be converted to a + * LOOKUP node. +*/ + newNode = ++topNode; + switch (curNode->typ ) { + case LOOKUP: + newNode->nxt = NULL; + newNode->alt = NULL, + curNode->node[*pixels] = newNode; + break; + case SEARCH: + if ( nodeArray != lastArray ) { + nodeArray += cc; + curNode->node = nodeArray; + curNode->typ = LOOKUP; + curNode->node[*pixels] = newNode; + curNode->node[(curNode->nxt)->ix] = curNode->nxt; + lookuptypes++; + newNode->nxt = NULL; + newNode->alt = NULL, + curNode->nxt = NULL; + break; + } +/* otherwise do as we do with a TERMIN node */ + case TERMIN: + newNode->alt = curNode->nxt; + newNode->nxt = NULL, + curNode->nxt = newNode; + curNode->typ = SEARCH; + break; + default: + fprintf(stderr, "Silly node type: %d\n", curNode->typ); + } + newNode->code = next; + newNode->ix = *pixels; + newNode->typ = TERMIN; + newNode->node = empty; + nodecount++; +/* +* End of node creation +* ****************************************************** +*/ +#ifdef _WHGDBG + if (debugFlag) { + if (curNode == newNode) fprintf(stderr, "Wrong choice of node\n"); + if ( curNode->typ == LOOKUP && curNode->node[*pixels] != newNode ) fprintf(stderr, "Wrong pixel coding\n"); + if ( curNode->typ == TERMIN ) fprintf(stderr, "Wrong Type coding; frame no = %d; pixel# = %d; nodecount = %d\n", count, tel, nodecount); + } +#endif + pos = AddCodeToBuffer(curNode->code, cLength, pos); + if ( chainlen > maxchainlen ) maxchainlen = chainlen; + chainlen = 0; + if(pos-buffer>BLOKLEN) { + buffer[-1] = BLOKLEN; + fwrite(buffer-1, 1, BLOKLEN+1, fout); + buffer[0] = buffer[BLOKLEN]; + buffer[1] = buffer[BLOKLEN+1]; + buffer[2] = buffer[BLOKLEN+2]; + buffer[3] = buffer[BLOKLEN+3]; + pos -= BLOKLEN; + } + curNode = first; + + if(next == (1<BLOKLEN) { + buffer[-1] = BLOKLEN; + fwrite(buffer-1, 1, BLOKLEN+1, fout); + buffer[0] = buffer[BLOKLEN]; + buffer[1] = buffer[BLOKLEN+1]; + buffer[2] = buffer[BLOKLEN+2]; + buffer[3] = buffer[BLOKLEN+3]; + pos -= BLOKLEN; + } + next = cc+2; + cLength = (short) ((depth == 1)?3:depth+1); + } + } + + pos = AddCodeToBuffer(curNode->code, cLength, pos); + if(pos-buffer>BLOKLEN-3) { + buffer[-1] = BLOKLEN-3; + fwrite(buffer-1, 1, BLOKLEN-2, fout); + buffer[0] = buffer[BLOKLEN-3]; + buffer[1] = buffer[BLOKLEN-2]; + buffer[2] = buffer[BLOKLEN-1]; + buffer[3] = buffer[BLOKLEN]; + buffer[4] = buffer[BLOKLEN+1]; + pos -= BLOKLEN-3; + } + pos = AddCodeToBuffer(eoi, cLength, pos); + pos = AddCodeToBuffer(0x0, -1, pos); + buffer[-1] = (char) (pos-buffer); + + fwrite(buffer-1, pos-buffer+1, 1, fout); + free(buffer-1); free(first->node); free(baseNode); + buffer=NULL;first->node=NULL;baseNode=NULL; +#ifdef _WHGDBG + if (debugFlag) fprintf(stderr, "pixel count = %d; nodeCount = %d lookup nodes = %d\n", tel, nodecount, lookuptypes); +#endif + return; + +} + +void ClearTree(int cc, GifTree *root) +{ + int i; + GifTree *newNode, **xx; + +#ifdef _WHGDBG + if (debugFlag>1) fprintf(stderr, "Clear Tree cc= %d\n", cc); + if (debugFlag>1) fprintf(stderr, "nodeCount = %d lookup nodes = %d\n", nodecount, lookuptypes); +#endif + maxchainlen=0; lookuptypes = 1; + nodecount = 0; + nodeArray = root->node; + xx= nodeArray; + for (i = 0; i < noOfArrays; i++ ) { + memmove (xx, empty, 256*sizeof(GifTree **)); + xx += 256; + } + topNode = baseNode; + for(i=0; inode[i] = newNode = ++topNode; + newNode->nxt = NULL; + newNode->alt = NULL; + newNode->code = i; + newNode->ix = (unsigned char) i; + newNode->typ = TERMIN; + newNode->node = empty; + nodecount++; + } +} + +char *AddCodeToBuffer(int code, short n, char *buf) +{ + int mask; + + if(n<0) { + if(need<8) { + buf++; + *buf = 0x0; + } + need = 8; + return buf; + } + + while(n>=need) { + mask = (1<>need; + n -= need; + need = 8; + } + if(n) { + mask = (1< +#include + +#ifdef _USE_STRINGS_H +#include +#else +#include +#endif + +#ifdef _FOPEN_TXT_OR_BIN +#define WRIBIN "wb" +#define REATXT "rt" +#define REABIN "rb" +#else +/* Usually there is no need to distinguish between binary and txt */ +#define WRIBIN "w" +#define REATXT "r" +#define REABIN "r" +#endif + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* define constants and defaults */ + /* Default amount of inter-frame time */ +#define DEFAULT_TIME 10 + /* If set to 1, Netscape 'loop' code will be added by default */ +#define DEFAULT_LOOP 0 + /* If set to 1, use the colormaps from all images, not just the first */ +#define DEFAULT_USE_COLORMAP 0 + + /* Used in calculating the transparent color */ +#define TRANS_NONE 1 +#define TRANS_RGB 2 +#define TRANS_MAP 3 + +#define DISP_NONE 0 +#define DISP_NOT 1 +#define DISP_BACK 2 +#define DISP_PREV 3 +#define DEFAULT_DISPOSAL DISP_NONE + /* set default disposal method here to any of the DISP_XXXX values */ + +#define BIGSTRING 256 +#define MAXVAL 4100 /* maxval of lzw coding size */ +#define MAXVALP 4200 +#define TERMIN 'T' +#define LOOKUP 'L' +#define SEARCH 'S' +#define noOfArrays 20 +/* defines the amount of memory set aside in the encoding for the + * LOOKUP type nodes; for a 256 color GIF, the number of LOOKUP + * nodes will be <= noOfArrays, for a 128 color GIF the number of + * LOOKUP nodes will be <= 2 * noOfArrays, etc. */ + +/* define shorthand for various types */ +#define LONG int +#define ULONG unsigned int +#define BYTE char +#define UBYTE unsigned char +#define SHORT short +#define USHORT unsigned short +#define WORD short int +#define UWORD unsigned short int + +int chainlen = 0, maxchainlen = 0, nodecount = 0, lookuptypes = 0, nbits; + +short need = 8; + + + + +unsigned int debugFlag, verbose; +int count; + +/* definition of various structures */ +typedef struct Transparency { + int type; + UBYTE valid; + UBYTE map; + UBYTE red; + UBYTE green; + UBYTE blue; + } Transparency; + +typedef struct Global { + Transparency trans; + int left; + int top; + unsigned int time; + unsigned short disposal; + } Global; + +typedef struct GifScreenHdr { + int width; + int height; + UBYTE m; + UBYTE cres; + UBYTE pixbits; + UBYTE bc; + UBYTE aspect; + } GifScreenHdr; + +typedef union GifColor { + struct cmap { + UBYTE red; + UBYTE green; + UBYTE blue; + UBYTE pad; + } cmap; + ULONG pixel; + } GifColor; + +typedef struct GifImageHdr { + int left; + int top; + int width; + int height; + UBYTE m; + UBYTE i; + UBYTE pixbits; + UBYTE reserved; + } GifImageHdr; + +typedef struct GifTable { + UBYTE valid; + UBYTE data; + UBYTE first; + UBYTE res; + int last; + } GifTable; + +typedef struct GifTree { + char typ; /* terminating, lookup, or search */ + int code; /* the code to be output */ + UBYTE ix; /* the color map index */ + struct GifTree **node, *nxt, *alt; +} GifTree; + +GifTree *empty[256], GifRoot = {LOOKUP, 0, 0, empty, NULL, NULL}, +*topNode, *baseNode, **nodeArray, **lastArray; + +/* define inline functions */ +#define GifPutShort(i, fout) {fputc(i&0xff, fout); fputc(i>>8, fout);} +#define GifGetShort(fin) (Xgetc(fin) | Xgetc(fin)<<8) + +/* forward declaration of the functions */ +void CalcTrans(); +void GifAddToTable(); +void GifClearTable(); +void GifComment(); +void GifDecode(); +void GifEncode(); +ULONG GifGetCode(); +void GifGetNextEntry(); +void GifLoop(); +void GifReadFile(); +void GifScreenHeader(); +UBYTE *GifSendData(); +void ReadImageHeader(); +void SetOffset(); +void TheEnd(); +void TheEnd1(); +void Usage(); +void WriteImageHeader(); +UBYTE Xgetc(); +void ClearTree(int cc, GifTree *root); +char *AddCodeToBuffer(int code, short n, char *buf); diff --git a/tool/tilem-src/gui/gtk-compat.h b/tool/tilem-src/gui/gtk-compat.h new file mode 100644 index 0000000..f817781 --- /dev/null +++ b/tool/tilem-src/gui/gtk-compat.h @@ -0,0 +1,11 @@ +#if !GTK_CHECK_VERSION(2, 14, 0) +# define gtk_dialog_get_content_area(d) ((d)->vbox) +# define gtk_widget_get_window(w) ((w)->window) +# define gtk_selection_data_get_data(s) ((s)->data) +#endif +#if !GTK_CHECK_VERSION(2, 18, 0) +# define gtk_widget_get_allocation(w, a) (*(a) = (w)->allocation) +# define gtk_widget_get_visible(w) GTK_WIDGET_VISIBLE(w) +# define gtk_widget_set_can_default(w, v) g_object_set((w), "can-default", v, NULL) +# define gtk_widget_set_can_focus(w, v) g_object_set((w), "can-focus", v, NULL) +#endif diff --git a/tool/tilem-src/gui/gui.h b/tool/tilem-src/gui/gui.h new file mode 100644 index 0000000..0e48c2d --- /dev/null +++ b/tool/tilem-src/gui/gui.h @@ -0,0 +1,386 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "animation.h" +#include "emulator.h" +#include "skinops.h" +#include "emuwin.h" +#include "debugger.h" + +#include "gtk-compat.h" + +/* This struture is a wrapper for VarEntry with additionnal informations used by tilem */ +typedef struct { + int model; + + VarEntry *ve; /* Original variable info retrieved + from calculator */ + int slot; /* Slot number */ + + /* Strings for display (UTF-8) */ + char *name_str; /* Variable name */ + char *type_str; /* Variable type */ + char *slot_str; /* Program slot */ + char *file_ext; /* Default file extension */ + char *filetype_desc; /* File format description */ + + int size; /* Variable size */ + gboolean archived; /* Is archived */ + gboolean can_group; /* Can be stored in group file */ + +} TilemVarEntry; + +/* Screenshot view (widgets and flags) */ +typedef struct _TilemScreenshotDialog { + TilemCalcEmulator *emu; + + GtkWidget* window; /* The window itself */ + + /* Buttons */ + GtkWidget* screenshot; /* Grab button */ + GtkWidget* record; /* Record button */ + GtkWidget* stop; /* Stop button */ + + /* Screenshot menu */ + GtkWidget* screenshot_preview_image; /* Review pixbuf */ + GtkWidget* ss_ext_combo; /* Combo box for file format */ + GtkWidget* ss_size_combo; /* Combo box for size */ + GtkWidget* width_spin; /* The width of the gif */ + GtkWidget* height_spin; /* The height of the gif */ + GtkWidget* grayscale_tb; /* Toggle Button for enabling/disabling grayscale */ + GtkWidget* animation_speed; /* A scale for the speed of the animation */ + GtkWidget* background_color; /* Color chooser : Color used for pixel-off */ + GtkWidget* foreground_color; /* Color chooser : Color used for pixel-on */ + + TilemAnimation *current_anim; + gboolean current_anim_grayscale; +} TilemScreenshotDialog; + +/* This struture is used by receive menu */ +typedef struct _TilemReceiveDialog { + TilemCalcEmulator *emu; + + GSList *vars; /* The list of vars */ + + GtkWidget* window; /* The window itself */ + + GtkWidget* treeview; /* The treeview to print the list of vars */ + GtkTreeModel* model; /* The model used by the treeview */ + + /* Radio buttons */ + GtkWidget* mode_box; + GtkWidget* multiple_rb; /* Write multiple files */ + GtkWidget* group_rb; /* Write a single group file */ + + gboolean refresh_pending; +} TilemReceiveDialog; + +/* Handle the ilp progress stuff */ +typedef struct _TilemLinkProgress { + TilemCalcEmulator *emu; + + GtkProgressBar* progress_bar; /* progress bar (total) */ + GtkLabel* title_lbl; + GtkLabel* status_lbl; + GtkWidget* window; +} TilemLinkProgress; + +#define LABEL_X_ALIGN 0.0 + + +/* ###### event.c ##### */ + +/* Dialog mesg */ +void show_about(); + +/* Launch the debugger */ +void launch_debugger(TilemEmulatorWindow *ewin); + +/* Button-press event */ +gboolean mouse_press_event(GtkWidget* w, GdkEventButton *event, gpointer data); + +/* Pointer-motion event */ +gboolean pointer_motion_event(GtkWidget* w, GdkEventMotion *event, gpointer data); + +/* Button-release event */ +gboolean mouse_release_event(GtkWidget* w, GdkEventButton *event, gpointer data); + +/* Key-press event */ +gboolean key_press_event(GtkWidget* w, GdkEventKey *event, gpointer data); + +/* Key-release event */ +gboolean key_release_event(GtkWidget* w, GdkEventKey *event, gpointer data); + +/* Pop up menu on main window */ +gboolean popup_menu_event(GtkWidget* w, gpointer data); + +/* Handle drag and drop */ +void drag_data_received(GtkWidget *win, GdkDragContext *dc, gint x, gint y, + GtkSelectionData *seldata, guint info, guint t, + gpointer data); + + +/* ###### emuwin.c ##### */ + +/* Display the lcd image into the terminal */ +void display_lcdimage_into_terminal(TilemEmulatorWindow *ewin); + +/* Redraw the screen with or without skin */ +void redraw_screen(TilemEmulatorWindow *ewin); + + +/* ##### preferences.c ##### */ + +/* Run preferences dialog. */ +void tilem_preferences_dialog(TilemEmulatorWindow *ewin); + + +/* ##### address.c ##### */ + +/* Convert address to a displayable string. */ +char * tilem_format_addr(TilemDebugger *dbg, dword addr, gboolean physical); + +/* Parse physical address expressed as page and offset. */ +gboolean tilem_parse_paged_addr(TilemDebugger *dbg, const char *pagestr, + const char *offsstr, dword *value); + +/* Parse an address or hex constant. If PHYSICAL is null, only a + logical address (simple hex value or symbol) is allowed. If + PHYSICAL is non-null, physical addresses in the form "PAGE:OFFSET" + are also allowed. *PHYSICAL will be set to true if the user + entered a physical address. */ +gboolean tilem_parse_addr(TilemDebugger *dbg, const char *string, + dword *value, gboolean *physical); + +/* Open a dialog box prompting the user to enter an address. PARENT + is the transient-for window; TITLE is the dialog's title; PROMPT is + a label for the input. */ +gboolean tilem_prompt_address(TilemDebugger *dbg, GtkWindow *parent, + const char *title, const char *prompt, + dword *value, gboolean physical, + gboolean usedefault); + + +/* ##### tool.c ##### */ + +/* Get model name (abbreviation) for a TilEm model ID. */ +const char * model_to_name(int model); + +/* Convert model name to a model ID. */ +int name_to_model(const char *name); + +/* Convert TilEm model ID to tifiles2 model ID. */ +CalcModel model_to_calcmodel(int model); + +/* Convert tifiles2 model ID to TilEm model ID. */ +int calcmodel_to_model(CalcModel model); + +/* Get model ID for a given file. */ +int file_to_model(const char *name); + +/* Get "base" model for file type support. */ +int model_to_base_model(int calc_model); + +/* Check if calc is compatible with given file type. */ +gboolean model_supports_file(int calc_model, int file_model); + +/* Create a frame around the given widget */ +GtkWidget* new_frame(const gchar* label, GtkWidget* contents); + +/* The popup to choose what kind of rom you are trying to load (at startup)*/ +char choose_rom_popup(GtkWidget *parent_window, const char *filename, char default_model); + +/* Convert UTF-8 to filename encoding. Use ASCII digits in place of + subscripts if necessary. If conversion fails utterly, fall back to + the UTF-8 name, which is broken but better than nothing. */ +char * utf8_to_filename(const char *utf8str); + +/* Convert UTF-8 to a subset of UTF-8 that is compatible with the + locale */ +char * utf8_to_restricted_utf8(const char *utf8str); + +/* Generate default filename (UTF-8) for a variable */ +char * get_default_filename(const TilemVarEntry *tve); + + +/* ##### config.c ##### */ + +/* Retrieve settings from configuration file. GROUP is the + configuration group; following arguments are a series of OPTION + strings, each followed by a pointer to a variable that will receive + the value. The list of options is terminated by NULL. + + Each OPTION is a string of the form "KEY/TYPE" or "KEY/TYPE=VALUE", + where KEY is the name of the configuration property, and TYPE is + either 'f' for a filename (char*), 's' for a UTF-8 string (char*), + 'i' for an integer (int), 'r' for a real number (double), or 'b' + for a boolean (int). + + VALUE, if specified, is the default value for the option if it has + not been defined by the user. If no VALUE is specified, the option + defaults to zero or NULL. + + Strings returned by this function must be freed by the caller + (using g_free().) */ +void tilem_config_get(const char *group, const char *option, ...) + G_GNUC_NULL_TERMINATED; + +/* Save settings to the configuration file. Arguments are a series of + option names, as above, each followed by the new value of the + option. The list is terminated by NULL. */ +void tilem_config_set(const char *group, const char *option, ...) + G_GNUC_NULL_TERMINATED; + + +/* ##### link.c ##### */ + +/* This structure is used to send a file (usually slot=-1, first=TRUE, last=TRUE)*/ +struct TilemSendFileInfo { + char *filename; + char *display_name; + int slot; + int first; + int last; + char *error_message; +}; + +/* This structure is used to receive a file */ +struct TilemReceiveFileInfo { + GSList *entries; + char* destination; + char *error_message; + gboolean output_tig; +}; + +/* Copy a TilemVarEntry structure */ +TilemVarEntry *tilem_var_entry_copy(const TilemVarEntry *tve); + +/* Free a previous allocated TilemVarEntry */ +void tilem_var_entry_free(TilemVarEntry *tve); + +/* Send a file to the calculator through the GUI. SLOT is the + destination program slot (for TI-81.) FIRST must be true if this + is the first variable in a series; LAST must be true if this is the + last in a series. */ +void tilem_link_send_file(TilemCalcEmulator *emu, const char *filename, + int slot, gboolean first, gboolean last); + +/* The effective send file function. If there's no good reason, use tilem_link_send_file instead. */ +gboolean send_file_main(TilemCalcEmulator *emu, gpointer data); + +/* Request directory listing. */ +void tilem_link_get_dirlist(TilemCalcEmulator *emu); + +/* Get the calc model as needed by ticalcs functions */ +int get_calc_model(TilemCalc *calc); + +/* Show error */ +void show_error(TilemCalcEmulator *emu, const char *title, const char *message); + +/* Receive a variable and write it to a file. */ +void tilem_link_receive_file(TilemCalcEmulator *emu, + const TilemVarEntry* varentry, + const char* destination); + +/* Receive a list of variables (GSList of TilemVarEntries) and save + them to a group file. */ +void tilem_link_receive_group(TilemCalcEmulator *emu, + GSList *entries, + const char *destination); + +/* Receive variables with names matching a pattern. PATTERN is a + glob-like pattern in UTF-8. Files will be written out to + DESTDIR. */ +void tilem_link_receive_matching(TilemCalcEmulator *emu, + const char *pattern, + const char *destdir); + + +/* ##### pbar.c ##### */ + +/* Create or update the progress bar */ +void progress_bar_update(TilemCalcEmulator* emu); + + + +/* ##### animatedgif.c ##### */ + +/* Save a TilemAnimation to a GIF file. */ +void tilem_animation_write_gif(TilemAnimation *anim, byte* palette, int palette_size, FILE *fp); + + +/* ##### gifencod.c ##### */ + +/* Encode gif data */ +void GifEncode(FILE *fout, unsigned char *pixels, int depth, int siz); + + +/* ##### screenshot.c ##### */ + +/* create the screenshot popup */ +void popup_screenshot_window(TilemEmulatorWindow* ewin); + +/* Take a single screenshot */ +void quick_screenshot(TilemEmulatorWindow *ewin); + + +/* ##### keybindings.c ##### */ + +/* Load the keybindings */ +void tilem_keybindings_init(TilemCalcEmulator* emu, const char* model); + + +/* ##### menu.c ##### */ + +/* Build the menu (do not print it) */ +void build_menu(TilemEmulatorWindow* ewin); + + +/* ##### sendfile.c ##### */ + +/* Load a list of files through the GUI. The list of filenames must + end with NULL. */ +void load_files(TilemEmulatorWindow *ewin, char **filenames); + +/* Load a list of files from the command line. Filenames may begin + with an optional slot designation. */ +void load_files_cmdline(TilemEmulatorWindow *ewin, char **filenames); + +/* Prompt user to load a file from PC to TI */ +void load_file_dialog(TilemEmulatorWindow *ewin); + + +/* ##### rcvmenu.c ##### */ + +/* Createe the popup dialog */ +void popup_receive_menu(TilemEmulatorWindow *ewin); + +/* Create a TilemReceiveDialog */ +TilemReceiveDialog* tilem_receive_dialog_new(TilemCalcEmulator *emu); + +/* Destroy a TilemReceiveDialog */ +void tilem_receive_dialog_free(TilemReceiveDialog *rcvdlg); + +/* Update TilemReceiveDialog with directory listing. VARLIST is a + GSList of TilemVarEntries; the dialog assumes ownership of this + list. Display the dialog if it's currently hidden. */ +void tilem_receive_dialog_update(TilemReceiveDialog *rcvdlg, + GSList *varlist); + diff --git a/tool/tilem-src/gui/icons.c b/tool/tilem-src/gui/icons.c new file mode 100644 index 0000000..b1818a4 --- /dev/null +++ b/tool/tilem-src/gui/icons.c @@ -0,0 +1,71 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "icons.h" +#include "files.h" + +static const char * const custom_icons[] = { + /* Disassembly icons */ + "tilem-disasm-pc", + "tilem-disasm-break", + "tilem-disasm-break-pc", + + /* Debugger actions */ + "tilem-db-step", + "tilem-db-step-over", + "tilem-db-finish" +}; + +/* Set up custom icons. */ +void init_custom_icons() +{ + GtkIconTheme *theme; + GtkIconFactory *factory; + GtkIconSet *set; + GtkIconSource *source; + char *path; + gsize i; + + path = get_shared_dir_path("icons", NULL); + if (path) { + theme = gtk_icon_theme_get_default(); + gtk_icon_theme_append_search_path(theme, path); + g_free(path); + } + + factory = gtk_icon_factory_new(); + for (i = 0; i < G_N_ELEMENTS(custom_icons); i++) { + set = gtk_icon_set_new(); + source = gtk_icon_source_new(); + gtk_icon_source_set_icon_name(source, custom_icons[i]); + gtk_icon_set_add_source(set, source); + gtk_icon_source_free(source); + gtk_icon_factory_add(factory, custom_icons[i], set); + gtk_icon_set_unref(set); + } + gtk_icon_factory_add_default(factory); + g_object_unref(factory); +} diff --git a/tool/tilem-src/gui/icons.h b/tool/tilem-src/gui/icons.h new file mode 100644 index 0000000..3096f4c --- /dev/null +++ b/tool/tilem-src/gui/icons.h @@ -0,0 +1,20 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +void init_custom_icons(void); diff --git a/tool/tilem-src/gui/keybindings.c b/tool/tilem-src/gui/keybindings.c new file mode 100644 index 0000000..cbf4afd --- /dev/null +++ b/tool/tilem-src/gui/keybindings.c @@ -0,0 +1,255 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "gui.h" +#include "msgbox.h" +#include "files.h" + +/* Get the associated calculator key name */ +static int calc_key_from_name(const TilemCalc *calc, const char *name) +{ + int i; + + for (i = 0; i < 64; i++) + if (calc->hw.keynames[i] + && !strcmp(calc->hw.keynames[i], name)) + return i + 1; + + /* kludge: accept aliases for a few keys */ + for (i = 0; i < 64; i++) { + if (!calc->hw.keynames[i]) + continue; + + if (!strcmp(name, "Matrix") + && !strcmp(calc->hw.keynames[i], "Apps")) + return i + 1; + if (!strcmp(name, "Apps") + && !strcmp(calc->hw.keynames[i], "AppsMenu")) + return i + 1; + if (!strcmp(name, "List") + && !strcmp(calc->hw.keynames[i], "StatEd")) + return i + 1; + if (!strcmp(name, "Power") + && !strcmp(calc->hw.keynames[i], "Expon")) + return i + 1; + if (!strcmp(name, "Stat") + && !strcmp(calc->hw.keynames[i], "Table")) + return i + 1; + } + + return 0; +} + +/* Parse a line of the group (model) in the keybindings file */ +static gboolean parse_binding(TilemKeyBinding *kb, + const char *pckeys, const char *tikeys, + const TilemCalc *calc) +{ + const char *p; + char *s; + int n, k; + + kb->modifiers = 0; + kb->keysym = 0; + kb->nscancodes = 0; + kb->scancodes = NULL; + + /* Parse modifiers */ + + while ((p = strchr(pckeys, '+'))) { + s = g_strndup(pckeys, p - pckeys); + g_strstrip(s); + if (!g_ascii_strcasecmp(s, "ctrl") + || !g_ascii_strcasecmp(s, "control")) + kb->modifiers |= GDK_CONTROL_MASK; + else if (!g_ascii_strcasecmp(s, "shift")) + kb->modifiers |= GDK_SHIFT_MASK; + else if (!g_ascii_strcasecmp(s, "alt") + || !g_ascii_strcasecmp(s, "mod1")) + kb->modifiers |= GDK_MOD1_MASK; + else if (!g_ascii_strcasecmp(s, "mod2")) + kb->modifiers |= GDK_MOD2_MASK; + else if (!g_ascii_strcasecmp(s, "mod3")) + kb->modifiers |= GDK_MOD3_MASK; + else if (!g_ascii_strcasecmp(s, "mod4")) + kb->modifiers |= GDK_MOD4_MASK; + else if (!g_ascii_strcasecmp(s, "mod5")) + kb->modifiers |= GDK_MOD5_MASK; + else if (!g_ascii_strcasecmp(s, "lock") + || !g_ascii_strcasecmp(s, "capslock")) + kb->modifiers |= GDK_LOCK_MASK; + else { + g_free(s); + return FALSE; + } + g_free(s); + pckeys = p + 1; + } + + /* Parse keysym */ + + s = g_strstrip(g_strdup(pckeys)); + kb->keysym = gdk_keyval_from_name(s); + g_free(s); + if (!kb->keysym) + return FALSE; + + /* Parse calculator keys */ + + /* FIXME: allow combinations of simultaneous keys (separated + by '+'); current TilemKeyBinding struct doesn't provide for + this */ + + n = 0; + do { + if ((p = strchr(tikeys, ','))) + s = g_strndup(tikeys, p - tikeys); + else + s = g_strdup(tikeys); + g_strstrip(s); + + k = calc_key_from_name(calc, s); + g_free(s); + + if (!k) { + g_free(kb->scancodes); + kb->scancodes = NULL; + return FALSE; + } + + kb->nscancodes++; + if (kb->nscancodes >= n) { + n = kb->nscancodes * 2; + kb->scancodes = g_renew(byte, kb->scancodes, n); + } + kb->scancodes[kb->nscancodes - 1] = k; + + tikeys = (p ? p + 1 : NULL); + } while (tikeys); + + return TRUE; +} + +/* Parse a group (model) in the keybindings file */ +static void parse_binding_group(TilemCalcEmulator *emu, GKeyFile *gkf, + const char *group, int maxdepth) +{ + gchar **keys, **groups; + char *k, *v; + int i, n; + + keys = g_key_file_get_keys(gkf, group, NULL, NULL); + if (!keys) { + printf("no bindings for %s\n", group); + return; + } + + for (i = 0; keys[i]; i++) + ; + + n = emu->nkeybindings; + emu->keybindings = g_renew(TilemKeyBinding, emu->keybindings, n + i); + + for(i = 0; keys[i]; i++) { + k = keys[i]; + if (!strcmp(k, "INHERIT")) + continue; + + v = g_key_file_get_value(gkf, group, k, NULL); + if (!v) + continue; + + if (parse_binding(&emu->keybindings[n], k, v, emu->calc)) + n++; + else + g_printerr("syntax error in key bindings: '%s=%s'\n", + k, v); + g_free(v); + } + + emu->nkeybindings = n; + + g_strfreev(keys); + + /* Include all bindings from groups marked as INHERIT */ + + if (maxdepth == 0) + return; + + groups = g_key_file_get_string_list(gkf, group, "INHERIT", + NULL, NULL); + for (i = 0; groups && groups[i]; i++) + parse_binding_group(emu, gkf, groups[i], maxdepth - 1); + g_strfreev(groups); + +} + +/* Init the keybindings struct and open the keybindings file */ +void tilem_keybindings_init(TilemCalcEmulator *emu, const char *model) +{ + char *kfname = get_shared_file_path("keybindings.ini", NULL); + char *dname; + GKeyFile *gkf; + GError *err = NULL; + + g_return_if_fail(emu != NULL); + g_return_if_fail(emu != NULL); + g_return_if_fail(emu->calc != NULL); + + if (kfname == NULL) { + messagebox00(NULL, GTK_MESSAGE_ERROR, + "Unable to load key bindings", + "The file keybindings.ini could not be found." + " TilEm may not have been installed correctly."); + return; + } + + gkf = g_key_file_new(); + if (!g_key_file_load_from_file(gkf, kfname, 0, &err)) { + dname = g_filename_display_name(kfname); + messagebox02(NULL, GTK_MESSAGE_ERROR, + "Unable to load key bindings", + "An error occurred while reading %s: %s", + dname, err->message); + g_error_free(err); + g_free(dname); + g_free(kfname); + return; + } + + g_free(emu->keybindings); + emu->keybindings = NULL; + emu->nkeybindings = 0; + + parse_binding_group(emu, gkf, model, 5); + + g_key_file_free(gkf); + g_free(kfname); +} diff --git a/tool/tilem-src/gui/keypaddlg.c b/tool/tilem-src/gui/keypaddlg.c new file mode 100644 index 0000000..e099d20 --- /dev/null +++ b/tool/tilem-src/gui/keypaddlg.c @@ -0,0 +1,305 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "gui.h" + +#define NGROUPS 7 +#define NKEYS 8 + +/* Check-button toggled */ +static void group_toggled(GtkToggleButton *btn, gpointer data) +{ + TilemKeypadDialog *kpdlg = data; + TilemCalcEmulator *emu; + int i; + gboolean state; + + if (kpdlg->refreshing) + return; + + g_return_if_fail(kpdlg->dbg != NULL); + g_return_if_fail(kpdlg->dbg->emu != NULL); + emu = kpdlg->dbg->emu; + + state = gtk_toggle_button_get_active(btn); + + for (i = 0; i < NGROUPS; i++) { + if (GTK_WIDGET(btn) == kpdlg->output[i]) { + tilem_calc_emulator_lock(emu); + if (state) + emu->calc->keypad.group &= ~(1 << i); + else + emu->calc->keypad.group |= (1 << i); + tilem_calc_emulator_unlock(emu); + + tilem_keypad_dialog_refresh(kpdlg); + return; + } + } + + g_return_if_reached(); +} + +/* Key toggled */ +static void key_toggled(GtkToggleButton *btn, gpointer data) +{ + TilemKeypadDialog *kpdlg = data; + TilemCalcEmulator *emu; + int i, j, k; + gboolean state; + + if (kpdlg->refreshing) + return; + + g_return_if_fail(kpdlg->dbg != NULL); + g_return_if_fail(kpdlg->dbg->emu != NULL); + emu = kpdlg->dbg->emu; + + state = gtk_toggle_button_get_active(btn); + + for (i = 0; i < NGROUPS; i++) { + for (j = 0; j < NKEYS; j++) { + if (GTK_WIDGET(btn) == kpdlg->keys[i][j]) { + k = i * 8 + j + 1; + if (state) + tilem_calc_emulator_press_key(emu, k); + else + tilem_calc_emulator_release_key(emu, k); + return; + } + } + } + + g_return_if_reached(); +} + +/* Create a new TilemKeypadDialog. */ +TilemKeypadDialog *tilem_keypad_dialog_new(TilemDebugger *dbg) +{ + TilemKeypadDialog *kpdlg; + GtkWidget *tbl1, *tbl2, *hbox, *vbox, *btn, *lbl; + int i, j; + char buf[20]; + + g_return_val_if_fail(dbg != NULL, NULL); + + kpdlg = g_slice_new0(TilemKeypadDialog); + kpdlg->dbg = dbg; + + kpdlg->window = gtk_dialog_new_with_buttons + ("Keypad", NULL, 0, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + g_signal_connect(kpdlg->window, "delete-event", + G_CALLBACK(gtk_widget_hide_on_delete), NULL); + + tbl1 = gtk_table_new(NGROUPS, NKEYS, TRUE); + hbox = gtk_hbox_new(TRUE, 0); + vbox = gtk_vbox_new(TRUE, 0); + + /* Keypad buttons (labels will be filled in, and buttons + shown/hidden, by tilem_keypad_dialog_calc_changed()) + + Buttons are displayed right to left, top to bottom; this + way, the layout of groups 1-5 roughly corresponds to the + physical layout of the keys, and the "input" value can be + read across the bottom as a binary number. */ + + for (i = 0; i < NGROUPS; i++) { + for (j = 0; j < NKEYS; j++) { + btn = gtk_toggle_button_new_with_label(""); + kpdlg->keys[i][j] = btn; + gtk_table_attach(GTK_TABLE(tbl1), btn, + NKEYS - j - 1, NKEYS - j, + i, i + 1, + GTK_FILL, GTK_FILL, 2, 2); + + g_signal_connect(btn, "toggled", + G_CALLBACK(key_toggled), kpdlg); + + gtk_widget_set_no_show_all(btn, TRUE); + } + } + + /* Check buttons for key groups (output bits) */ + + for (i = 0; i < NGROUPS; i++) { + g_snprintf(buf, sizeof(buf), "Group %d", i); + btn = gtk_check_button_new_with_label(buf); + kpdlg->output[i] = btn; + gtk_box_pack_start(GTK_BOX(vbox), btn, FALSE, TRUE, 2); + + g_signal_connect(btn, "toggled", + G_CALLBACK(group_toggled), kpdlg); + } + + /* Labels for input bits */ + + for (j = NKEYS - 1; j >= 0; j--) { + kpdlg->input[j] = lbl = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, TRUE, 2); + } + + tbl2 = gtk_table_new(3, 2, FALSE); + gtk_container_set_border_width(GTK_CONTAINER(tbl2), 6); + gtk_table_set_row_spacings(GTK_TABLE(tbl2), 12); + gtk_table_set_col_spacings(GTK_TABLE(tbl2), 12); + + lbl = gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(lbl), "Scan Groups"); + gtk_table_attach(GTK_TABLE(tbl2), lbl, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + + lbl = gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(lbl), "Keys"); + gtk_table_attach(GTK_TABLE(tbl2), lbl, 1, 2, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + + lbl = gtk_label_new("Input Value:"); + gtk_table_attach(GTK_TABLE(tbl2), lbl, 0, 1, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + + gtk_table_attach(GTK_TABLE(tbl2), vbox, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_table_attach(GTK_TABLE(tbl2), tbl1, 1, 2, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_table_attach(GTK_TABLE(tbl2), hbox, 1, 2, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + + gtk_widget_show_all(tbl2); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(kpdlg->window)); + gtk_box_pack_start(GTK_BOX(vbox), tbl2, FALSE, FALSE, 0); + + tilem_keypad_dialog_calc_changed(kpdlg); + + return kpdlg; +} + +/* Free a TilemKeypadDialog. */ +void tilem_keypad_dialog_free(TilemKeypadDialog *kpdlg) +{ + g_return_if_fail(kpdlg != NULL); + if (kpdlg->window) + gtk_widget_destroy(kpdlg->window); + g_slice_free(TilemKeypadDialog, kpdlg); +} + +/* New calculator loaded. */ +void tilem_keypad_dialog_calc_changed(TilemKeypadDialog *kpdlg) +{ + TilemCalc *calc; + int i, j, k; + GtkWidget *btn, *lbl; + + g_return_if_fail(kpdlg != NULL); + g_return_if_fail(kpdlg->dbg != NULL); + g_return_if_fail(kpdlg->dbg->emu != NULL); + g_return_if_fail(kpdlg->dbg->emu->calc != NULL); + calc = kpdlg->dbg->emu->calc; + + for (i = 0; i < NGROUPS; i++) { + for (j = 0; j < NKEYS; j++) { + btn = kpdlg->keys[i][j]; + k = i * 8 + j + 1; + if (k != TILEM_KEY_ON + && calc->hw.keynames[k - 1] != NULL) { + lbl = gtk_bin_get_child(GTK_BIN(btn)); + gtk_label_set_text(GTK_LABEL(lbl), + calc->hw.keynames[k - 1]); + gtk_widget_show(btn); + } + else { + gtk_widget_hide(btn); + } + } + } + + tilem_keypad_dialog_refresh(kpdlg); +} + +/* Refresh key states. */ +void tilem_keypad_dialog_refresh(TilemKeypadDialog *kpdlg) +{ + int i, j; + byte keys[NGROUPS], inval, outval; + TilemCalcEmulator *emu; + GtkWidget *btn, *lbl; + + g_return_if_fail(kpdlg != NULL); + g_return_if_fail(kpdlg->dbg != NULL); + g_return_if_fail(kpdlg->dbg->emu != NULL); + emu = kpdlg->dbg->emu; + + if (kpdlg->refreshing) + return; + + kpdlg->refreshing = TRUE; + + tilem_calc_emulator_lock(emu); + for (i = 0; i < NGROUPS; i++) + keys[i] = emu->calc->keypad.keysdown[i]; + outval = emu->calc->keypad.group; + inval = tilem_keypad_read_keys(emu->calc); + tilem_calc_emulator_unlock(emu); + + for (i = 0; i < NGROUPS; i++) { + for (j = 0; j < NKEYS; j++) { + btn = kpdlg->keys[i][j]; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btn), + (keys[i] & (1 << j))); + } + } + + + for (i = 0; i < NGROUPS; i++) { + btn = kpdlg->output[i]; + if (emu->paused) { + gtk_widget_set_sensitive(btn, TRUE); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btn), + !(outval & (1 << i))); + } + else { + gtk_widget_set_sensitive(btn, FALSE); + } + } + + for (j = 0; j < NKEYS; j++) { + lbl = kpdlg->input[j]; + gtk_label_set_text(GTK_LABEL(lbl), + (emu->paused + ? (inval & (1 << j) ? "1" : "0") + : "")); + + } + + kpdlg->refreshing = FALSE; +} diff --git a/tool/tilem-src/gui/link.c b/tool/tilem-src/gui/link.c new file mode 100644 index 0000000..6a3ecc6 --- /dev/null +++ b/tool/tilem-src/gui/link.c @@ -0,0 +1,1301 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "emucore.h" +#include "ti81prg.h" +#include "msgbox.h" + +/**************** Internal link emulation ****************/ + +/* Open cable */ +static int ilp_open(CableHandle* cbl) +{ + TilemCalcEmulator* emu = cbl->priv; + + tilem_em_lock(emu); + + if (emu->ilp_active) { + fprintf(stderr, "INTERNAL ERROR: cable already opened\n"); + tilem_em_unlock(emu); + return 1; + } + + emu->ilp_active = TRUE; + tilem_linkport_graylink_reset(emu->calc); + tilem_em_unlock(emu); + return 0; +} + +/* Close cable */ +static int ilp_close(CableHandle* cbl) +{ + TilemCalcEmulator* emu = cbl->priv; + + tilem_em_lock(emu); + + if (!emu->ilp_active) { + fprintf(stderr, "INTERNAL ERROR: cable already closed\n"); + tilem_em_unlock(emu); + return 1; + } + + emu->ilp_active = FALSE; + emu->calc->linkport.linkemu = TILEM_LINK_EMULATOR_NONE; + tilem_linkport_graylink_reset(emu->calc); + tilem_em_unlock(emu); + return 0; +} + +/* Reset cable */ +static int ilp_reset(CableHandle* cbl) +{ + TilemCalcEmulator* emu = cbl->priv; + + tilem_em_lock(emu); + tilem_linkport_graylink_reset(emu->calc); + tilem_em_unlock(emu); + return 0; +} + +/* Send data to calc */ +static int ilp_send(CableHandle* cbl, uint8_t* data, uint32_t count) +{ + TilemCalcEmulator* emu = cbl->priv; + int timeout = cbl->timeout * 100000; + + tilem_em_lock(emu); + while (count > 0) { + if (tilem_em_send_byte(emu, data[0], timeout, TRUE)) { + tilem_em_unlock(emu); + return ERROR_WRITE_TIMEOUT; + } + data++; + count--; + } + tilem_em_unlock(emu); + return 0; +} + +/* cool-down period required after receiving and before sending */ +#define COOLDOWN 10000 + +/* Receive data from calc */ +static int ilp_recv(CableHandle* cbl, uint8_t* data, uint32_t count) +{ + TilemCalcEmulator* emu = cbl->priv; + int timeout = cbl->timeout * 100000; + int value; + + tilem_em_lock(emu); + while (count > 0) { + value = tilem_em_get_byte(emu, timeout, TRUE); + if (value < 0) { + tilem_em_unlock(emu); + return ERROR_READ_TIMEOUT; + } + data[0] = value; + data++; + count--; + } + tilem_em_delay(emu, COOLDOWN, TRUE); + tilem_em_unlock(emu); + return 0; +} + +/* Check if ready */ +static int ilp_check(CableHandle* cbl, int* status) +{ + TilemCalcEmulator* emu = cbl->priv; + + tilem_em_lock(emu); + + *status = STATUS_NONE; + if (emu->calc->linkport.lines) + *status |= STATUS_RX; + if (emu->calc->linkport.extlines) + *status |= STATUS_TX; + + tilem_em_unlock(emu); + return 0; +} + +/* Open a cable */ +static CableHandle* internal_link_handle_new(TilemCalcEmulator* emu) +{ + CableHandle* cbl; + + cbl = ticables_handle_new(CABLE_ILP, PORT_0); + if (!cbl) + return NULL; + + cbl->priv = emu; + cbl->cable->open = ilp_open; + cbl->cable->close = ilp_close; + cbl->cable->reset = ilp_reset; + cbl->cable->send = ilp_send; + cbl->cable->recv = ilp_recv; + cbl->cable->check = ilp_check; + + return cbl; +} + +/**************** Automatic link menu ****************/ + +/* Run a key (wait, press, wait; release; wait) */ +static void run_with_key(TilemCalcEmulator* emu, int key) +{ + tilem_em_delay(emu, 50000, TRUE); + tilem_keypad_press_key(emu->calc, key); + tilem_em_delay(emu, 50000, TRUE); + tilem_keypad_release_key(emu->calc, key); + tilem_em_delay(emu, 50000, TRUE); +} + +/* Automatically press key to be in the receive mode (ti82 and ti85) */ +static void prepare_for_link_send(TilemCalcEmulator* emu) +{ + tilem_em_lock(emu); + tilem_em_wake_up(emu, TRUE); + if (emu->calc->hw.model_id == TILEM_CALC_TI82) { + run_with_key(emu, TILEM_KEY_2ND); + run_with_key(emu, TILEM_KEY_MODE); + run_with_key(emu, TILEM_KEY_2ND); + run_with_key(emu, TILEM_KEY_GRAPHVAR); + run_with_key(emu, TILEM_KEY_RIGHT); + run_with_key(emu, TILEM_KEY_ENTER); + } + else if (emu->calc->hw.model_id == TILEM_CALC_TI85) { + run_with_key(emu, TILEM_KEY_MODE); + run_with_key(emu, TILEM_KEY_MODE); + run_with_key(emu, TILEM_KEY_MODE); + run_with_key(emu, TILEM_KEY_2ND); + run_with_key(emu, TILEM_KEY_GRAPHVAR); + run_with_key(emu, TILEM_KEY_WINDOW); + } + tilem_em_unlock(emu); +} + +/* Automatically press key to be in the send mode (ti82 and ti85) */ +static void prepare_for_link_receive(TilemCalcEmulator *emu) +{ + tilem_em_lock(emu); + tilem_em_wake_up(emu, TRUE); + if (emu->calc->hw.model_id == TILEM_CALC_TI82) { + run_with_key(emu, TILEM_KEY_2ND); + run_with_key(emu, TILEM_KEY_MODE); + run_with_key(emu, TILEM_KEY_2ND); + run_with_key(emu, TILEM_KEY_GRAPHVAR); + run_with_key(emu, TILEM_KEY_ENTER); + tilem_em_delay(emu, 10000000, TRUE); + run_with_key(emu, TILEM_KEY_RIGHT); + run_with_key(emu, TILEM_KEY_ENTER); + } + else if (emu->calc->hw.model_id == TILEM_CALC_TI85) { + run_with_key(emu, TILEM_KEY_MODE); + run_with_key(emu, TILEM_KEY_MODE); + run_with_key(emu, TILEM_KEY_MODE); + run_with_key(emu, TILEM_KEY_2ND); + run_with_key(emu, TILEM_KEY_GRAPHVAR); + run_with_key(emu, TILEM_KEY_YEQU); + run_with_key(emu, TILEM_KEY_GRAPH); + tilem_em_delay(emu, 10000000, TRUE); + run_with_key(emu, TILEM_KEY_ZOOM); + tilem_em_delay(emu, 10000000, TRUE); + run_with_key(emu, TILEM_KEY_YEQU); + } + tilem_em_unlock(emu); +} + +/**************** Calc handle ****************/ + +static GStaticPrivate current_emu_key = G_STATIC_PRIVATE_INIT; + +/* ticalcs progress bar callback */ +static void pbar_do_update() +{ + TilemCalcEmulator *emu = g_static_private_get(¤t_emu_key); + CalcUpdate *upd = emu->link_update; + gdouble frac; + + if (upd->max1 > 0 && upd->max2 > 0) + frac = ((gdouble) upd->cnt1 / upd->max1 + upd->cnt2) / upd->max2; + else if (upd->max1 > 0) + frac = ((gdouble) upd->cnt1 / upd->max1); + else if (upd->max2 > 0) + frac = ((gdouble) upd->cnt2 / upd->max2); + else + frac = -1.0; + + tilem_em_set_progress(emu, frac, upd->text); +} + +/* Get the calc model (compatible for ticalcs) */ +int get_calc_model(TilemCalc *calc) +{ + return model_to_calcmodel(calc->hw.model_id); +} + +/* Create a calc handle */ +void begin_link(TilemCalcEmulator *emu, CableHandle **cbl, CalcHandle **ch, + const char *title) +{ + tilem_em_unlock(emu); + + *cbl = internal_link_handle_new(emu); + + emu->link_update->max1 = 0; + emu->link_update->max2 = 0; + emu->link_update->text[0] = 0; + + emu->link_update->pbar = &pbar_do_update; + emu->link_update->label = &pbar_do_update; + + g_static_private_set(¤t_emu_key, emu, NULL); + + tilem_em_set_progress_title(emu, title); + + *ch = ticalcs_handle_new(get_calc_model(emu->calc)); + if (!*ch) { + g_critical("unsupported calc"); + return; + } + + ticalcs_update_set(*ch, emu->link_update); + ticalcs_cable_attach(*ch, *cbl); +} + +/* Destroy calc handle */ +void end_link(TilemCalcEmulator *emu, CableHandle *cbl, CalcHandle *ch) +{ + tilem_em_set_progress_title(emu, NULL); + + ticalcs_cable_detach(ch); + ticalcs_handle_del(ch); + ticables_handle_del(cbl); + + tilem_em_lock(emu); +} + +/**************** Error messages ****************/ + +static char * get_tilibs_error(int errcode) +{ + char *p = NULL; + + if (!ticalcs_error_get(errcode, &p) + || !ticables_error_get(errcode, &p) + || !tifiles_error_get(errcode, &p)) + return p; + else + return g_strdup_printf("Unknown error (%d)", errcode); +} + +static char * get_ti81_error(int errcode) +{ + switch (errcode) { + case TI81_ERR_FILE_IO: + return g_strdup("File I/O error"); + + case TI81_ERR_INVALID_FILE: + return g_strdup("Not a valid PRG file"); + + case TI81_ERR_MEMORY: + return g_strdup("The calculator does not have enough free memory" + " to load the program."); + + case TI81_ERR_SLOTS_FULL: + return g_strdup("All calculator program slots are in use. " + " You must delete an existing program before" + " loading a new program."); + + case TI81_ERR_BUSY: + return g_strdup("The calculator is currently busy. Please" + " exit to the home screen before loading" + " programs."); + + default: + return g_strdup_printf("Unknown error code (%d)", errcode); + } +} + +void show_error(TilemCalcEmulator *emu, + const char *title, const char *message) +{ + if (emu->ewin) + messagebox11(emu->ewin->window, GTK_MESSAGE_ERROR, + "%s", title, "%s", message); + else + g_printerr("\n=== %s ===\n%s\n\n", title, message); +} + +/**************** Sending files ****************/ + +/* Send a file to TI-81 */ +static gboolean send_file_ti81(TilemCalcEmulator *emu, struct TilemSendFileInfo *sf) +{ + TI81Program *prgm = NULL; + FILE *f; + int errnum; + sf->error_message = NULL; + + f = g_fopen(sf->filename, "rb"); + if (!f) { + sf->error_message = g_strdup_printf + ("Failed to open %s for reading: %s", + sf->display_name, g_strerror(errno)); + return FALSE; + } + + if (ti81_read_prg_file(f, &prgm)) { + sf->error_message = g_strdup_printf + ("The file %s is not a valid TI-81 program file.", + sf->display_name); + fclose(f); + return FALSE; + } + + fclose(f); + + tilem_em_wake_up(emu, TRUE); + + prgm->info.slot = sf->slot; + errnum = ti81_load_program(emu->calc, prgm); + ti81_program_free(prgm); + + if (errnum && !emu->task_abort) + sf->error_message = get_ti81_error(errnum); + return (errnum == 0); +} + +/* Get application name */ +static gboolean get_app_name(const FlashContent *flashc, char *name) +{ + int i; + const unsigned char *data; + unsigned int type, length; + + if (flashc->num_pages < 1 || flashc->pages[0]->size < 6 + || flashc->pages[0]->data[0] != 0x80 + || flashc->pages[0]->data[1] != 0x0f) + return FALSE; + + i = 6; + data = flashc->pages[0]->data; + while (i < flashc->pages[0]->size && i < 128) { + type = (data[i] << 8 | (data[i + 1] & 0xf0)); + length = data[i + 1] & 0x0f; + i += 2; + + if (length == 0x0d) { + length = data[i]; + i++; + } + else if (length == 0x0e) { + length = (data[i] << 8 | data[i + 1]); + i += 2; + } + else if (length == 0x0f) { + return FALSE; + } + + if (type == 0x8070) + return FALSE; + + if (type == 0x8040) { + memcpy(name, data + i, length > 8 ? 8 : length); + return TRUE; + } + } + return FALSE; +} + +/* Try to delete an existing Flash app before we send a replacement */ +static void try_delete_app(CalcHandle *ch, const FlashContent *flashc) +{ + VarRequest vr; + + /* TI-73 does not support remote deletion */ + if (ch->model == CALC_TI73) + return; + + memset(&vr, 0, sizeof(VarRequest)); + if (!get_app_name(flashc, vr.name)) + return; + + /* Why does this use type 0x14 and not 0x24? I don't know. */ + vr.type = 0x14; + ticalcs_calc_del_var(ch, &vr); + /* if an error occurs, ignore it */ +} + +/* Send a file using ticalcs2 */ +static gboolean send_file_linkport(TilemCalcEmulator *emu, struct TilemSendFileInfo *sf) +{ + CalcModel model; + FileClass cls; + CableHandle *cbl; + CalcHandle *ch; + FileContent *filec; + BackupContent *backupc; + FlashContent *flashc; + TigContent *tigc; + CalcMode mode; + int e; + char *desc; + + model = get_calc_model(emu->calc); + cls = tifiles_file_get_class(sf->filename); + + desc = g_strdup_printf("Sending %s", sf->display_name); + + /* Read input file */ + + switch (cls) { + case TIFILE_SINGLE: + case TIFILE_GROUP: + case TIFILE_REGULAR: + filec = tifiles_content_create_regular(model); + e = tifiles_file_read_regular(sf->filename, filec); + if (!e) { + begin_link(emu, &cbl, &ch, desc); + if (sf->first) + prepare_for_link_send(emu); + mode = (sf->last ? MODE_SEND_LAST_VAR : MODE_NORMAL); + e = ticalcs_calc_send_var(ch, mode, filec); + end_link(emu, cbl, ch); + } + tifiles_content_delete_regular(filec); + break; + + case TIFILE_BACKUP: + backupc = tifiles_content_create_backup(model); + e = tifiles_file_read_backup(sf->filename, backupc); + if (!e) { + begin_link(emu, &cbl, &ch, desc); + prepare_for_link_send(emu); + e = ticalcs_calc_send_backup(ch, backupc); + end_link(emu, cbl, ch); + } + tifiles_content_delete_backup(backupc); + break; + + case TIFILE_FLASH: + case TIFILE_OS: + case TIFILE_APP: + flashc = tifiles_content_create_flash(model); + e = tifiles_file_read_flash(sf->filename, flashc); + if (!e) { + begin_link(emu, &cbl, &ch, desc); + ticables_options_set_timeout(cbl, 30 * 10); + prepare_for_link_send(emu); + if (tifiles_file_is_os(sf->filename)) + e = ticalcs_calc_send_os(ch, flashc); + else if (tifiles_file_is_app(sf->filename)) { + try_delete_app(ch, flashc); + e = ticalcs_calc_send_app(ch, flashc); + } + else + e = ticalcs_calc_send_cert(ch, flashc); + end_link(emu, cbl, ch); + } + tifiles_content_delete_flash(flashc); + break; + + case TIFILE_TIGROUP: + tigc = tifiles_content_create_tigroup(model, 0); + e = tifiles_file_read_tigroup(sf->filename, tigc); + if (!e) { + begin_link(emu, &cbl, &ch, desc); + prepare_for_link_send(emu); + e = ticalcs_calc_send_tigroup(ch, tigc, TIG_ALL); + end_link(emu, cbl, ch); + } + tifiles_content_delete_tigroup(tigc); + break; + + default: + g_free(desc); + sf->error_message = g_strdup_printf + ("The file %s is not a valid program or" + " variable file.", + sf->display_name); + return FALSE; + } + + g_free(desc); + if (e && !emu->task_abort) + sf->error_message = get_tilibs_error(e); + return (e == 0); +} + +gboolean send_file_main(TilemCalcEmulator *emu, gpointer data) +{ + struct TilemSendFileInfo *sf = data; + /*emu->ilp.finished_cond = g_cond_new(); */ + + if (emu->calc->hw.model_id == TILEM_CALC_TI81) + return send_file_ti81(emu, sf); + else + return send_file_linkport(emu, sf); +} + +static void send_file_finished(TilemCalcEmulator *emu, gpointer data, + gboolean cancelled) +{ + struct TilemSendFileInfo *sf = data; + + if (sf->error_message && !cancelled) + show_error(emu, "Unable to send file", sf->error_message); + + g_free(sf->filename); + g_free(sf->display_name); + g_free(sf->error_message); + g_slice_free(struct TilemSendFileInfo, sf); + + /*g_cond_broadcast(emu->ilp.finished_cond);*/ + + +} + +void tilem_link_send_file(TilemCalcEmulator *emu, const char *filename, + int slot, gboolean first, gboolean last) +{ + struct TilemSendFileInfo *sf; + + sf = g_slice_new0(struct TilemSendFileInfo); + sf->filename = g_strdup(filename); + sf->display_name = g_filename_display_basename(filename); + sf->slot = slot; + sf->first = first; + sf->last = last; + + tilem_calc_emulator_begin(emu, &send_file_main, + &send_file_finished, sf); +} + +/**************** Get directory listing ****************/ + +/* Make a copy of a TilemVarEntry */ +TilemVarEntry *tilem_var_entry_copy(const TilemVarEntry *tve) +{ + TilemVarEntry *nve; + + g_return_val_if_fail(tve != NULL, NULL); + + nve = g_slice_new(TilemVarEntry); + *nve = *tve; + + if (tve->ve) { + nve->ve = g_slice_new(VarEntry); + *nve->ve = *tve->ve; + nve->ve->data = g_memdup(tve->ve->data, tve->ve->size); + } + if (tve->name_str) + nve->name_str = g_strdup(tve->name_str); + if (tve->type_str) + nve->type_str = g_strdup(tve->type_str); + if (tve->slot_str) + nve->slot_str = g_strdup(tve->slot_str); + if (tve->file_ext) + nve->file_ext = g_strdup(tve->file_ext); + if (tve->filetype_desc) + nve->filetype_desc = g_strdup(tve->filetype_desc); + + return nve; +} + +/* Free a TilemVarEntry */ +void tilem_var_entry_free(TilemVarEntry *tve) +{ + g_return_if_fail(tve != NULL); + if (tve->ve) { + g_free(tve->ve->data); + g_slice_free(VarEntry, tve->ve); + } + g_free(tve->name_str); + g_free(tve->type_str); + g_free(tve->slot_str); + g_free(tve->file_ext); + g_free(tve->filetype_desc); + g_slice_free(TilemVarEntry, tve); +} + +struct dirlistinfo { + GSList *list; + char *error_message; + gboolean aborted; + gboolean no_gui; +}; + +/* Convert tifiles VarEntry into a TilemVarEntry */ +static TilemVarEntry *convert_ve(TilemCalcEmulator *emu, VarEntry *ve, + gboolean is_flash) +{ + TilemVarEntry *tve = g_slice_new0(TilemVarEntry); + CalcModel tfmodel = get_calc_model(emu->calc); + const char *model_str; + const char *type_str; + const char *fext; + + tve->model = emu->calc->hw.model_id; + + tve->ve = g_slice_new(VarEntry); + *tve->ve = *ve; + if (ve->data) + tve->ve->data = g_memdup(ve->data, ve->size); + + tve->size = ve->size; + tve->archived = (ve->attr & ATTRB_ARCHIVED ? TRUE : FALSE); + tve->can_group = TRUE; + + tve->name_str = ticonv_varname_to_utf8(tfmodel, ve->name, ve->type); + g_strchomp(tve->name_str); + tve->type_str = g_strdup(tifiles_vartype2string(tfmodel, ve->type)); + fext = tifiles_vartype2fext(tfmodel, ve->type); + tve->file_ext = g_ascii_strdown(fext, -1); + + /* FIXME: the filetype_desc string is used as a description in + the file chooser. It should be written in the same style + as other such strings (e.g., "TI-83 Plus programs" rather + than "TI83+ Program".) But this is better than nothing. */ + model_str = tifiles_model_to_string(tfmodel); + type_str = tifiles_vartype2type(tfmodel, ve->type); + tve->filetype_desc = g_strdup_printf("%s %s", model_str, type_str); + + tve->can_group = !is_flash; + + return tve; +} + +/* Convert a complete directory listing */ +static void convert_dir_list(TilemCalcEmulator *emu, GNode *root, + gboolean is_flash, GSList **list) +{ + GNode *dir, *var; + VarEntry *ve; + TilemVarEntry *tve; + + if (!root) + return; + + for (dir = root->children; dir; dir = dir->next) { + for (var = dir->children; var; var = var->next) { + ve = var->data; + tve = convert_ve(emu, ve, is_flash); + *list = g_slist_prepend(*list, tve); + } + } +} + +/* Request directory listing using ticalcs */ +static gboolean get_dirlist_silent(TilemCalcEmulator *emu, + struct dirlistinfo *dl) +{ + CableHandle *cbl; + CalcHandle *ch; + GNode *vars = NULL, *apps = NULL; + GSList *list = NULL; + int e = 0; + + begin_link(emu, &cbl, &ch, "Reading variable list"); + prepare_for_link_receive(emu); + + if (ticalcs_calc_features(ch) & OPS_DIRLIST) { + e = ticalcs_calc_get_dirlist(ch, &vars, &apps); + if (!e) { + convert_dir_list(emu, vars, FALSE, &list); + convert_dir_list(emu, apps, TRUE, &list); + } + ticalcs_dirlist_destroy(&vars); + ticalcs_dirlist_destroy(&apps); + } + + end_link(emu, cbl, ch); + + dl->list = g_slist_reverse(list); + + dl->aborted = emu->task_abort; + + if (e && !emu->task_abort) + dl->error_message = get_tilibs_error(e); + return (e == 0); +} + +/* Transfer variables non-silently using ticalcs */ +static gboolean get_dirlist_nonsilent(TilemCalcEmulator *emu, + struct dirlistinfo *dl) +{ + CableHandle *cbl; + CalcHandle *ch; + FileContent *fc; + VarEntry *head_entry; + TilemVarEntry *tve; + GSList *list = NULL; + int e, i; + + begin_link(emu, &cbl, &ch, "Receiving variables"); + prepare_for_link_receive(emu); + + fc = tifiles_content_create_regular(ch->model); + e = ticalcs_calc_recv_var_ns(ch, MODE_BACKUP, fc, &head_entry); + if (!e) { + for (i = 0; i < fc->num_entries; i++) { + tve = convert_ve(emu, fc->entries[i], FALSE); + list = g_slist_prepend(list, tve); + } + } + if (head_entry) + tifiles_ve_delete(head_entry); + tifiles_content_delete_regular(fc); + + end_link(emu, cbl, ch); + + dl->list = g_slist_reverse(list); + + dl->aborted = emu->task_abort; + + if (e && !emu->task_abort) + dl->error_message = get_tilibs_error(e); + return (e == 0); +} + +/* Get TI-81 directory listing */ +static gboolean get_dirlist_ti81(TilemCalcEmulator *emu, + struct dirlistinfo *dl) +{ + int i, slot; + TI81ProgInfo info; + GSList *list = NULL; + TilemVarEntry *tve; + int e; + + tilem_em_wake_up(emu, TRUE); + + for (i = 0; i <= TI81_SLOT_MAX; i++) { + /* put Prgm0 after Prgm9, the way it appears in the menu */ + if (i < 9) + slot = i + 1; + else if (i == 9) + slot = 0; + else + slot = i; + + if ((e = ti81_get_program_info(emu->calc, slot, &info))) + break; + + if (info.size == 0) + continue; + + tve = g_slice_new0(TilemVarEntry); + tve->model = TILEM_CALC_TI81; + tve->slot = info.slot; + tve->name_str = ti81_program_name_to_string(info.name); + tve->slot_str = ti81_program_slot_to_string(info.slot); + tve->file_ext = g_strdup("prg"); + tve->filetype_desc = g_strdup("TI-81 programs"); + tve->size = info.size; + tve->archived = FALSE; + tve->can_group = FALSE; + + list = g_slist_prepend(list, tve); + } + + dl->list = g_slist_reverse(list); + + if (e && !emu->task_abort) + dl->error_message = get_ti81_error(e); + return (e == 0); +} + +static gboolean get_dirlist_main(TilemCalcEmulator *emu, gpointer data) +{ + switch (emu->calc->hw.model_id) { + case TILEM_CALC_TI81: + return get_dirlist_ti81(emu, data); + + case TILEM_CALC_TI82: + case TILEM_CALC_TI85: + return get_dirlist_nonsilent(emu, data); + + default: + return get_dirlist_silent(emu, data); + } +} + +static void get_dirlist_finished(TilemCalcEmulator *emu, gpointer data, + gboolean cancelled) +{ + GSList *l; + struct dirlistinfo *dl = data; + + if (dl->error_message && !cancelled) + show_error(emu, "Unable to receive variable list", + dl->error_message); + else if (!cancelled && !dl->aborted && emu->ewin && !dl->no_gui) { + if (!emu->rcvdlg) + emu->rcvdlg = tilem_receive_dialog_new(emu); + tilem_receive_dialog_update(emu->rcvdlg, dl->list); + dl->list = NULL; + } + + if (!dl->no_gui && emu->rcvdlg) + emu->rcvdlg->refresh_pending = FALSE; + + for (l = dl->list; l; l = l->next) + tilem_var_entry_free(l->data); + + g_slist_free(dl->list); + g_slice_free(struct dirlistinfo, dl); +} + +void tilem_link_get_dirlist(TilemCalcEmulator *emu) +{ + struct dirlistinfo *dl = g_slice_new0(struct dirlistinfo); + tilem_calc_emulator_begin(emu, &get_dirlist_main, + &get_dirlist_finished, dl); +} + +/**************** Receiving files ****************/ + +static gboolean write_output(FileContent **vars, FlashContent **apps, + const char *filename, gboolean output_tig, + char **error_message) +{ + FileContent *group = NULL; + TigContent *tig = NULL; + int e, nvars, napps; + + for (nvars = 0; vars && vars[nvars]; nvars++) + ; + for (napps = 0; apps && apps[napps]; napps++) + ; + + g_return_val_if_fail(nvars > 0 || napps > 0, FALSE); + + if (output_tig) { + e = tifiles_tigroup_contents(vars, apps, &tig); + if (!e) + e = tifiles_file_write_tigroup(filename, tig); + } + else if (nvars > 1 && napps == 0) { + e = tifiles_group_contents(vars, &group); + if (!e) + e = tifiles_file_write_regular(filename, group, NULL); + } + else if (nvars == 0 && napps == 1) { + e = tifiles_file_write_flash(filename, apps[0]); + } + else if (nvars == 1 && napps == 0) { + e = tifiles_file_write_regular(filename, vars[0], NULL); + } + else { + *error_message = g_strdup + ("Applications cannot be saved in an XXg group" + " file. Try using TIG format or saving apps" + " individually."); + return FALSE; + } + + if (e) + *error_message = get_tilibs_error(e); + + if (tig) + tifiles_content_delete_tigroup(tig); + if (group) + tifiles_content_delete_regular(group); + + return (e == 0); +} + +static gboolean receive_files_silent(TilemCalcEmulator* emu, + struct TilemReceiveFileInfo *rf) +{ + CableHandle *cbl; + CalcHandle *ch; + FileContent **vars, *filec; + FlashContent **apps, *flashc; + GSList *l; + TilemVarEntry *tve; + int e, i, nvars, napps; + + g_return_val_if_fail(rf->entries != NULL, FALSE); + + i = g_slist_length(rf->entries); + + vars = g_new0(FileContent *, i + 1); + apps = g_new0(FlashContent *, i + 1); + nvars = napps = 0; + + begin_link(emu, &cbl, &ch, "Receiving variables"); + + for (l = rf->entries; l; l = l->next) { + tve = l->data; + + if (tve->ve->type == tifiles_flash_type(ch->model)) { + flashc = tifiles_content_create_flash(ch->model); + e = ticalcs_calc_recv_app(ch, flashc, tve->ve); + apps[napps++] = flashc; + } + else { + filec = tifiles_content_create_regular(ch->model); + e = ticalcs_calc_recv_var(ch, MODE_NORMAL, + filec, tve->ve); + vars[nvars++] = filec; + } + if (e) + break; + } + + if (e && !emu->task_abort) + rf->error_message = get_tilibs_error(e); + + end_link(emu, cbl, ch); + + if (!e) + e = !write_output(vars, apps, rf->destination, + rf->output_tig, &rf->error_message); + + for (i = 0; i < nvars; i++) + tifiles_content_delete_regular(vars[i]); + for (i = 0; i < napps; i++) + tifiles_content_delete_flash(apps[i]); + + return (e == 0); +} + +static gboolean receive_files_ti81(TilemCalcEmulator* emu, + struct TilemReceiveFileInfo *rf) +{ + TilemVarEntry *tve; + TI81Program *prgm = NULL; + int e; + FILE *f; + char *dname; + + g_return_val_if_fail(rf->entries != NULL, FALSE); + + if (rf->entries->next) { + rf->error_message = g_strdup + ("TI-81 programs cannot be saved in a group file." + " Try saving programs individually."); + return FALSE; + } + + tve = rf->entries->data; + e = ti81_get_program(emu->calc, tve->slot, &prgm); + if (e) { + rf->error_message = get_ti81_error(e); + return FALSE; + } + + f = g_fopen(rf->destination, "wb"); + if (!f) { + e = errno; + dname = g_filename_display_basename(rf->destination); + rf->error_message = g_strdup_printf + ("Failed to open %s for writing: %s", + dname, g_strerror(e)); + g_free(dname); + ti81_program_free(prgm); + return FALSE; + } + + e = ti81_write_prg_file(f, prgm); + if (fclose(f) || e) { + e = errno; + dname = g_filename_display_basename(rf->destination); + rf->error_message = g_strdup_printf + ("Error writing %s: %s", + dname, g_strerror(e)); + g_free(dname); + ti81_program_free(prgm); + return FALSE; + } + + ti81_program_free(prgm); + return TRUE; +} + +static gboolean receive_files_nonsilent(TilemCalcEmulator *emu, + struct TilemReceiveFileInfo *rf) +{ + const TilemVarEntry *tve; + FileContent **vars, *fc; + int i, nvars; + GSList *l; + gboolean status; + + nvars = g_slist_length(rf->entries); + + vars = g_new0(FileContent *, nvars + 1); + i = 0; + for (l = rf->entries; l; l = l->next) { + tve = l->data; + g_return_val_if_fail(tve->ve != NULL, FALSE); + g_return_val_if_fail(tve->ve->data != NULL, FALSE); + + /* avoid copying variable data */ + fc = tifiles_content_create_regular(get_calc_model(emu->calc)); + fc->entries = g_new(VarEntry *, 1); + fc->num_entries = 1; + fc->entries[0] = tve->ve; + vars[i++] = fc; + } + + status = write_output(vars, NULL, rf->destination, rf->output_tig, + &rf->error_message); + + for (i = 0; i < nvars; i++) { + if (vars[i]) { + vars[i]->num_entries = 0; + g_free(vars[i]->entries); + vars[i]->entries = NULL; + tifiles_content_delete_regular(vars[i]); + } + } + g_free(vars); + + return status; +} + +static gboolean receive_files_main(TilemCalcEmulator *emu, gpointer data) +{ + struct TilemReceiveFileInfo *rf = data; + TilemVarEntry *tve; + + g_return_val_if_fail(rf->entries != NULL, FALSE); + + tve = rf->entries->data; + + if (emu->calc->hw.model_id == TILEM_CALC_TI81) + return receive_files_ti81(emu, rf); + else if (tve->ve && tve->ve->data) + return receive_files_nonsilent(emu, rf); + else + return receive_files_silent(emu, rf); +} + +static void receive_files_finished(TilemCalcEmulator *emu, gpointer data, + gboolean cancelled) +{ + struct TilemReceiveFileInfo *rf = data; + GSList *l; + + if (rf->error_message && !cancelled) + show_error(emu, "Unable to save file", rf->error_message); + + g_free(rf->destination); + g_free(rf->error_message); + for (l = rf->entries; l; l = l->next) + tilem_var_entry_free(l->data); + g_slist_free(rf->entries); + g_slice_free(struct TilemReceiveFileInfo, rf); +} + +void tilem_link_receive_group(TilemCalcEmulator *emu, + GSList *entries, + const char *destination) +{ + struct TilemReceiveFileInfo *rf; + GSList *l; + TilemVarEntry *tve; + const char *p; + gboolean output_tig = FALSE; + + g_return_if_fail(emu != NULL); + g_return_if_fail(emu->calc != NULL); + g_return_if_fail(entries != NULL); + g_return_if_fail(destination != NULL); + + for (l = entries; l; l = l->next) { + tve = l->data; + g_return_if_fail(emu->calc->hw.model_id == tve->model); + } + + p = strrchr(destination, '.'); + if (p && (!g_ascii_strcasecmp(p, ".tig") + || !g_ascii_strcasecmp(p, ".zip"))) + output_tig = TRUE; + + rf = g_slice_new0(struct TilemReceiveFileInfo); + rf->destination = g_strdup(destination); + rf->output_tig = output_tig; + + tve = entries->data; + if (tve->ve && tve->ve->data) { + /* non-silent: we already have the data; save the file + right now */ + rf->entries = entries; + receive_files_nonsilent(emu, rf); + rf->entries = NULL; + receive_files_finished(emu, rf, FALSE); + } + else { + /* silent: retrieve and save data in background */ + for (l = entries; l; l = l->next) { + tve = tilem_var_entry_copy(l->data); + rf->entries = g_slist_prepend(rf->entries, tve); + } + rf->entries = g_slist_reverse(rf->entries); + + tilem_calc_emulator_begin(emu, &receive_files_main, + &receive_files_finished, rf); + } +} + +void tilem_link_receive_file(TilemCalcEmulator *emu, + const TilemVarEntry *varentry, + const char* destination) +{ + GSList *l; + l = g_slist_prepend(NULL, (gpointer) varentry); + tilem_link_receive_group(emu, l, destination); + g_slist_free(l); +} + +/**************** Receive matching files ****************/ + +struct recmatchinfo { + char *pattern; + char *destdir; + struct dirlistinfo *dl; + struct TilemReceiveFileInfo *rf; +}; + +static gboolean receive_matching_main(TilemCalcEmulator *emu, gpointer data) +{ + struct recmatchinfo *rm = data; + GSList *l, *selected = NULL; + TilemVarEntry *tve; + char *defname, *defname_r, *defname_l; + GPatternSpec *pat; + gboolean status = TRUE; + + if (!get_dirlist_main(emu, rm->dl)) + return FALSE; + + /* Find variables that match the pattern */ + + pat = g_pattern_spec_new(rm->pattern); + + for (l = rm->dl->list; l; l = l->next) { + tve = l->data; + + defname = get_default_filename(tve); + defname_r = g_utf8_normalize(defname, -1, G_NORMALIZE_NFKD); + + if (g_pattern_match(pat, strlen(defname_r), defname_r, NULL)) + selected = g_slist_prepend(selected, tve); + + g_free(defname); + g_free(defname_r); + } + + g_pattern_spec_free(pat); + + if (!selected) { + rm->rf->error_message = g_strdup_printf + ("Variable %s not found", rm->pattern); + return FALSE; + } + + /* Receive variables */ + + selected = g_slist_reverse(selected); + + for (l = selected; l; l = l->next) { + tve = l->data; + + g_free(rm->rf->destination); + + defname = get_default_filename(tve); + defname_l = utf8_to_filename(defname); + rm->rf->destination = g_build_filename(rm->destdir, + defname_l, NULL); + g_free(defname); + g_free(defname_l); + + rm->rf->entries = g_slist_prepend(NULL, tve); + status = receive_files_main(emu, rm->rf); + g_slist_free(rm->rf->entries); + rm->rf->entries = NULL; + + if (!status) + break; + } + + g_slist_free(selected); + + return status; +} + +static void receive_matching_finished(TilemCalcEmulator *emu, gpointer data, + gboolean cancelled) +{ + struct recmatchinfo *rm = data; + + get_dirlist_finished(emu, rm->dl, cancelled); + receive_files_finished(emu, rm->rf, cancelled); + g_free(rm->pattern); + g_free(rm->destdir); + g_slice_free(struct recmatchinfo, rm); +} + +/* Receive variables with names matching a pattern. PATTERN is a + glob-like pattern in UTF-8. Files will be written out to + DESTDIR. */ +void tilem_link_receive_matching(TilemCalcEmulator *emu, + const char *pattern, + const char *destdir) +{ + struct recmatchinfo *rm; + + g_return_if_fail(emu != NULL); + g_return_if_fail(pattern != NULL); + g_return_if_fail(destdir != NULL); + + rm = g_slice_new0(struct recmatchinfo); + rm->pattern = g_utf8_normalize(pattern, -1, G_NORMALIZE_NFKD); + rm->destdir = g_strdup(destdir); + + rm->dl = g_slice_new0(struct dirlistinfo); + rm->dl->no_gui = TRUE; + rm->rf = g_slice_new0(struct TilemReceiveFileInfo); + + tilem_calc_emulator_begin(emu, &receive_matching_main, + &receive_matching_finished, rm); +} diff --git a/tool/tilem-src/gui/macro.c b/tool/tilem-src/gui/macro.c new file mode 100644 index 0000000..ae8db8b --- /dev/null +++ b/tool/tilem-src/gui/macro.c @@ -0,0 +1,274 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "filedlg.h" +#include "emucore.h" + + +/* Allocate a new TilemMacro structure which is empty */ +static TilemMacro* tilem_macro_new() { + TilemMacro *macro = g_new(TilemMacro, 1); + macro->n = 0; + macro->actions = NULL; + return macro; +} + +/* New or renew the table of actions (each TilemMacroAtom is an action) */ +static TilemMacroAtom** tilem_macro_actions_new(TilemMacro *macro, int n) { + TilemMacroAtom **atom; + + if(n == 0) { + atom = g_new(TilemMacroAtom*, n); + } else { + atom = g_renew(TilemMacroAtom*, macro->actions, n); + } + return atom; +} + +/* Try to destroy the TilemMacro if really allocated */ +static void tilem_macro_finalize(TilemMacro* macro) { + if(macro) { + if(macro->actions) + g_free(macro->actions); + g_free(macro); + } +} + +/* Firstly free the memory then create a new TilemMacro */ +void tilem_macro_start(TilemCalcEmulator *emu) { + emu->isMacroRecording = TRUE; + + /* Firstly destroy the macro if exists */ + tilem_macro_finalize(emu->macro); + + /* Then allocate a new one */ + emu->macro = tilem_macro_new(emu); +} + +/* Add an action to the macro. The action could be : + * A keypress (type == 0) + * A file load (type == 1) + * Or something else if I implement it :) + */ +void tilem_macro_add_action(TilemMacro* macro, int type, char * value) { + + int n = macro->n; + + /* We want to allocate for 1 object, but index is 0 */ + macro->actions = tilem_macro_actions_new(macro, n + 1); + + /* Then we need to save the action */ + macro->actions[n] = g_new(char, strlen(value)); /* FIXME : gcc says : "assignment from incompatible pointer type" ??? */ + macro->actions[n]->value = g_strdup(value); + macro->actions[n]->type = type; + macro->n++; +} + +/* Stop the macro */ +void tilem_macro_stop(TilemCalcEmulator *emu) +{ + if(emu->isMacroRecording) + emu->isMacroRecording = FALSE; +} + +/* Print the macro actions content (debug) */ +void tilem_macro_print(TilemMacro *macro) { + int i = 0; + + printf("macro->n : %d\n", macro->n); + for(i = 0; i < macro->n; i++ ){ + printf("type : %d value : %s\n", macro->actions[i]->type, macro->actions[i]->value); + } +} + +/* Write a file using TilemMacro structure */ +void tilem_macro_write_file(TilemCalcEmulator *emu) { + char *dir, *filename; + tilem_config_get("macro", + "directory/f", &dir, + NULL); + + filename = prompt_save_file("Save macro", + GTK_WINDOW(emu->ewin->window), + NULL, + dir, + "Macro files", "*.txt", + "All files", "*", + NULL); + if(filename) { + FILE * fp = g_fopen(filename, "w"); + if(fp) { + int i = 0; + for(i = 0; i< emu->macro->n; i++ ){ + printf("type : %d value : %s\n", emu->macro->actions[i]->type, emu->macro->actions[i]->value); + /* Test if it's a key press or a file loading action */ + if(emu->macro->actions[i]->type == 1) { + char * lengthchar = g_new0(char, 4); + int length = strlen(emu->macro->actions[i]->value); + fwrite("file=", 1, 5, fp); + sprintf(lengthchar, "%04d", strlen(emu->macro->actions[i]->value)); + fwrite(lengthchar, 1, sizeof(int), fp); + fwrite("-", 1, 1, fp); + fwrite(emu->macro->actions[i]->value, 1, length, fp); + g_free(lengthchar); + } else { + fwrite(emu->macro->actions[i]->value, 1, 4, fp); + fwrite(",", 1, 1, fp); + } + } + tilem_config_set("macro", "directory/f", g_path_get_dirname(filename), NULL); + fclose(fp); + } + g_free(filename); + g_free(dir); + } +} + +#define MACRO_KEYPRESS 0 +#define MACRO_FILE 1 + +/* Play the macro (macro should be created or loaded before else it does nothing) */ +static gboolean tilem_macro_play_main(TilemCalcEmulator *emu, G_GNUC_UNUSED gpointer data) { + + if(!emu->macro) { + printf("Nothing to play\n"); + return FALSE; + } + + int i; + for(i = 0; i < emu->macro->n; i++ ){ + if(emu->macro->actions[i]->type == MACRO_FILE) { + /* Type == 1 is load file */ + struct TilemSendFileInfo *sf; + sf = g_slice_new0(struct TilemSendFileInfo); + sf->filename = g_strdup(emu->macro->actions[i]->value); + sf->display_name = g_filename_display_basename(emu->macro->actions[i]->value); + sf->slot = -1; + sf->first = TRUE; + sf->last = TRUE; + send_file_main(emu, sf); + + } else { + /* type == 0 is keypress */ + int code = atoi(emu->macro->actions[i]->value); + tilem_em_unlock(emu); + run_with_key_slowly(emu->calc, code); + tilem_em_lock(emu); + + } + } + + + return TRUE; +} + + +static void tilem_macro_play_finished(G_GNUC_UNUSED TilemCalcEmulator *emu, G_GNUC_UNUSED gpointer data, + G_GNUC_UNUSED gboolean cancelled) { + +} + +/* Play the macro */ +void tilem_macro_play(TilemCalcEmulator* emu) { + + tilem_calc_emulator_begin(emu, &tilem_macro_play_main, &tilem_macro_play_finished, NULL); +} + +/* Load a macro (when finished, task manager will normally call tilem_macro_play) */ +static gboolean tilem_macro_load_main(TilemCalcEmulator* emu, gpointer data) { + + char* filename = (char*) data; + char c = 'a'; + + if(filename) { + FILE * fp = g_fopen(filename, "r"); + /* printf("filename : %s\n", filename); */ + + tilem_macro_start(emu); + while(c != EOF) { + char* codechar = g_new0(char, 4); + fread(codechar, 1, 4, fp); + if(strcmp(codechar, "file") == 0) { + c = fgetc(fp); /* Drop the "="*/ + char *lengthchar = g_new0(char, 4); + fread(lengthchar, 1, 4, fp); + c = fgetc(fp); /* Drop the "-"*/ + int length = atoi(lengthchar); + char* filetoload= g_new0(char, length); + fread(filetoload, 1, length, fp); + tilem_macro_add_action(emu->macro, 1, filetoload); + g_free(lengthchar); + g_free(filetoload); + } else { + int code = atoi(codechar); + if(code <= 0) + break; + /*printf("code : %d, codechar : %s\n",code, codechar); */ + tilem_macro_add_action(emu->macro, 0, codechar); + c = fgetc(fp); + } + } + tilem_macro_stop(emu); + fclose(fp); + } + + return TRUE; +} + +/* When the macro is totally loaded, then we can play it ! */ +static void tilem_macro_load_finished(G_GNUC_UNUSED TilemCalcEmulator *emu, G_GNUC_UNUSED gpointer data, + G_GNUC_UNUSED gboolean cancelled) +{ + tilem_calc_emulator_begin(emu, &tilem_macro_play_main, &tilem_macro_play_finished, NULL); +} + +/* Load a macro from filename. + * If filename == NULL prompt the user + */ +void tilem_macro_load(TilemCalcEmulator *emu, char* filename) { + + /* printf("tilem_macro_load : filename : %s\n", filename); */ + if(!filename) { + char *dir; + tilem_config_get("macro", "directory/f", &dir, NULL); + + filename = prompt_open_file("Open macro", + GTK_WINDOW(emu->ewin->window), + dir, + "Macro files", "*.txt", + "All files", "*", + NULL); + if(dir) + g_free(dir); + } + tilem_calc_emulator_begin(emu, &tilem_macro_load_main, &tilem_macro_load_finished, filename); +} + diff --git a/tool/tilem-src/gui/memmodel.c b/tool/tilem-src/gui/memmodel.c new file mode 100644 index 0000000..eefe43d --- /dev/null +++ b/tool/tilem-src/gui/memmodel.c @@ -0,0 +1,529 @@ +/* + * TilEm II + * + * Copyright (c) 2011-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "gui.h" +#include "memmodel.h" +#include "charmap.h" + +/* GTK+ requires us to supply several property values for every byte, + and we might have hundreds of bytes that need to be refreshed each + time the window is repainted. To avoid locking and unlocking the + calc for every call to tilem_mem_model_get_value(), we can retrieve + information for an entire block of memory and keep it in cache. + + For each address, we cache the current byte value (8 bits), whether + or not it is editable (1 bit), and its physical address (up to 22 + bits for current calculator models), so everything fits in a + guint32. For future models, this scheme might need to be + modified. */ + +#define CACHE_BLOCK_SIZE 256 +#define CACHE_NUM_BLOCKS 16 + +typedef struct { + dword address; + guint32 *info; +} MemModelCacheBlock; + +/* Check if a given physical address is editable (i.e., located in RAM + or in a non-protected Flash sector) */ +static gboolean address_editable(TilemCalc *calc, dword a) +{ + int start, end, i; + + if (a >= calc->hw.romsize) + /* address is in RAM */ + return TRUE; + + if (!(calc->hw.flags & TILEM_CALC_HAS_FLASH)) + /* calc does not use Flash */ + return FALSE; + + /* address is in Flash -> check if sector is protected */ + start = 0; + end = calc->hw.nflashsectors; + while (start < end) { + i = (start + end) / 2; + if (a < calc->hw.flashsectors[i].start) + end = i; + else if (a >= (calc->hw.flashsectors[i].start + + calc->hw.flashsectors[i].size)) + start = i + 1; + else + return !(calc->hw.flashsectors[i].protectgroup + & ~calc->flash.overridegroup); + } + + g_return_val_if_reached(FALSE); +} + +/* Copy calc memory contents into cache. */ +static void fill_cache_block(TilemMemModel *mm, MemModelCacheBlock *cb) +{ + TilemCalc *calc; + dword i, addr, phys; + byte value, editable; + + g_return_if_fail(mm->emu != NULL); + + tilem_calc_emulator_lock(mm->emu); + calc = mm->emu->calc; + if (!calc) { + tilem_calc_emulator_unlock(mm->emu); + return; + } + + for (i = 0; i < CACHE_BLOCK_SIZE; i++) { + addr = (cb->address + i) % mm->wrap_addr; + + if (mm->use_logical) + phys = (*calc->hw.mem_ltop)(calc, addr); + else + phys = addr; + + editable = address_editable(calc, phys); + value = calc->mem[phys]; + + cb->info[i] = (value + | (editable << 8) + | (phys << 9)); + } + + tilem_calc_emulator_unlock(mm->emu); +} + +/* Retrieve info for given address. */ +static guint32 get_mem_info(TilemMemModel *mm, dword addr) +{ + GList *l; + MemModelCacheBlock *cb; + dword start, index; + + start = addr & ~(CACHE_BLOCK_SIZE - 1); + index = addr & (CACHE_BLOCK_SIZE - 1); + + for (l = mm->cache->head; l; l = l->next) { + cb = l->data; + if (cb->address == start) { + if (l->prev) { + /* Move this cache block to the start + of the list */ + g_queue_unlink(mm->cache, l); + g_queue_push_head_link(mm->cache, l); + } + + return cb->info[index]; + } + } + + /* Data not found in cache; drop the least recently used block + and retrieve the requested block from the calc */ + l = g_queue_pop_tail_link(mm->cache); + g_queue_push_head_link(mm->cache, l); + cb = l->data; + cb->address = start; + fill_cache_block(mm, cb); + return cb->info[index]; +} + +/* Get address's byte value. */ +static byte get_value(TilemMemModel *mm, dword addr) +{ + return (get_mem_info(mm, addr) & 0xff); +} + +/* Get address's editability. */ +static gboolean get_editable(TilemMemModel *mm, dword addr) +{ + return ((get_mem_info(mm, addr) >> 8) & 1); +} + +/* Get address's corresponding physical address. */ +static dword get_phys_addr(TilemMemModel *mm, dword addr) +{ + return (get_mem_info(mm, addr) >> 9); +} + +/* Clear cache. This function should be called any time something + happens that might affect memory contents. */ +void tilem_mem_model_clear_cache(TilemMemModel *mm) +{ + GList *l; + MemModelCacheBlock *cb; + + g_return_if_fail(TILEM_IS_MEM_MODEL(mm)); + + for (l = mm->cache->head; l; l = l->next) { + cb = l->data; + cb->address = (dword) -1; + } +} + +/* Get flags for the model */ +static GtkTreeModelFlags +tilem_mem_model_get_flags(G_GNUC_UNUSED GtkTreeModel *model) +{ + return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST); +} + +/* Get the number of columns */ +static int +tilem_mem_model_get_n_columns(GtkTreeModel *model) +{ + TilemMemModel *mm; + g_return_val_if_fail(TILEM_IS_MEM_MODEL(model), 0); + mm = TILEM_MEM_MODEL(model); + return (MM_COLUMNS_PER_BYTE * mm->row_size); +} + +/* Get type of data for the given column. Currently all columns are + strings. */ +static GType +tilem_mem_model_get_column_type(G_GNUC_UNUSED GtkTreeModel *model, + int index) +{ + index %= MM_COLUMNS_PER_BYTE; + + switch (index) { + case MM_COL_ADDRESS_0: + case MM_COL_HEX_0: + case MM_COL_CHAR_0: + return G_TYPE_STRING; + + case MM_COL_BYTE_PTR_0: + return G_TYPE_POINTER; + + case MM_COL_EDITABLE_0: + return G_TYPE_BOOLEAN; + + default: + g_return_val_if_reached(G_TYPE_INVALID); + } +} + +/* Get an iterator pointing to the nth row */ +static gboolean get_nth_iter(GtkTreeModel *model, GtkTreeIter *iter, int n) +{ + TilemMemModel *mm; + + g_return_val_if_fail(TILEM_IS_MEM_MODEL(model), FALSE); + mm = TILEM_MEM_MODEL(model); + + if (n >= mm->num_rows) + return FALSE; + + iter->stamp = mm->stamp; + iter->user_data = GINT_TO_POINTER(n); + iter->user_data2 = NULL; + iter->user_data3 = NULL; + return TRUE; +} + +/* Get row number for the given iterator */ +static int get_row_number(GtkTreeModel *model, GtkTreeIter *iter) +{ + TilemMemModel *mm; + int n; + + g_return_val_if_fail(TILEM_IS_MEM_MODEL(model), 0); + mm = TILEM_MEM_MODEL(model); + g_return_val_if_fail(iter != NULL, 0); + g_return_val_if_fail(iter->stamp == mm->stamp, 0); + n = GPOINTER_TO_INT(iter->user_data); + g_return_val_if_fail(n < mm->num_rows, 0); + return n; +} + +/* Get iterator for a given path */ +static gboolean tilem_mem_model_get_iter(GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + int *indices; + + if (gtk_tree_path_get_depth(path) != 1) + return FALSE; + + indices = gtk_tree_path_get_indices(path); + return get_nth_iter(model, iter, indices[0]); +} + +/* Get path for an iterator */ +static GtkTreePath * tilem_mem_model_get_path(GtkTreeModel *model, + GtkTreeIter *iter) +{ + int n; + n = get_row_number(model, iter); + return gtk_tree_path_new_from_indices(n, -1); +} + +/* Get next (sibling) iterator */ +static gboolean tilem_mem_model_iter_next(GtkTreeModel *model, + GtkTreeIter *iter) +{ + int n; + n = get_row_number(model, iter); + return get_nth_iter(model, iter, n + 1); +} + +/* Check if iterator has a child */ +static gboolean +tilem_mem_model_iter_has_child(G_GNUC_UNUSED GtkTreeModel *model, + G_GNUC_UNUSED GtkTreeIter *iter) +{ + return FALSE; +} + +/* Get number of children (iter = NULL means get number of root + nodes) */ +static gint tilem_mem_model_iter_n_children(GtkTreeModel *model, + GtkTreeIter *iter) +{ + TilemMemModel *mm; + + g_return_val_if_fail(TILEM_IS_MEM_MODEL(model), 0); + mm = TILEM_MEM_MODEL(model); + + if (iter) + return 0; + else + return (mm->num_rows); +} + +/* Get nth child (parent = NULL means get nth root node */ +static gboolean tilem_mem_model_iter_nth_child( GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + G_GNUC_UNUSED TilemMemModel* mm; + + g_return_val_if_fail(TILEM_IS_MEM_MODEL(model), FALSE); + mm = TILEM_MEM_MODEL(model); + + if (parent) + return FALSE; + else + return get_nth_iter(model, iter, n); +} + +/* Get first child */ +static gboolean tilem_mem_model_iter_children(GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *parent) +{ + return tilem_mem_model_iter_nth_child(model, iter, parent, 0); +} + +/* Get parent */ +static gboolean tilem_mem_model_iter_parent(G_GNUC_UNUSED GtkTreeModel *model, + G_GNUC_UNUSED GtkTreeIter *iter, + G_GNUC_UNUSED GtkTreeIter *child) +{ + return FALSE; +} + +/* Retrieve value for a given column */ +static void tilem_mem_model_get_value(GtkTreeModel *model, + GtkTreeIter *iter, + gint column, + GValue *value) +{ + TilemMemModel *mm; + dword n, addr, phys; + TilemCalc *calc; + char buf[100], *s; + + g_return_if_fail(TILEM_IS_MEM_MODEL(model)); + mm = TILEM_MEM_MODEL(model); + + g_return_if_fail(mm->emu != NULL); + g_return_if_fail(mm->emu->calc != NULL); + + n = get_row_number(model, iter); + + calc = mm->emu->calc; + + addr = (mm->start_addr + + n * mm->row_size + + column / MM_COLUMNS_PER_BYTE) % mm->wrap_addr; + + column %= MM_COLUMNS_PER_BYTE; + + switch (column) { + case MM_COL_ADDRESS_0: + s = tilem_format_addr(mm->emu->dbg, addr, !mm->use_logical); + g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, s); + g_free(s); + break; + + case MM_COL_HEX_0: + g_snprintf(buf, sizeof(buf), "%02X", get_value(mm, addr)); + g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, buf); + break; + + case MM_COL_CHAR_0: + s = ti_to_unicode(calc->hw.model_id, get_value(mm, addr)); + g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, s); + g_free(s); + break; + + case MM_COL_BYTE_PTR_0: + phys = get_phys_addr(mm, addr); + g_value_init(value, G_TYPE_POINTER); + g_value_set_pointer(value, &calc->mem[phys]); + break; + + case MM_COL_EDITABLE_0: + g_value_init(value, G_TYPE_BOOLEAN); + g_value_set_boolean(value, get_editable(mm, addr)); + break; + } +} + +static void tilem_mem_model_init(TilemMemModel *mm) +{ + int i; + MemModelCacheBlock *cb; + + mm->stamp = g_random_int(); + mm->row_size = 1; + + mm->cache = g_queue_new(); + for (i = 0; i < CACHE_NUM_BLOCKS; i++) { + cb = g_slice_new(MemModelCacheBlock); + cb->address = (dword) -1; + cb->info = g_new(guint32, CACHE_BLOCK_SIZE); + g_queue_push_head(mm->cache, cb); + } +} + +static void tilem_mem_model_finalize(GObject *obj) +{ + TilemMemModel *mm; + MemModelCacheBlock *cb; + + g_return_if_fail(TILEM_IS_MEM_MODEL(obj)); + mm = TILEM_MEM_MODEL(obj); + + while ((cb = g_queue_pop_head(mm->cache))) { + g_free(cb->info); + g_slice_free(MemModelCacheBlock, cb); + } +} + +static void tilem_mem_model_class_init(TilemMemModelClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + obj_class->finalize = &tilem_mem_model_finalize; +} + +static void tilem_mem_tree_model_init(GtkTreeModelIface *iface) +{ + iface->get_flags = &tilem_mem_model_get_flags; + iface->get_n_columns = &tilem_mem_model_get_n_columns; + iface->get_column_type = &tilem_mem_model_get_column_type; + iface->get_iter = &tilem_mem_model_get_iter; + iface->get_path = &tilem_mem_model_get_path; + iface->get_value = &tilem_mem_model_get_value; + iface->iter_next = &tilem_mem_model_iter_next; + iface->iter_children = &tilem_mem_model_iter_children; + iface->iter_has_child = &tilem_mem_model_iter_has_child; + iface->iter_n_children = &tilem_mem_model_iter_n_children; + iface->iter_nth_child = &tilem_mem_model_iter_nth_child; + iface->iter_parent = &tilem_mem_model_iter_parent; +} + +GType tilem_mem_model_get_type(void) +{ + static GType type = 0; + + static const GTypeInfo type_info = { + sizeof(TilemMemModelClass), + NULL, + NULL, + (GClassInitFunc) tilem_mem_model_class_init, + NULL, + NULL, + sizeof(TilemMemModel), + 0, + (GInstanceInitFunc) tilem_mem_model_init, + NULL + }; + + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) tilem_mem_tree_model_init, + NULL, + NULL + }; + + if (!type) { + type = g_type_register_static(G_TYPE_OBJECT, "TilemMemModel", + &type_info, 0); + g_type_add_interface_static(type, GTK_TYPE_TREE_MODEL, + &tree_model_info); + } + + return type; +} + +GtkTreeModel * tilem_mem_model_new(TilemCalcEmulator* emu, + int rowsize, dword start, + gboolean logical) +{ + TilemMemModel* mm; + + g_return_val_if_fail(emu != NULL, NULL); + g_return_val_if_fail(emu->calc != NULL, NULL); + g_return_val_if_fail(rowsize > 0, NULL); + + mm = g_object_new(TILEM_TYPE_MEM_MODEL, NULL); + + mm->emu = emu; + mm->row_size = rowsize; + mm->start_addr = start; + + if (logical) { + mm->use_logical = TRUE; + mm->wrap_addr = 0x10000; + } + else { + mm->use_logical = FALSE; + mm->wrap_addr = (emu->calc->hw.romsize + + emu->calc->hw.ramsize); + } + + mm->num_rows = (mm->wrap_addr + rowsize - 1) / rowsize; + + return GTK_TREE_MODEL(mm); +} diff --git a/tool/tilem-src/gui/memmodel.h b/tool/tilem-src/gui/memmodel.h new file mode 100644 index 0000000..58f84f4 --- /dev/null +++ b/tool/tilem-src/gui/memmodel.h @@ -0,0 +1,77 @@ +/* + * TilEm II + * + * Copyright (c) 2011-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +/* TilemMemModel object: an efficient GtkTreeModel interface for + viewing and editing calculator's memory */ + +G_BEGIN_DECLS + +/* Column numbers (repeated for each byte in a given row) */ +enum { + MM_COL_ADDRESS_0 = 0, /* Address (hexadecimal) */ + MM_COL_HEX_0, /* Byte value (hexadecimal) */ + MM_COL_CHAR_0, /* Byte value (character, converted to + UTF-8) */ + MM_COL_BYTE_PTR_0, /* Pointer to corresponding memory byte */ + MM_COL_EDITABLE_0, /* TRUE if byte is editable */ + MM_COLUMNS_PER_BYTE +}; + +#define MM_COL_ADDRESS(n) (MM_COL_ADDRESS_0 + (n) * MM_COLUMNS_PER_BYTE) +#define MM_COL_HEX(n) (MM_COL_HEX_0 + (n) * MM_COLUMNS_PER_BYTE) +#define MM_COL_CHAR(n) (MM_COL_CHAR_0 + (n) * MM_COLUMNS_PER_BYTE) +#define MM_COL_BYTE_PTR(n) (MM_COL_BYTE_PTR_0 + (n) * MM_COLUMNS_PER_BYTE) +#define MM_COL_EDITABLE(n) (MM_COL_EDITABLE_0 + (n) * MM_COLUMNS_PER_BYTE) + +/* GObject stuff */ + +#define TILEM_TYPE_MEM_MODEL (tilem_mem_model_get_type()) +#define TILEM_MEM_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TILEM_TYPE_MEM_MODEL, TilemMemModel)) +#define TILEM_MEM_MODEL_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls), TILEM_TYPE_MEM_MODEL, TilemMemModelClass)) +#define TILEM_IS_MEM_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TILEM_TYPE_MEM_MODEL)) +#define TILEM_IS_MEM_MODEL_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE((cls), TILEM_TYPE_MEM_MODEL)) +#define TILEM_MEM_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TILEM_TYPE_MEM_MODEL, TilemMemModelClass)) + +typedef struct _TilemMemModel { + GObject parent; + TilemCalcEmulator *emu; + gint stamp; + int row_size; + int num_rows; + dword start_addr; + dword wrap_addr; + gboolean use_logical; + GQueue *cache; +} TilemMemModel; + +typedef struct _TilemMemModelClass { + GObjectClass parent_class; +} TilemMemModelClass; + +GType tilem_mem_model_get_type(void) G_GNUC_CONST; + +GtkTreeModel * tilem_mem_model_new(TilemCalcEmulator* emu, + int rowsize, dword start, + gboolean logical); + +void tilem_mem_model_clear_cache(TilemMemModel *mm); + +G_END_DECLS diff --git a/tool/tilem-src/gui/memory.c b/tool/tilem-src/gui/memory.c new file mode 100644 index 0000000..03b09a1 --- /dev/null +++ b/tool/tilem-src/gui/memory.c @@ -0,0 +1,101 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +/* Memory management */ + +void tilem_free(void* p) +{ + g_free(p); +} + +void* tilem_malloc(size_t s) +{ + return g_malloc(s); +} + +void* tilem_realloc(void* p, size_t s) +{ + return g_realloc(p, s); +} + +void* tilem_try_malloc(size_t s) +{ + return g_try_malloc(s); +} + +void* tilem_malloc0(size_t s) +{ + return g_malloc0(s); +} + +void* tilem_try_malloc0(size_t s) +{ + return g_try_malloc0(s); +} + +void* tilem_malloc_atomic(size_t s) +{ + return g_malloc(s); +} + +void* tilem_try_malloc_atomic(size_t s) +{ + return g_try_malloc(s); +} + +/* Logging */ + +void tilem_message(TilemCalc* calc, const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + fprintf(stderr, "x%c: ", calc->hw.model_id); + vfprintf(stderr, msg, ap); + fputc('\n', stderr); + va_end(ap); +} + +void tilem_warning(TilemCalc* calc, const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + fprintf(stderr, "x%c: WARNING: ", calc->hw.model_id); + vfprintf(stderr, msg, ap); + fputc('\n', stderr); + va_end(ap); +} + +void tilem_internal(TilemCalc* calc, const char* msg, ...) +{ + va_list ap; + va_start(ap, msg); + fprintf(stderr, "x%c: INTERNAL ERROR: ", calc->hw.model_id); + vfprintf(stderr, msg, ap); + fputc('\n', stderr); + va_end(ap); +} diff --git a/tool/tilem-src/gui/memview.c b/tool/tilem-src/gui/memview.c new file mode 100644 index 0000000..28d96bf --- /dev/null +++ b/tool/tilem-src/gui/memview.c @@ -0,0 +1,291 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "memmodel.h" +#include "fixedtreeview.h" + +static int get_column_index(GtkWidget *view, GtkTreeViewColumn *col) +{ + GList *cols; + int i; + + cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(view)); + i = g_list_index(cols, col); + g_list_free(cols); + return -1; +} + +/* Determine current position in the memory view. */ +static void get_mem_view_position(GtkWidget *mem_view, dword *row_addr, + dword *col_addr, gboolean *cur_hex) +{ + GtkTreePath *path; + GtkTreeViewColumn *col; + GtkTreeModel *model; + TilemMemModel *mm; + const int *indices; + int n; + + *row_addr = *col_addr = (dword) -1; + *cur_hex = FALSE; + + gtk_tree_view_get_cursor(GTK_TREE_VIEW(mem_view), &path, &col); + model = gtk_tree_view_get_model(GTK_TREE_VIEW(mem_view)); + if (!TILEM_IS_MEM_MODEL(model)) + return; + + mm = TILEM_MEM_MODEL(model); + + if (!path) + return; + + indices = gtk_tree_path_get_indices(path); + *row_addr = mm->start_addr + indices[0] * mm->row_size; + + n = get_column_index(mem_view, col); + if (n > 0 && n <= mm->row_size) { + *col_addr = *row_addr + n - 1; + *cur_hex = TRUE; + } + else if (n > mm->row_size && n < mm->row_size * 2) { + *col_addr = *row_addr + n - mm->row_size - 1; + *cur_hex = FALSE; + } + + gtk_tree_path_free(path); +} + +static void addr_to_pos(TilemMemModel *mm, dword addr, + int *rownum, int *colnum) +{ + if (addr < mm->start_addr) + addr += mm->wrap_addr; + addr -= mm->start_addr; + if (rownum) *rownum = (addr / mm->row_size); + if (colnum) *colnum = (addr % mm->row_size); +} + +/* Move memory view cursor */ +static void set_mem_view_position(GtkWidget *mem_view, dword row_addr, + dword col_addr, gboolean cur_hex) +{ + int rownum, colnum; + GtkTreePath *path = NULL; + GtkTreeViewColumn *col = NULL; + GtkTreeModel *model; + TilemMemModel *mm; + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(mem_view)); + if (!TILEM_IS_MEM_MODEL(model)) + return; + + mm = TILEM_MEM_MODEL(model); + + if (col_addr != (dword) -1) { + addr_to_pos(mm, col_addr, &rownum, &colnum); + path = gtk_tree_path_new_from_indices(rownum, -1); + + if (!cur_hex) + colnum += mm->row_size; + + col = gtk_tree_view_get_column(GTK_TREE_VIEW(mem_view), + colnum + 1); + } + else if (row_addr != (dword) -1) { + addr_to_pos(mm, row_addr, &rownum, NULL); + path = gtk_tree_path_new_from_indices(rownum, -1); + } + + if (path) { + gtk_tree_view_set_cursor(GTK_TREE_VIEW(mem_view), + path, col, FALSE); + gtk_tree_path_free(path); + } +} + +/* Cell edited in memory view */ +static void hex_cell_edited(GtkCellRendererText *renderer, + gchar *pathstr, gchar *text, + gpointer data) +{ + GtkTreeView *mem_view = data; + TilemDebugger *dbg; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; + byte *bptr = NULL; + int col; + int value; + char *end; + + value = strtol(text, &end, 16); + if (end == text || *end != 0) + return; + + dbg = g_object_get_data(G_OBJECT(mem_view), "tilem-debugger"); + g_return_if_fail(dbg != NULL); + + col = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(renderer), + "tilem-mem-column")); + + model = gtk_tree_view_get_model(mem_view); + path = gtk_tree_path_new_from_string(pathstr); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_path_free(path); + + gtk_tree_model_get(model, &iter, MM_COL_BYTE_PTR(col), &bptr, -1); + g_return_if_fail(bptr != NULL); + + *bptr = (byte) value; + tilem_debugger_refresh(dbg, TRUE); +} + +/* Create the GtkTreeView to show the memory */ +GtkWidget *tilem_debugger_mem_view_new(TilemDebugger *dbg) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkWidget *treeview; + + /* Create the memory list tree view and set title invisible */ + treeview = gtk_tree_view_new(); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + + g_object_set_data(G_OBJECT(treeview), "tilem-debugger", dbg); + + /* Create the columns */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes + ("ADDR", renderer, "text", MM_COL_ADDRESS(0), NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_expand(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + return treeview; +} + +static void create_columns(GtkWidget *mem_view, int width) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + int i; + + for (i = 0; i < width; i++) { + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (NULL, renderer, + "text", MM_COL_HEX(i), + "editable", MM_COL_EDITABLE(i), + NULL); + + g_object_set_data(G_OBJECT(renderer), "tilem-mem-column", + GINT_TO_POINTER(i)); + g_signal_connect(renderer, "edited", + G_CALLBACK(hex_cell_edited), mem_view); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_expand(column, (i == width - 1)); + gtk_tree_view_append_column(GTK_TREE_VIEW(mem_view), column); + } + + for (i = 0; i < width; i++) { + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes + (NULL, renderer, "text", MM_COL_CHAR(i), NULL); + + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_expand(column, (i == width - 1)); + gtk_tree_view_append_column(GTK_TREE_VIEW(mem_view), column); + } +} + +static dword translate_addr(TilemCalcEmulator *emu, dword a, gboolean ptol) +{ + if (!emu->calc || a == (dword) -1) + return a; + if (ptol) + return (*emu->calc->hw.mem_ptol)(emu->calc, a); + else + return (*emu->calc->hw.mem_ltop)(emu->calc, a & 0xffff); +} + +void tilem_debugger_mem_view_configure(GtkWidget *mem_view, + TilemCalcEmulator *emu, + int rowsize, int start, + gboolean logical) +{ + GtkTreeModel *model; + dword row_addr, col_addr; + gboolean cur_hex; + GList *cols, *l; + int old_rowsize; + + get_mem_view_position(mem_view, &row_addr, &col_addr, &cur_hex); + + model = gtk_tree_view_get_model(GTK_TREE_VIEW(mem_view)); + if (TILEM_IS_MEM_MODEL(model) + && TILEM_MEM_MODEL(model)->use_logical != logical) { + tilem_calc_emulator_lock(emu); + row_addr = translate_addr(emu, row_addr, logical); + col_addr = translate_addr(emu, col_addr, logical); + tilem_calc_emulator_unlock(emu); + } + + cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(mem_view)); + old_rowsize = (g_list_length(cols) - 1) / 2; + if (old_rowsize != rowsize) + for (l = g_list_next(cols); l; l = l->next) + gtk_tree_view_remove_column(GTK_TREE_VIEW(mem_view), + l->data); + g_list_free(cols); + + model = tilem_mem_model_new(emu, rowsize, start, logical); + gtk_tree_view_set_model(GTK_TREE_VIEW(mem_view), model); + g_object_unref(model); + + if (old_rowsize != rowsize) + create_columns(mem_view, rowsize); + + fixed_tree_view_init(mem_view, MM_COLUMNS_PER_BYTE, + MM_COL_ADDRESS_0, "DD:DDDD ", + MM_COL_HEX_0, "DD ", + MM_COL_CHAR_0, "M ", + MM_COL_EDITABLE_0, TRUE, + -1); + + set_mem_view_position(mem_view, row_addr, col_addr, cur_hex); +} diff --git a/tool/tilem-src/gui/menu.c b/tool/tilem-src/gui/menu.c new file mode 100644 index 0000000..debf60a --- /dev/null +++ b/tool/tilem-src/gui/menu.c @@ -0,0 +1,318 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2011-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "gui.h" +#include "msgbox.h" + +static void action_send_file(G_GNUC_UNUSED GtkAction *act, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + load_file_dialog(ewin); +} + +static void action_receive_file(G_GNUC_UNUSED GtkAction *act, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + //tilem_rcvmenu_new(ewin->emu); + //get_var(ewin->emu); + popup_receive_menu(ewin); +} + +static void action_start_debugger(G_GNUC_UNUSED GtkAction *act, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + launch_debugger(ewin); +} + +static void action_open_calc(G_GNUC_UNUSED GtkAction *act, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + tilem_calc_emulator_prompt_open_rom(ewin->emu); +} + +static void action_save_calc(G_GNUC_UNUSED GtkAction *act, G_GNUC_UNUSED gpointer data) +{ + TilemEmulatorWindow *ewin = data; + GError *err = NULL; + + if (!tilem_calc_emulator_save_state(ewin->emu, &err)) { + messagebox01(GTK_WINDOW(ewin->window), GTK_MESSAGE_ERROR, + "Unable to save calculator state", + "%s", err->message); + g_error_free(err); + } +} + +static void action_revert_calc(G_GNUC_UNUSED GtkAction *act, G_GNUC_UNUSED gpointer data) +{ + TilemEmulatorWindow *ewin = data; + GError *err = NULL; + + if (!tilem_calc_emulator_revert_state(ewin->emu, &err)) { + messagebox01(GTK_WINDOW(ewin->window), GTK_MESSAGE_ERROR, + "Unable to load calculator state", + "%s", err->message); + g_error_free(err); + } +} + +static void action_reset_calc(G_GNUC_UNUSED GtkAction *act, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + tilem_calc_emulator_reset(ewin->emu); +} + +static void action_begin_macro(G_GNUC_UNUSED GtkAction *act, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + tilem_macro_start(ewin->emu); +} + +static void action_end_macro(G_GNUC_UNUSED GtkAction *act, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + tilem_macro_stop(ewin->emu); + /* tilem_macro_print(ewin->emu->macro); */ +} + +static void action_play_macro(G_GNUC_UNUSED GtkAction *act, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + tilem_macro_play(ewin->emu); +} + +static void action_open_macro(G_GNUC_UNUSED GtkAction *act, G_GNUC_UNUSED gpointer data) +{ + TilemEmulatorWindow *ewin = data; + tilem_macro_load(ewin->emu, NULL); +} + +/* I will improve macro creation by saving it firstly into a macro object + * Save macro will only be done if user choose to save it */ +static void action_save_macro(G_GNUC_UNUSED GtkAction *act, G_GNUC_UNUSED gpointer data) +{ + TilemEmulatorWindow *ewin = data; + tilem_macro_write_file(ewin->emu); +} + +static void action_screenshot(G_GNUC_UNUSED GtkAction *act, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + popup_screenshot_window(ewin); +} + +static void action_quick_screenshot(G_GNUC_UNUSED GtkAction *act, + gpointer data) +{ + TilemEmulatorWindow *ewin = data; + quick_screenshot(ewin); +} + +static void action_preferences(G_GNUC_UNUSED GtkAction *act, + gpointer data) +{ + TilemEmulatorWindow *ewin = data; + tilem_preferences_dialog(ewin); +} + +static void action_about(G_GNUC_UNUSED GtkAction *act, + G_GNUC_UNUSED gpointer data) +{ + show_about(); +} + +static void action_quit(G_GNUC_UNUSED GtkAction *act, + G_GNUC_UNUSED gpointer data) +{ + + TilemEmulatorWindow *ewin = data; + gtk_widget_destroy(ewin->window); +} + +static const GtkActionEntry main_action_ents[] = + {{ "send-file", + GTK_STOCK_OPEN, "Send _File...", "O", + "Send a program or variable file to the calculator", + G_CALLBACK(action_send_file) }, + + { "receive-file", + GTK_STOCK_SAVE_AS, "Re_ceive File...", "S", + "Receive a program or variable from the calculator", + G_CALLBACK(action_receive_file) }, + + { "open-calc", + GTK_STOCK_OPEN, "_Open Calculator...", "O", + "Open a calculator ROM file", + G_CALLBACK(action_open_calc) }, + { "save-calc", + GTK_STOCK_SAVE, "_Save Calculator", "S", + "Save current calculator state", + G_CALLBACK(action_save_calc) }, + { "revert-calc", + GTK_STOCK_REVERT_TO_SAVED, "Re_vert Calculator State", 0, + "Revert to saved calculator state", + G_CALLBACK(action_revert_calc) }, + { "reset-calc", + GTK_STOCK_CLEAR, "_Reset Calculator", "Delete", + "Reset the calculator", + G_CALLBACK(action_reset_calc) }, + + { "start-debugger", + 0, "_Debugger", "Pause", + "Pause emulation and start the debugger", + G_CALLBACK(action_start_debugger) }, + + { "begin-macro", + GTK_STOCK_MEDIA_RECORD, "_Record", 0, + "Begin recording a macro", + G_CALLBACK(action_begin_macro) }, + { "end-macro", + GTK_STOCK_MEDIA_STOP, "S_top", 0, + "Begin recording a macro", + G_CALLBACK(action_end_macro) }, + { "play-macro", + GTK_STOCK_MEDIA_PLAY, "_Play", 0, + "Play back the current macro", + G_CALLBACK(action_play_macro) }, + { "open-macro", + GTK_STOCK_OPEN, "_Open Macro File...", "", + "Load a macro from a file", + G_CALLBACK(action_open_macro) }, + { "save-macro", + GTK_STOCK_SAVE_AS, "_Save Macro File...", "", + "Save current macro to a file", + G_CALLBACK(action_save_macro) }, + + { "screenshot", + 0, "S_creenshot...", "Print", + "Save a screenshot", + G_CALLBACK(action_screenshot) }, + { "quick-screenshot", + 0, "_Quick Screenshot", "Print", + "Save a screenshot using default settings", + G_CALLBACK(action_quick_screenshot) }, + + { "preferences", + GTK_STOCK_PREFERENCES, 0, 0, + "Edit emulator settings", + G_CALLBACK(action_preferences) }, + + { "about", + GTK_STOCK_ABOUT, "_About", "", + "Print some informations about TilEm 2 and its authors", + G_CALLBACK(action_about) }, + + { "quit", + GTK_STOCK_QUIT, "_Quit", "Q", + "Quit the application", + G_CALLBACK(action_quit) }}; + +static GtkWidget *add_item(GtkWidget *menu, GtkAccelGroup *accelgrp, + GtkActionGroup *actions, const char *name) +{ + GtkAction *action; + GtkWidget *item; + + action = gtk_action_group_get_action(actions, name); + g_return_val_if_fail(action != NULL, NULL); + + gtk_action_set_accel_group(action, accelgrp); + item = gtk_action_create_menu_item(action); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + return item; +} + +static GtkWidget *add_separator(GtkWidget *menu) +{ + GtkWidget *item; + item = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + return item; +} + +static GtkWidget *add_submenu(GtkWidget *menu, const char *label) +{ + GtkWidget *item, *submenu; + + item = gtk_menu_item_new_with_mnemonic(label); + submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + return submenu; +} + +/* Build the menu */ +void build_menu(TilemEmulatorWindow* ewin) +{ + GtkActionGroup *acts; + GtkAccelGroup *ag; + GtkWidget *menu, *submenu; + + ewin->actions = acts = gtk_action_group_new("Emulator"); + gtk_action_group_add_actions(ewin->actions, main_action_ents, + G_N_ELEMENTS(main_action_ents), ewin); + + ag = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(ewin->window), ag); + + ewin->popup_menu = menu = gtk_menu_new(); + + add_item(menu, ag, acts, "send-file"); + add_item(menu, ag, acts, "receive-file"); + add_separator(menu); + + add_item(menu, ag, acts, "open-calc"); + add_item(menu, ag, acts, "save-calc"); + add_item(menu, ag, acts, "revert-calc"); + add_item(menu, ag, acts, "reset-calc"); + add_separator(menu); + + add_item(menu, ag, acts, "start-debugger"); + + submenu = add_submenu(menu, "_Macro"); + add_item(submenu, ag, acts, "begin-macro"); + add_item(submenu, ag, acts, "end-macro"); + add_item(submenu, ag, acts, "play-macro"); + add_separator(submenu); + add_item(submenu, ag, acts, "open-macro"); + add_item(submenu, ag, acts, "save-macro"); + + add_item(menu, ag, acts, "screenshot"); + add_item(menu, ag, acts, "quick-screenshot"); + add_separator(menu); + + add_item(menu, ag, acts, "preferences"); + add_separator(menu); + + add_item(menu, ag, acts, "about"); + add_item(menu, ag, acts, "quit"); +} diff --git a/tool/tilem-src/gui/msgbox.h b/tool/tilem-src/gui/msgbox.h new file mode 100644 index 0000000..7d88efd --- /dev/null +++ b/tool/tilem-src/gui/msgbox.h @@ -0,0 +1,67 @@ +/* + * Tilem II + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Message box macros */ + +#define messagebox00(prnt, mtyp, tprm, tsec) \ + do { \ + GtkWidget* mbx; \ + mbx = gtk_message_dialog_new(GTK_WINDOW(prnt), GTK_DIALOG_MODAL, \ + mtyp, GTK_BUTTONS_OK, tprm); \ + gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(mbx), \ + tsec); \ + gtk_dialog_set_default_response(GTK_DIALOG(mbx), GTK_RESPONSE_OK); \ + gtk_dialog_run(GTK_DIALOG(mbx)); \ + gtk_widget_destroy(mbx); \ + } while(0) + +#define messagebox01(prnt, mtyp, tprm, tsec, aaaa) \ + do { \ + GtkWidget* mbx; \ + mbx = gtk_message_dialog_new(GTK_WINDOW(prnt), GTK_DIALOG_MODAL, \ + mtyp, GTK_BUTTONS_OK, tprm); \ + gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(mbx), \ + tsec, (aaaa)); \ + gtk_dialog_set_default_response(GTK_DIALOG(mbx), GTK_RESPONSE_OK); \ + gtk_dialog_run(GTK_DIALOG(mbx)); \ + gtk_widget_destroy(mbx); \ + } while(0) + +#define messagebox02(prnt, mtyp, tprm, tsec, aaaa, bbbb) \ + do { \ + GtkWidget* mbx; \ + mbx = gtk_message_dialog_new(GTK_WINDOW(prnt), GTK_DIALOG_MODAL, \ + mtyp, GTK_BUTTONS_OK, tprm); \ + gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(mbx), \ + tsec, (aaaa), (bbbb)); \ + gtk_dialog_set_default_response(GTK_DIALOG(mbx), GTK_RESPONSE_OK); \ + gtk_dialog_run(GTK_DIALOG(mbx)); \ + gtk_widget_destroy(mbx); \ + } while(0) + +#define messagebox11(prnt, mtyp, tprm, aaaa, tsec, bbbb) \ + do { \ + GtkWidget* mbx; \ + mbx = gtk_message_dialog_new(GTK_WINDOW(prnt), GTK_DIALOG_MODAL, \ + mtyp, GTK_BUTTONS_OK, tprm, (aaaa)); \ + gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(mbx), \ + tsec, (bbbb)); \ + gtk_dialog_set_default_response(GTK_DIALOG(mbx), GTK_RESPONSE_OK); \ + gtk_dialog_run(GTK_DIALOG(mbx)); \ + gtk_widget_destroy(mbx); \ + } while(0) diff --git a/tool/tilem-src/gui/pbar.c b/tool/tilem-src/gui/pbar.c new file mode 100644 index 0000000..0d6354e --- /dev/null +++ b/tool/tilem-src/gui/pbar.c @@ -0,0 +1,156 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "gui.h" + +/* Update the progress_bar */ +static void progress_bar_update_activity(TilemLinkProgress *linkpb) +{ + char *s; + gdouble f; + + if (!linkpb->window || !linkpb->emu->pbar_title) + return; + + gtk_window_set_title(GTK_WINDOW(linkpb->window), linkpb->emu->pbar_title); + + s = g_strdup_printf("%s", linkpb->emu->pbar_title); + gtk_label_set_markup(linkpb->title_lbl, s); + g_free(s); + + if (linkpb->emu->paused && linkpb->emu->pbar_status) { + s = g_strconcat(linkpb->emu->pbar_status, " (paused)", NULL); + gtk_label_set_text(linkpb->status_lbl, s); + g_free(s); + } + else if (linkpb->emu->paused) + gtk_label_set_text(linkpb->status_lbl, "(paused)"); + else + gtk_label_set_text(linkpb->status_lbl, linkpb->emu->pbar_status); + + if (linkpb->emu->pbar_progress < 0.0) { + gtk_progress_bar_pulse(linkpb->progress_bar); + } + else { + f = CLAMP(linkpb->emu->pbar_progress, 0.0, 1.0); + gtk_progress_bar_set_fraction(linkpb->progress_bar, f); + } +} + +/* Callback to destroy the progress bar */ +static void destroy_progress(G_GNUC_UNUSED GtkDialog *dlg, + G_GNUC_UNUSED gint response, + gpointer data) +{ + TilemLinkProgress* linkpb = data; + tilem_calc_emulator_cancel_tasks(linkpb->emu); + gtk_widget_destroy(linkpb->window); + linkpb->window = NULL; + linkpb->progress_bar = NULL; + linkpb->title_lbl = NULL; + linkpb->status_lbl = NULL; +} + +/* Create the progress bar window */ +static void progress_bar_init(TilemLinkProgress* linkpb) +{ + GtkWidget *pw, *parent, *vbox, *vbox2, *lbl, *pb; + + if (linkpb->emu->ewin) + parent = linkpb->emu->ewin->window; + else + parent = NULL; + + pw = gtk_dialog_new_with_buttons("", GTK_WINDOW(parent), 0, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + NULL); + linkpb->window = pw; + + gtk_window_set_resizable(GTK_WINDOW(pw), FALSE); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(pw)); + + vbox2 = gtk_vbox_new(FALSE, 6); + gtk_container_set_border_width(GTK_CONTAINER(vbox2), 6); + + lbl = gtk_label_new(NULL); + gtk_label_set_width_chars(GTK_LABEL(lbl), 35); + gtk_misc_set_alignment(GTK_MISC(lbl), 0.0, 0.5); + gtk_box_pack_start(GTK_BOX(vbox2), lbl, FALSE, FALSE, 0); + linkpb->title_lbl = GTK_LABEL(lbl); + + pb = gtk_progress_bar_new(); + gtk_box_pack_start(GTK_BOX(vbox2), pb, FALSE, FALSE, 0); + linkpb->progress_bar = GTK_PROGRESS_BAR(pb); + + lbl = gtk_label_new(NULL); + gtk_misc_set_alignment(GTK_MISC(lbl), 0.0, 0.5); + gtk_box_pack_start(GTK_BOX(vbox2), lbl, FALSE, FALSE, 0); + linkpb->status_lbl = GTK_LABEL(lbl); + + gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 6); + + g_signal_connect(pw, "response", G_CALLBACK(destroy_progress), linkpb); + + progress_bar_update_activity(linkpb); + + gtk_widget_show_all(pw); +} + +/* Create or update the progress bar */ +void progress_bar_update(TilemCalcEmulator* emu) +{ + g_return_if_fail(emu != NULL); + + g_mutex_lock(emu->pbar_mutex); + + if (!emu->linkpb) { + emu->linkpb = g_slice_new0(TilemLinkProgress); + emu->linkpb->emu = emu; + } + + if (!emu->linkpb->window && emu->pbar_title) { + progress_bar_init(emu->linkpb); + } + else if (emu->linkpb->window && !emu->pbar_title) { + gtk_widget_destroy(emu->linkpb->window); + emu->linkpb->window = NULL; + emu->linkpb->title_lbl = NULL; + emu->linkpb->status_lbl = NULL; + emu->linkpb->progress_bar = NULL; + } + else { + progress_bar_update_activity(emu->linkpb); + } + + emu->pbar_update_pending = FALSE; + g_mutex_unlock(emu->pbar_mutex); +} + diff --git a/tool/tilem-src/gui/preferences.c b/tool/tilem-src/gui/preferences.c new file mode 100644 index 0000000..7758d38 --- /dev/null +++ b/tool/tilem-src/gui/preferences.c @@ -0,0 +1,270 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "files.h" +#include "filedlg.h" + +/* Convert to an absolute path */ +static char *canonicalize_filename(const char *name) +{ +#ifdef G_OS_WIN32 + static const char delim[] = "/\\"; +#else + static const char delim[] = G_DIR_SEPARATOR_S; +#endif + char *result, **parts, *p; + int i; + + if (name == NULL || g_path_is_absolute(name)) + return g_strdup(name); + + result = g_get_current_dir(); + parts = g_strsplit_set(name, delim, -1); + for (i = 0; parts[i]; i++) { + if (!strcmp(parts[i], "..")) { + p = g_path_get_dirname(result); + g_free(result); + result = p; + } + else if (strcmp(parts[i], ".") + && strcmp(parts[i], "")) { + p = g_build_filename(result, parts[i], NULL); + g_free(result); + result = p; + } + } + g_strfreev(parts); + return result; +} + +/* check if two file names are equivalent (of course, if this fails, + it doesn't necessarily mean the files are distinct) */ +static gboolean file_names_equal(const char *a, const char *b) +{ + char *ca, *cb; + gboolean status; + + if (a == NULL && b == NULL) + return TRUE; + else if (a == NULL || b == NULL) + return FALSE; + + ca = canonicalize_filename(a); + cb = canonicalize_filename(b); + status = !strcmp(ca, cb); + g_free(ca); + g_free(cb); + return status; +} + +static void save_skin_name(TilemEmulatorWindow *ewin) +{ + const char *model = ewin->emu->calc->hw.name; + char *base, *shared; + + /* don't save pref unless skin was actually loaded */ + if (!ewin->skin_file_name || !ewin->skin) + return; + + /* if file is stored in shared skins directory, save + only the relative path; otherwise, save the + absolute path */ + base = g_path_get_basename(ewin->skin_file_name); + shared = get_shared_file_path("skins", base, NULL); + + if (file_names_equal(shared, ewin->skin_file_name)) + tilem_config_set(model, + "skin/f", base, + NULL); + else + tilem_config_set(model, + "skin/f", ewin->skin_file_name, + NULL); + + g_free(base); + g_free(shared); +} + +static void speed_changed(GtkToggleButton *btn, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + gboolean setting = gtk_toggle_button_get_active(btn); + tilem_calc_emulator_set_limit_speed(ewin->emu, setting); + tilem_config_set("emulation", + "limit_speed/b", setting, + NULL); +} + +static void grayscale_changed(GtkToggleButton *btn, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + gboolean setting = gtk_toggle_button_get_active(btn); + tilem_calc_emulator_set_grayscale(ewin->emu, setting); + tilem_config_set("emulation", + "grayscale/b", setting, + NULL); +} + +static void smooth_changed(GtkToggleButton *btn, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + gboolean setting = gtk_toggle_button_get_active(btn); + ewin->lcd_smooth_scale = setting; + tilem_config_set("settings", + "smooth_scaling/b", setting, + NULL); +} + + +static void skin_enable_changed(GtkToggleButton *btn, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + gboolean enable = gtk_toggle_button_get_active(btn); + + if (ewin->skin_disabled == !enable) + return; + + tilem_emulator_window_set_skin_disabled(ewin, !enable); + tilem_config_set("settings", + "skin_disabled/b", !enable, + NULL); + + save_skin_name(ewin); +} + +static void skin_file_changed(GtkWidget *fe, gpointer data) +{ + TilemEmulatorWindow *ewin = data; + char *fname = file_entry_get_filename(fe); + + if (fname && !file_names_equal(fname, ewin->skin_file_name)) { + tilem_emulator_window_set_skin(ewin, fname); + save_skin_name(ewin); + g_free(fname); + } +} + +/* Run preferences dialog. */ +void tilem_preferences_dialog(TilemEmulatorWindow *ewin) +{ + GtkWidget *dlg, *vbox1, *vbox2, *frame, *slow_rb, *fast_rb, + *grayscale_cb, *smooth_cb, *hbox, *skin_cb, *skin_entry; + + g_return_if_fail(ewin != NULL); + g_return_if_fail(ewin->emu != NULL); + + dlg = gtk_dialog_new_with_buttons("Preferences", + GTK_WINDOW(ewin->window), + GTK_DIALOG_MODAL, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + vbox1 = gtk_vbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(vbox1), 6); + + /* Emulation speed */ + + vbox2 = gtk_vbox_new(FALSE, 6); + + slow_rb = gtk_radio_button_new_with_mnemonic + (NULL, "_Limit to actual calculator speed"); + gtk_box_pack_start(GTK_BOX(vbox2), slow_rb, FALSE, FALSE, 0); + + fast_rb = gtk_radio_button_new_with_mnemonic_from_widget + (GTK_RADIO_BUTTON(slow_rb), "As _fast as possible"); + gtk_box_pack_start(GTK_BOX(vbox2), fast_rb, FALSE, FALSE, 0); + + if (!ewin->emu->limit_speed) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fast_rb), TRUE); + + g_signal_connect(slow_rb, "toggled", + G_CALLBACK(speed_changed), ewin); + + frame = new_frame("Emulation Speed", vbox2); + gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); + + /* Display settings */ + + vbox2 = gtk_vbox_new(FALSE, 6); + + grayscale_cb = gtk_check_button_new_with_mnemonic("Emulate _grayscale"); + gtk_box_pack_start(GTK_BOX(vbox2), grayscale_cb, FALSE, FALSE, 0); + + if (ewin->emu->grayscale) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grayscale_cb), TRUE); + + g_signal_connect(grayscale_cb, "toggled", + G_CALLBACK(grayscale_changed), ewin); + + smooth_cb = gtk_check_button_new_with_mnemonic("Use _smooth scaling"); + gtk_box_pack_start(GTK_BOX(vbox2), smooth_cb, FALSE, FALSE, 0); + + if (ewin->lcd_smooth_scale) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(smooth_cb), TRUE); + + g_signal_connect(smooth_cb, "toggled", + G_CALLBACK(smooth_changed), ewin); + + hbox = gtk_hbox_new(FALSE, 6); + + skin_cb = gtk_check_button_new_with_mnemonic("Use s_kin:"); + gtk_box_pack_start(GTK_BOX(hbox), skin_cb, FALSE, FALSE, 0); + + skin_entry = file_entry_new("Select Skin", + "Skin files", "*.skn", + "All files", "*", + NULL); + gtk_box_pack_start(GTK_BOX(hbox), skin_entry, TRUE, TRUE, 0); + + gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0); + + if (!ewin->skin_disabled) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(skin_cb), TRUE); + + if (ewin->skin_file_name) + file_entry_set_filename(skin_entry, ewin->skin_file_name); + + g_signal_connect(skin_cb, "toggled", + G_CALLBACK(skin_enable_changed), ewin); + + g_signal_connect(skin_entry, "selection-changed", + G_CALLBACK(skin_file_changed), ewin); + + frame = new_frame("Display", vbox2); + gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0); + + vbox2 = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); + gtk_box_pack_start(GTK_BOX(vbox2), vbox1, FALSE, FALSE, 0); + gtk_widget_show_all(vbox1); + + gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_destroy(dlg); +} diff --git a/tool/tilem-src/gui/rcvmenu.c b/tool/tilem-src/gui/rcvmenu.c new file mode 100644 index 0000000..22ec546 --- /dev/null +++ b/tool/tilem-src/gui/rcvmenu.c @@ -0,0 +1,665 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "disasmview.h" +#include "memmodel.h" +#include "files.h" +#include "filedlg.h" +#include "msgbox.h" +#include "fixedtreeview.h" + +static GtkTreeModel* fill_varlist(TilemReceiveDialog *rcvdialog); +TilemReceiveDialog* create_receive_menu(TilemCalcEmulator *emu); + +/* Columns */ +enum +{ + COL_ENTRY = 0, + COL_SLOT_STR, + COL_NAME_STR, + COL_TYPE_STR, + COL_SIZE_STR, + COL_SIZE, + NUM_COLS +}; + +#define RESPONSE_REFRESH 1 + +/* Prompt to overwrite a list of files. */ +static gboolean prompt_overwrite(GtkWindow *win, const char *dirname, + char **filenames) +{ + int i; + char *dname; + GString *conflicts = NULL; + int nconflicts = 0; + GtkWidget *dlg, *btn; + int response; + + for (i = 0; filenames[i]; i++) { + if (g_file_test(filenames[i], G_FILE_TEST_EXISTS)) { + if (conflicts) + g_string_append_c(conflicts, '\n'); + else + conflicts = g_string_new(NULL); + dname = g_filename_display_basename(filenames[i]); + g_string_append(conflicts, dname); + g_free(dname); + nconflicts++; + } + } + + if (!conflicts) + return TRUE; + + dname = g_filename_display_basename(dirname); + + dlg = gtk_message_dialog_new + (win, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, + (nconflicts == 1 + ? "Replace existing file?" + : "Replace existing files?")); + + gtk_message_dialog_format_secondary_text + (GTK_MESSAGE_DIALOG(dlg), + (nconflicts == 1 + ? "The file \"%2$s\" already exists in \"%1$s\"." + " Replacing it will overwrite its contents." + : "The following files already exist in \"%s\"." + " Replacing them will overwrite their contents:\n%s"), + dname, conflicts->str); + + g_free(dname); + g_string_free(conflicts, TRUE); + + gtk_dialog_add_button(GTK_DIALOG(dlg), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + btn = gtk_button_new_with_mnemonic("_Replace"); + gtk_button_set_image(GTK_BUTTON(btn), + gtk_image_new_from_stock(GTK_STOCK_SAVE, + GTK_ICON_SIZE_BUTTON)); + gtk_widget_show(btn); + gtk_dialog_add_action_widget(GTK_DIALOG(dlg), btn, + GTK_RESPONSE_ACCEPT); + + gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + + response = gtk_dialog_run(GTK_DIALOG(dlg)); + gtk_widget_destroy(dlg); + return (response == GTK_RESPONSE_ACCEPT); +} + +/* #### SIGNALS CALLBACK #### */ + +/* Prompt to save a single file. */ +static gboolean prompt_save_single(TilemReceiveDialog *rcvdialog, TilemVarEntry *tve) +{ + char *dir, *default_filename, *default_filename_r, *filename, *pattern; + + default_filename = get_default_filename(tve); + default_filename_r = utf8_to_restricted_utf8(default_filename); + g_free(default_filename); + + tilem_config_get("download", "receivefile_recentdir/f", &dir, NULL); + if (!dir) dir = g_get_current_dir(); + + pattern = g_strconcat("*.", tve->file_ext, NULL); + + filename = prompt_save_file("Save File", GTK_WINDOW(rcvdialog->window), + default_filename_r, dir, + tve->filetype_desc, pattern, + "All files", "*", + NULL); + g_free(default_filename_r); + g_free(pattern); + g_free(dir); + + if (!filename) + return FALSE; + + dir = g_path_get_dirname(filename); + tilem_config_set("download", "receivefile_recentdir/f", dir, NULL); + g_free(dir); + + tilem_link_receive_file(rcvdialog->emu, tve, filename); + g_free(filename); + return TRUE; +} + +/* Prompt to save a list of variables as a group file. */ +static gboolean prompt_save_group(TilemReceiveDialog *rcvdialog, GList *rows) +{ + char *dir, *default_filename, *pattern_desc, *pattern, *filename, *fext; + int tfmodel; + gboolean can_group = TRUE; + const char *model_str; + GList *l; + GtkTreePath *path; + GtkTreeIter iter; + TilemVarEntry *tve; + GSList *velist = NULL; + + tilem_config_get("download", "receivefile_recentdir/f", &dir, NULL); + if (!dir) dir = g_get_current_dir(); + + for (l = rows; l; l = l->next) { + path = (GtkTreePath*) l->data; + gtk_tree_model_get_iter(rcvdialog->model, &iter, path); + gtk_tree_model_get(rcvdialog->model, &iter, COL_ENTRY, &tve, -1); + velist = g_slist_prepend(velist, tve); + if (!tve->can_group) + can_group = FALSE; + } + + velist = g_slist_reverse(velist); + + tfmodel = get_calc_model(rcvdialog->emu->calc); + + fext = g_ascii_strdown(tifiles_fext_of_group(tfmodel), -1); + pattern = g_strconcat("*.", fext, NULL); + default_filename = g_strdup_printf("untitled.%s", + (can_group ? fext : "tig")); + g_free(fext); + + model_str = tifiles_model_to_string(tfmodel); + pattern_desc = g_strdup_printf("%s group files", model_str); + + filename = prompt_save_file("Save File", GTK_WINDOW(rcvdialog->window), + default_filename, dir, + pattern_desc, (can_group ? pattern : ""), + "TIGroup files", "*.tig", + "All files", "*", + NULL); + + g_free(default_filename); + g_free(dir); + g_free(pattern_desc); + g_free(pattern); + + if (!filename) { + g_slist_free(velist); + return FALSE; + } + + dir = g_path_get_dirname(filename); + tilem_config_set("download", + "receivefile_recentdir/f", dir, + "save_as_group/b", TRUE, + NULL); + g_free(dir); + + tilem_link_receive_group(rcvdialog->emu, velist, filename); + + g_free(filename); + g_slist_free(velist); + return TRUE; +} + +/* Prompt to save a list of files. Input is a list of GtkTreePaths */ +static gboolean prompt_save_multiple(TilemReceiveDialog *rcvdialog, GList *rows) +{ + char *dir, *dir_selected, *default_filename, *default_filename_f; + GList *l; + GtkTreePath *path; + GtkTreeIter iter; + TilemVarEntry *tve, **vars; + char **names; + gboolean is_81, use_group; + int i; + + is_81 = (rcvdialog->emu->calc->hw.model_id == TILEM_CALC_TI81); + use_group = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(rcvdialog->group_rb)); + + if (use_group && !is_81) + return prompt_save_group(rcvdialog, rows); + + tilem_config_get("download", "receivefile_recentdir/f", &dir, NULL); + if (!dir) dir = g_get_current_dir(); + + dir_selected = prompt_select_dir("Save Files to Directory", + GTK_WINDOW(rcvdialog->window), + dir); + g_free(dir); + + if (!dir_selected) + return FALSE; + + tilem_config_set("download", + "receivefile_recentdir/f", dir_selected, + "save_as_group/b", use_group, + NULL); + + vars = g_new(TilemVarEntry *, g_list_length(rows) + 1); + names = g_new(char *, g_list_length(rows) + 1); + + for (l = rows, i = 0; l; l = l->next, i++) { + path = (GtkTreePath*) l->data; + gtk_tree_model_get_iter(rcvdialog->model, &iter, path); + gtk_tree_model_get(rcvdialog->model, &iter, COL_ENTRY, &tve, -1); + + vars[i] = tve; + + default_filename = get_default_filename(tve); + default_filename_f = utf8_to_filename(default_filename); + names[i] = g_build_filename(dir_selected, + default_filename_f, NULL); + g_free(default_filename); + g_free(default_filename_f); + } + + vars[i] = NULL; + names[i] = NULL; + + if (!prompt_overwrite(GTK_WINDOW(rcvdialog->window), + dir_selected, names)) { + g_free(vars); + g_strfreev(names); + g_free(dir_selected); + return FALSE; + } + + for (i = 0; vars[i]; i++) + tilem_link_receive_file(rcvdialog->emu, vars[i], names[i]); + + g_free(vars); + g_strfreev(names); + g_free(dir_selected); + return TRUE; +} + +/* Event called on Send button click. Get the selected var/app and save it. */ +static gboolean prompt_save(TilemReceiveDialog *rcvdialog) +{ + TilemVarEntry *tve; /* Variable entry */ + GtkTreeSelection* selection = NULL; /* GtkTreeSelection */ + GtkTreeModel *model; + GtkTreeIter iter; + GList *rows, *l; + GtkTreePath *path; + gboolean status; + + /* Get the selected entry */ + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(rcvdialog->treeview)); + rows = gtk_tree_selection_get_selected_rows(selection, &model); + + if (!rows) + return FALSE; + + if (!rows->next) { + path = (GtkTreePath*) rows->data; + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_ENTRY, &tve, -1); + status = prompt_save_single(rcvdialog, tve); + } + else { + status = prompt_save_multiple(rcvdialog, rows); + } + + for (l = rows; l; l = l->next) + gtk_tree_path_free(l->data); + g_list_free(rows); + return status; +} + +/* Dialog response button clicked */ +static void dialog_response(GtkDialog *dlg, gint response, gpointer data) +{ + TilemReceiveDialog* rcvdialog = (TilemReceiveDialog*) data; + + switch (response) { + case RESPONSE_REFRESH: + if (!rcvdialog->refresh_pending) { + rcvdialog->refresh_pending = TRUE; + tilem_link_get_dirlist(rcvdialog->emu); + } + break; + + case GTK_RESPONSE_ACCEPT: + if (!prompt_save(rcvdialog)) + break; + default: + gtk_widget_hide(GTK_WIDGET(dlg)); + } +} + +/* Selection changed */ +static void selection_changed(GtkTreeSelection *sel, gpointer data) +{ + TilemReceiveDialog* rcvdialog = data; + int n = gtk_tree_selection_count_selected_rows(sel); + + gtk_dialog_set_response_sensitive(GTK_DIALOG(rcvdialog->window), + GTK_RESPONSE_ACCEPT, (n > 0)); + gtk_widget_set_sensitive(rcvdialog->mode_box, (n > 1)); +} + +/* Row activated in tree view */ +static void row_activated(G_GNUC_UNUSED GtkTreeView *treeview, + G_GNUC_UNUSED GtkTreePath *path, + G_GNUC_UNUSED GtkTreeViewColumn *col, + gpointer data) +{ + TilemReceiveDialog* rcvdialog = (TilemReceiveDialog*) data; + gtk_dialog_response(GTK_DIALOG(rcvdialog->window), GTK_RESPONSE_ACCEPT); +} + +/* #### WIDGET CREATION #### */ + +/* Create a new scrolled window with sensible default settings. */ +static GtkWidget *new_scrolled_window(GtkWidget *contents) +{ + GtkWidget *sw; + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(sw), contents); + return sw; +} + +/* Create the (empty) GtkTreeView to show the vars list */ +static GtkWidget *create_varlist(TilemReceiveDialog *rcvdialog) +{ + GtkCellRenderer *renderer; + GtkWidget *treeview; + GtkTreeSelection *sel; + GtkTreeViewColumn *c1, *c2, *c3, *c4; + gboolean is_81; + + g_return_val_if_fail(rcvdialog->emu != NULL, NULL); + g_return_val_if_fail(rcvdialog->emu->calc != NULL, NULL); + + is_81 = (rcvdialog->emu->calc->hw.model_id == TILEM_CALC_TI81); + + /* Create the stack list tree view and set title invisible */ + treeview = gtk_tree_view_new(); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), TRUE); + + /* Allow multiple selection */ + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); + + /* Create the columns */ + renderer = gtk_cell_renderer_text_new(); + + if (is_81) { + c1 = gtk_tree_view_column_new_with_attributes + ("Slot", renderer, "text", COL_SLOT_STR, NULL); + + gtk_tree_view_column_set_sizing(c1, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_sort_column_id(c1, COL_SLOT_STR); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), c1); + } + + c2 = gtk_tree_view_column_new_with_attributes + ("Name", renderer, "text", COL_NAME_STR, NULL); + + gtk_tree_view_column_set_sizing(c2, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_sort_column_id(c2, COL_NAME_STR); + gtk_tree_view_column_set_expand(c2, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), c2); + + if (!is_81) { + c3 = gtk_tree_view_column_new_with_attributes + ("Type", renderer, "text", COL_TYPE_STR, NULL); + + gtk_tree_view_column_set_sizing(c3, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_sort_column_id(c3, COL_TYPE_STR); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), c3); + } + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "xalign", 1.0, NULL); + c4 = gtk_tree_view_column_new_with_attributes + ("Size", renderer, "text", COL_SIZE_STR, NULL); + + gtk_tree_view_column_set_sizing(c4, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_sort_column_id(c4, COL_SIZE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), c4); + + g_signal_connect(sel, "changed", + G_CALLBACK(selection_changed), rcvdialog); + + g_signal_connect(treeview, "row-activated", + G_CALLBACK(row_activated), rcvdialog); + + return treeview; +} + +/* Fill the list of vars. In fact, add all vars from list to a GtkListStore */ +static GtkTreeModel* fill_varlist(TilemReceiveDialog *rcvdialog) +{ + GSList *l; + TilemVarEntry *tve; + GtkListStore *store; + GtkTreeIter iter; + char *size_str; + + store = gtk_list_store_new(6, + G_TYPE_POINTER, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INT); + + for (l = rcvdialog->vars; l; l = l->next) { + tve = l->data; + gtk_list_store_append(store, &iter); +#ifdef G_OS_WIN32 + size_str = g_strdup_printf("%d", tve->size); +#else + size_str = g_strdup_printf("%'d", tve->size); +#endif + gtk_list_store_set(store, &iter, + COL_ENTRY, tve, + COL_SLOT_STR, tve->slot_str, + COL_NAME_STR, tve->name_str, + COL_TYPE_STR, tve->type_str, + COL_SIZE_STR, size_str, + COL_SIZE, tve->size, + -1); + g_free(size_str); + } + + return GTK_TREE_MODEL(store); +} + +/* Create a new menu for receiving vars. */ +/* Previous allocated and filled varlist is needed */ +TilemReceiveDialog* tilem_receive_dialog_new(TilemCalcEmulator *emu) +{ + TilemReceiveDialog* rcvdialog = g_slice_new0(TilemReceiveDialog); + GtkWidget *scroll, *btn, *vbox, *lbl, *rb, *vbox2; + int defheight = 300; + gboolean is_81; + gboolean use_group; + + g_return_val_if_fail(emu != NULL, NULL); + g_return_val_if_fail(emu->ewin != NULL, NULL); + g_return_val_if_fail(emu->calc != NULL, NULL); + + rcvdialog->emu = emu; + emu->rcvdlg = rcvdialog; + + is_81 = (emu->calc->hw.model_id == TILEM_CALC_TI81); + + rcvdialog->window = gtk_dialog_new(); + gtk_window_set_transient_for(GTK_WINDOW(rcvdialog->window), + GTK_WINDOW(emu->ewin->window)); + + gtk_window_set_title(GTK_WINDOW(rcvdialog->window), "Receive File"); + + btn = gtk_dialog_add_button(GTK_DIALOG(rcvdialog->window), + GTK_STOCK_REFRESH, RESPONSE_REFRESH); + + if (is_81) + gtk_widget_hide(btn); + + gtk_dialog_add_button(GTK_DIALOG(rcvdialog->window), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + gtk_dialog_add_button(GTK_DIALOG(rcvdialog->window), + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT); + + gtk_dialog_set_default_response(GTK_DIALOG(rcvdialog->window), + GTK_RESPONSE_ACCEPT); + gtk_dialog_set_alternative_button_order(GTK_DIALOG(rcvdialog->window), + RESPONSE_REFRESH, + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + + /* Set the size of the dialog */ + gtk_window_set_default_size(GTK_WINDOW(rcvdialog->window), -1, defheight); + + /* Create and fill tree view */ + rcvdialog->treeview = create_varlist(rcvdialog); + + /* Allow scrolling the list because we can't know how many vars the calc contains */ + scroll = new_scrolled_window(rcvdialog->treeview); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scroll), TRUE, TRUE, 0); + + rcvdialog->mode_box = gtk_hbox_new(FALSE, 6); + lbl = gtk_label_new("Save as:"); + gtk_box_pack_start(GTK_BOX(rcvdialog->mode_box), lbl, FALSE, FALSE, 0); + + rb = gtk_radio_button_new_with_mnemonic(NULL, "S_eparate files"); + gtk_box_pack_start(GTK_BOX(rcvdialog->mode_box), rb, FALSE, FALSE, 0); + rcvdialog->multiple_rb = rb; + + rb = gtk_radio_button_new_with_mnemonic_from_widget + (GTK_RADIO_BUTTON(rb), "_Group file"); + gtk_box_pack_start(GTK_BOX(rcvdialog->mode_box), rb, FALSE, FALSE, 0); + rcvdialog->group_rb = rb; + + tilem_config_get("download", "save_as_group/b=1", &use_group, NULL); + if (use_group) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rb), TRUE); + + if (is_81) + gtk_widget_set_no_show_all(rcvdialog->mode_box, TRUE); + + gtk_box_pack_start(GTK_BOX(vbox), rcvdialog->mode_box, FALSE, FALSE, 0); + vbox2 = gtk_dialog_get_content_area(GTK_DIALOG(rcvdialog->window)); + gtk_box_pack_start(GTK_BOX(vbox2), vbox, TRUE, TRUE, 0); + + /* Signals callback */ + g_signal_connect(rcvdialog->window, "response", + G_CALLBACK(dialog_response), rcvdialog); + g_signal_connect(rcvdialog->window, "delete-event", + G_CALLBACK(gtk_widget_hide_on_delete), NULL); + + gtk_widget_show_all(vbox); + + return rcvdialog; +} + +/* Destroy a TilemReceiveDialog */ +void tilem_receive_dialog_free(TilemReceiveDialog *rcvdialog) +{ + GSList *l; + + g_return_if_fail(rcvdialog != NULL); + + gtk_widget_destroy(rcvdialog->window); + + for (l = rcvdialog->vars; l; l = l->next) + tilem_var_entry_free(l->data); + g_slist_free(rcvdialog->vars); + + g_slice_free(TilemReceiveDialog, rcvdialog); +} + +void tilem_receive_dialog_update(TilemReceiveDialog *rcvdialog, GSList *varlist) +{ + GSList *l; + + g_return_if_fail(rcvdialog != NULL); + + rcvdialog->refresh_pending = FALSE; + + for (l = rcvdialog->vars; l; l = l->next) + tilem_var_entry_free(l->data); + g_slist_free(rcvdialog->vars); + + rcvdialog->vars = varlist; + rcvdialog->model = fill_varlist(rcvdialog); + gtk_tree_view_set_model(GTK_TREE_VIEW(rcvdialog->treeview), rcvdialog->model); + + fixed_tree_view_init(rcvdialog->treeview, 0, + COL_SLOT_STR, "PrgmM ", + COL_NAME_STR, "MMMMMMMMM ", + COL_TYPE_STR, "MMMMMM ", + COL_SIZE_STR, "00,000,000", + -1); + + gtk_widget_grab_focus(rcvdialog->treeview); + gtk_window_present(GTK_WINDOW(rcvdialog->window)); +} + +/* Popup the receive window */ +/* This is the entry point */ +void popup_receive_menu(TilemEmulatorWindow *ewin) +{ + g_return_if_fail(ewin != NULL); + g_return_if_fail(ewin->emu != NULL); + g_return_if_fail(ewin->emu->calc != NULL); + + if (ewin->emu->rcvdlg && ewin->emu->rcvdlg->refresh_pending) + return; + + /* TI-81 takes no time to refresh, so do it automatically */ + if (!ewin->emu->rcvdlg + || ewin->emu->calc->hw.model_id == TILEM_CALC_TI81) { + tilem_link_get_dirlist(ewin->emu); + } + else { + gtk_widget_grab_focus(ewin->emu->rcvdlg->treeview); + gtk_window_present(GTK_WINDOW(ewin->emu->rcvdlg->window)); + } +} diff --git a/tool/tilem-src/gui/screenshot.c b/tool/tilem-src/gui/screenshot.c new file mode 100644 index 0000000..d8b7e51 --- /dev/null +++ b/tool/tilem-src/gui/screenshot.c @@ -0,0 +1,810 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "files.h" +#include "filedlg.h" +#include "msgbox.h" + +#define DEFAULT_WIDTH_96 192 +#define DEFAULT_HEIGHT_96 128 +#define DEFAULT_WIDTH_128 256 +#define DEFAULT_HEIGHT_128 128 +#define DEFAULT_FORMAT "png" + +struct imgsize { + int width; + int height; +}; + +static const struct imgsize normal_sizes[] = + { { 96, 64 }, { 192, 128 }, { 288, 192 } }; + +static const struct imgsize wide_sizes[] = + /* actual aspect ratio is 92:55 or 1.673:1 */ + { { 128, 64 }, { 128, 77 }, + { 214, 128 }, { 256, 128 }, { 256, 153 }, + { 321, 192 }, { 384, 192 } }; + +static void grab_screen(GtkButton *btn, TilemScreenshotDialog *ssdlg); +static void begin_animation(GtkButton *btn, TilemScreenshotDialog *ssdlg); +static void end_animation(GtkButton *btn, TilemScreenshotDialog *ssdlg); +static gboolean save_output(TilemScreenshotDialog *ssdlg); + +static char* find_free_filename(const char* directory, + const char* filename, + const char* extension); + +/* Test if the calc has a wide screen (ti86) */ +static gboolean is_wide_screen(TilemCalcEmulator *emu) +{ + g_return_val_if_fail(emu != NULL, FALSE); + g_return_val_if_fail(emu->calc != NULL, FALSE); + + return (emu->calc->hw.lcdwidth == 128); +} + +/* Quick screenshot: save a screenshot with predefined settings, + without prompting the user */ +void quick_screenshot(TilemEmulatorWindow *ewin) +{ + char *folder, *filename, *format; + int grayscale, w96, h96, w128, h128, width, height; + TilemAnimation *anim; + GError *err = NULL; + GdkColor fg, bg; + + tilem_config_get("screenshot", + "directory/f", &folder, + "format/s", &format, + "grayscale/b=1", &grayscale, + "width_96x64/i", &w96, + "height_96x64/i", &h96, + "width_128x64/i", &w128, + "height_128x64/i", &h128, + "foreground/c=#000", &fg, + "background/c=#fff", &bg, + NULL); + + anim = tilem_calc_emulator_get_screenshot(ewin->emu, grayscale); + if (!anim) { + g_free(folder); + g_free(format); + return; + } + + if (is_wide_screen(ewin->emu)) { + width = (w128 > 0 ? w128 : DEFAULT_WIDTH_128); + height = (h128 > 0 ? h128 : DEFAULT_HEIGHT_128); + } + else { + width = (w96 > 0 ? w96 : DEFAULT_WIDTH_96); + height = (h96 > 0 ? h96 : DEFAULT_HEIGHT_96); + } + + tilem_animation_set_size(anim, width, height); + tilem_animation_set_colors(anim, &fg, &bg); + + if (!folder) + folder = get_config_file_path("screenshots", NULL); + + if (!format) + format = g_strdup(DEFAULT_FORMAT); + + g_mkdir_with_parents(folder, 0755); + + filename = find_free_filename(folder, "screenshot", format); + if (!filename) { + g_free(folder); + g_free(format); + g_object_unref(anim); + return; + } + + printf("screenshot saved : %s\n", filename); + + if (!tilem_animation_save(anim, filename, format, NULL, NULL, &err)) { + messagebox01(ewin->window, GTK_MESSAGE_ERROR, + "Unable to save screenshot", + "%s", err->message); + g_error_free(err); + } + + g_object_unref(anim); + g_free(filename); + g_free(folder); + g_free(format); +} + +/* Look for a free filename by testing [folder]/[basename]000.[extension] to [folder]/[basename]999.[extension] + Return a newly allocated string if success + Return null if no filename found */ +static char* find_free_filename(const char* folder, + const char* basename, + const char* extension) +{ + int i; + char *filename, *prefix; + + if(folder) + prefix = g_build_filename(folder, basename, NULL); + else + prefix = g_build_filename(basename, NULL); + + /* I do not use a while and limit number to 1000 because for any reason, if there's a problem in this scope + I don't want to freeze tilem (if tilem don't find a free filename and never return anything) + Limit to 1000 prevent this problem but if you prefer we could use a while wich wait a valid filename... */ + for(i=0; i<999; i++) { + filename = g_strdup_printf("%s%03d.%s", prefix, i, extension); + if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) { + g_free(prefix); + return filename; + } + g_free(filename); + } + + g_free(prefix); + return NULL; +} + +/* Change the review image to set the current animation */ +static void set_current_animation(TilemScreenshotDialog *ssdlg, + TilemAnimation *anim) +{ + GtkImage *img = GTK_IMAGE(ssdlg->screenshot_preview_image); + int width, height; + GdkColor fg, bg; + gdouble speed; + + if (anim) + g_object_ref(anim); + if (ssdlg->current_anim) + g_object_unref(ssdlg->current_anim); + ssdlg->current_anim = anim; + + if (!anim) { + gtk_image_set_from_animation(img, NULL); + gtk_dialog_set_response_sensitive(GTK_DIALOG(ssdlg->window), + GTK_RESPONSE_ACCEPT, FALSE); + } + else { + width = gtk_spin_button_get_value_as_int + (GTK_SPIN_BUTTON(ssdlg->width_spin)); + height = gtk_spin_button_get_value_as_int + (GTK_SPIN_BUTTON(ssdlg->height_spin)); + tilem_animation_set_size(anim, width, height); + + gtk_color_button_get_color + (GTK_COLOR_BUTTON(ssdlg->foreground_color), &fg); + gtk_color_button_get_color + (GTK_COLOR_BUTTON(ssdlg->background_color), &bg); + tilem_animation_set_colors(anim, &fg, &bg); + + speed = gtk_spin_button_get_value + (GTK_SPIN_BUTTON(ssdlg->animation_speed)); + tilem_animation_set_speed(anim, speed); + + gtk_image_set_from_animation(img, GDK_PIXBUF_ANIMATION(anim)); + + /* Need to call gtk_widget_show because we hide it + while recording */ + gtk_widget_show(ssdlg->screenshot_preview_image); + + gtk_dialog_set_response_sensitive(GTK_DIALOG(ssdlg->window), + GTK_RESPONSE_ACCEPT, TRUE); + } +} + +static void dialog_response(G_GNUC_UNUSED GtkDialog *dialog, gint response, gpointer data) +{ + TilemScreenshotDialog *ssdlg = data; + + if (response == GTK_RESPONSE_ACCEPT) { + if (!save_output(ssdlg)) + return; + } + + gtk_widget_hide(GTK_WIDGET(dialog)); + end_animation(NULL, ssdlg); + set_current_animation(ssdlg, NULL); +} + +static void set_size_spin_buttons(TilemScreenshotDialog *ssdlg, + int width, int height) +{ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(ssdlg->width_spin), width); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(ssdlg->height_spin), height); +} + +enum { + COL_TEXT, + COL_WIDTH, + COL_HEIGHT +}; + +static void animation_speed_changed(GtkSpinButton *animation_speed, + gpointer data) +{ + TilemScreenshotDialog *ssdlg = data; + TilemAnimation * anim = ssdlg->current_anim; + gdouble value = gtk_spin_button_get_value(animation_speed); + tilem_animation_set_speed(anim, value); +} + +/* Combo box changed. Update spin buttons accordingly. */ +static void size_combo_changed(GtkComboBox *combo, + TilemScreenshotDialog *ssdlg) +{ + GtkTreeModel *model; + GtkTreeIter iter; + int width, height; + + if (gtk_combo_box_get_active_iter(combo, &iter)) { + model = gtk_combo_box_get_model(combo); + gtk_tree_model_get(model, &iter, + COL_WIDTH, &width, + COL_HEIGHT, &height, + -1); + if (width && height) + set_size_spin_buttons(ssdlg, width, height); + } +} + +static void size_spin_changed(G_GNUC_UNUSED GtkSpinButton *sb, + TilemScreenshotDialog *ssdlg) +{ + GtkComboBox *combo = GTK_COMBO_BOX(ssdlg->ss_size_combo); + GtkTreeModel *model; + GtkTreeIter iter; + int width, height, w, h; + + model = gtk_combo_box_get_model(combo); + if (!model || !gtk_tree_model_get_iter_first(model, &iter)) + return; + + width = gtk_spin_button_get_value_as_int + (GTK_SPIN_BUTTON(ssdlg->width_spin)); + height = gtk_spin_button_get_value_as_int + (GTK_SPIN_BUTTON(ssdlg->height_spin)); + + do { + gtk_tree_model_get(model, &iter, + COL_WIDTH, &w, + COL_HEIGHT, &h, + -1); + + if ((w == 0 && h == 0) || (w == width && h == height)) { + gtk_combo_box_set_active_iter(combo, &iter); + break; + } + } while (gtk_tree_model_iter_next(model, &iter)); + + set_current_animation(ssdlg, ssdlg->current_anim); +} + +static void fill_size_combobox(GtkComboBox *combo, + const struct imgsize *sizes, + int nsizes) +{ + GtkListStore *store; + GtkTreeIter iter; + int i; + char *s; + + store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); + + for (i = 0; i < nsizes; i++) { + s = g_strdup_printf("%d \303\227 %d", + sizes[i].width, + sizes[i].height); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COL_TEXT, s, + COL_WIDTH, sizes[i].width, + COL_HEIGHT, sizes[i].height, + -1); + g_free(s); + } + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COL_TEXT, "Custom", + COL_WIDTH, 0, + COL_HEIGHT, 0, + -1); + + gtk_combo_box_set_model(GTK_COMBO_BOX(combo), GTK_TREE_MODEL(store)); +} + +/* This method is called when a color is set (foreground or background) + * It set a new palette based on new custom colors + * It refresh the screen to print new colors + */ +static void color_changed(G_GNUC_UNUSED GtkSpinButton *sb, + TilemScreenshotDialog *ssdlg) +{ + set_current_animation(ssdlg, ssdlg->current_anim); +} + +/* Create the screenshot menu */ +static TilemScreenshotDialog * create_screenshot_window(TilemCalcEmulator *emu) +{ + TilemScreenshotDialog *ssdlg = g_slice_new0(TilemScreenshotDialog); + GtkWidget *main_table, *vbox, *frame, *config_expander, + *tbl, *lbl, *align; + GtkCellRenderer *cell; + + ssdlg->emu = emu; + + ssdlg->window = gtk_dialog_new_with_buttons + ("Screenshot", + (emu->ewin ? GTK_WINDOW(emu->ewin->window) : NULL), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + gtk_window_set_resizable(GTK_WINDOW(ssdlg->window), FALSE); + + gtk_dialog_set_alternative_button_order(GTK_DIALOG(ssdlg->window), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + + gtk_dialog_set_default_response(GTK_DIALOG(ssdlg->window), + GTK_RESPONSE_ACCEPT); + + g_signal_connect(ssdlg->window, "response", + G_CALLBACK(dialog_response), ssdlg); + + g_signal_connect(ssdlg->window, "delete-event", + G_CALLBACK(gtk_widget_hide_on_delete), NULL); + + main_table = gtk_table_new(2, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(main_table), 6); + gtk_table_set_col_spacings(GTK_TABLE(main_table), 12); + gtk_container_set_border_width(GTK_CONTAINER(main_table), 6); + + /* Preview */ + + frame = gtk_frame_new("Preview"); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); + + ssdlg->screenshot_preview_image = gtk_image_new(); + align = gtk_alignment_new(0.0, 0.0, 0.0, 0.0); + gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0); + gtk_container_add(GTK_CONTAINER(align), ssdlg->screenshot_preview_image); + + gtk_container_add(GTK_CONTAINER(frame), align); + gtk_table_attach(GTK_TABLE(main_table), frame, 0, 1, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + /* Buttons */ + + vbox = gtk_vbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(vbox), GTK_BUTTONBOX_START); + gtk_box_set_spacing(GTK_BOX(vbox), 6); + + ssdlg->screenshot = gtk_button_new_with_mnemonic("_Grab"); + gtk_box_pack_start(GTK_BOX(vbox), ssdlg->screenshot, FALSE, FALSE, 0); + + ssdlg->record = gtk_button_new_with_mnemonic("_Record"); + gtk_box_pack_start(GTK_BOX(vbox), ssdlg->record, FALSE, FALSE, 0); + + ssdlg->stop = gtk_button_new_with_mnemonic("_Stop"); + gtk_box_pack_start(GTK_BOX(vbox), ssdlg->stop, FALSE, FALSE, 0); + gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->stop), FALSE); + + gtk_table_attach(GTK_TABLE(main_table), vbox, 1, 2, 0, 2, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + + /* Options */ + + config_expander = gtk_expander_new("Options"); + + tbl = gtk_table_new(7, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(tbl), 6); + gtk_table_set_col_spacings(GTK_TABLE(tbl), 6); + + ssdlg->grayscale_tb = gtk_check_button_new_with_mnemonic("Gra_yscale"); + gtk_table_attach(GTK_TABLE(tbl), ssdlg->grayscale_tb, + 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + + lbl = gtk_label_new_with_mnemonic("Image si_ze:"); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(GTK_TABLE(tbl), lbl, + 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0); + + ssdlg->ss_size_combo = gtk_combo_box_new(); + cell = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(ssdlg->ss_size_combo), + cell, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(ssdlg->ss_size_combo), + cell, "text", COL_TEXT, NULL); + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->ss_size_combo); + gtk_table_attach(GTK_TABLE(tbl), ssdlg->ss_size_combo, + 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0); + + lbl = gtk_label_new_with_mnemonic("_Width:"); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(GTK_TABLE(tbl), lbl, + 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0); + + ssdlg->width_spin = gtk_spin_button_new_with_range(1, 750, 1); + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->width_spin); + align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0); + gtk_container_add(GTK_CONTAINER(align), ssdlg->width_spin); + gtk_table_attach(GTK_TABLE(tbl), align, + 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0); + + lbl = gtk_label_new_with_mnemonic("_Height:"); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(GTK_TABLE(tbl), lbl, + 0, 1, 3, 4, GTK_FILL, GTK_FILL, 0, 0); + + ssdlg->height_spin = gtk_spin_button_new_with_range(1, 500, 1); + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->height_spin); + align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0); + gtk_container_add(GTK_CONTAINER(align), ssdlg->height_spin); + gtk_table_attach(GTK_TABLE(tbl), align, + 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0); + + + lbl = gtk_label_new_with_mnemonic("Animation s_peed:"); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(GTK_TABLE(tbl), lbl, + 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0); + + ssdlg->animation_speed = gtk_spin_button_new_with_range(0.1, 100.0, 0.1); + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->animation_speed); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(ssdlg->animation_speed), 1.0); + align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0); + gtk_container_add(GTK_CONTAINER(align), ssdlg->animation_speed); + gtk_table_attach(GTK_TABLE(tbl), align, + 1, 2, 4, 5, GTK_FILL, GTK_FILL, 0, 0); + + /* Foreground color and background color */ + lbl = gtk_label_new_with_mnemonic("_Foreground:"); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(GTK_TABLE(tbl), lbl, + 0, 1, 5, 6, GTK_FILL, GTK_FILL, 0, 0); + + ssdlg->foreground_color = gtk_color_button_new(); + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->foreground_color); + align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0); + gtk_container_add(GTK_CONTAINER(align), ssdlg->foreground_color); + gtk_table_attach(GTK_TABLE(tbl), align, + 1, 2, 5, 6, GTK_FILL, GTK_FILL, 0, 0); + + lbl = gtk_label_new_with_mnemonic("_Background:"); + gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5); + gtk_table_attach(GTK_TABLE(tbl), lbl, + 0, 1, 6, 7, GTK_FILL, GTK_FILL, 0, 0); + + ssdlg->background_color = gtk_color_button_new(); + gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->background_color); + align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0); + gtk_container_add(GTK_CONTAINER(align), ssdlg->background_color); + gtk_table_attach(GTK_TABLE(tbl), align, + 1, 2, 6, 7, GTK_FILL, GTK_FILL, 0, 0); + + align = gtk_alignment_new(0.5, 0.5, 1.0, 1.0); + gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0); + gtk_container_add(GTK_CONTAINER(align), tbl); + + gtk_container_add(GTK_CONTAINER(config_expander), align); + + gtk_table_attach(GTK_TABLE(main_table), config_expander, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + + g_signal_connect(ssdlg->screenshot, "clicked", + G_CALLBACK(grab_screen), ssdlg); + g_signal_connect(ssdlg->record, "clicked", + G_CALLBACK(begin_animation), ssdlg); + g_signal_connect(ssdlg->stop, "clicked", + G_CALLBACK(end_animation), ssdlg); + + g_signal_connect(ssdlg->ss_size_combo, "changed", + G_CALLBACK(size_combo_changed), ssdlg); + g_signal_connect(ssdlg->width_spin, "value-changed", + G_CALLBACK(size_spin_changed), ssdlg); + g_signal_connect(ssdlg->height_spin, "value-changed", + G_CALLBACK(size_spin_changed), ssdlg); + g_signal_connect(ssdlg->animation_speed, "value-changed", + G_CALLBACK(animation_speed_changed), ssdlg); + + g_signal_connect(ssdlg->foreground_color, "color-set", + G_CALLBACK(color_changed), ssdlg); + g_signal_connect(ssdlg->background_color, "color-set", + G_CALLBACK(color_changed), ssdlg); + /*g_signal_connect(config_expander, "activate", + G_CALLBACK(on_config_expander_activate), ssdlg); + */ + vbox = gtk_dialog_get_content_area(GTK_DIALOG(ssdlg->window)); + gtk_container_add(GTK_CONTAINER(vbox), main_table); + gtk_widget_show_all(main_table); + + return ssdlg; +} + +/* Popup the screenshot window */ +void popup_screenshot_window(TilemEmulatorWindow *ewin) +{ + TilemScreenshotDialog *ssdlg; + int w96, h96, w128, h128, width, height, grayscale; + GdkColor fg, bg; + + g_return_if_fail(ewin != NULL); + g_return_if_fail(ewin->emu != NULL); + + if (!ewin->emu->ssdlg) + ewin->emu->ssdlg = create_screenshot_window(ewin->emu); + ssdlg = ewin->emu->ssdlg; + + tilem_config_get("screenshot", + "grayscale/b=1", &grayscale, + "width_96x64/i", &w96, + "height_96x64/i", &h96, + "width_128x64/i", &w128, + "height_128x64/i", &h128, + "foreground/c=#000", &fg, + "background/c=#fff", &bg, + NULL); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ssdlg->grayscale_tb), + grayscale); + + if (is_wide_screen(ewin->emu)) { + fill_size_combobox(GTK_COMBO_BOX(ssdlg->ss_size_combo), + wide_sizes, G_N_ELEMENTS(wide_sizes)); + width = (w128 > 0 ? w128 : DEFAULT_WIDTH_128); + height = (h128 > 0 ? h128 : DEFAULT_HEIGHT_128); + } + else { + fill_size_combobox(GTK_COMBO_BOX(ssdlg->ss_size_combo), + normal_sizes, G_N_ELEMENTS(normal_sizes)); + width = (w96 > 0 ? w96 : DEFAULT_WIDTH_96); + height = (h96 > 0 ? h96 : DEFAULT_HEIGHT_96); + } + + set_size_spin_buttons(ssdlg, width, height); + size_spin_changed(NULL, ssdlg); + + gtk_color_button_set_color(GTK_COLOR_BUTTON(ssdlg->foreground_color), &fg); + gtk_color_button_set_color(GTK_COLOR_BUTTON(ssdlg->background_color), &bg); + + grab_screen(NULL, ssdlg); + gtk_window_present(GTK_WINDOW(ssdlg->window)); +} + +/* Save the current (static) output */ +static gboolean save_output(TilemScreenshotDialog *ssdlg) +{ + char *dir, *format, *filename, *basename; + TilemAnimation *anim = ssdlg->current_anim; + GdkPixbufAnimation *ganim = GDK_PIXBUF_ANIMATION(anim); + const char *format_opt, *width_opt, *height_opt; + gboolean is_static; + int width, height; + GdkColor fg, bg; + GError *err = NULL; + + g_return_val_if_fail(anim != NULL, FALSE); + + is_static = gdk_pixbuf_animation_is_static_image(ganim); + width = gdk_pixbuf_animation_get_width(ganim); + height = gdk_pixbuf_animation_get_height(ganim); + + gtk_color_button_get_color + (GTK_COLOR_BUTTON(ssdlg->foreground_color), &fg); + gtk_color_button_get_color + (GTK_COLOR_BUTTON(ssdlg->background_color), &bg); + + tilem_config_get("screenshot", + "directory/f", &dir, + "static_format/s", &format, + NULL); + + if (!dir) + dir = g_get_current_dir(); + + if (!is_static) { + g_free(format); + format = g_strdup("gif"); + } + else if (!format) { + format = g_strdup(DEFAULT_FORMAT); + } + + filename = find_free_filename(dir, "screenshot", format); + basename = (filename ? g_filename_display_basename(filename) : NULL); + g_free(filename); + g_free(format); + + if (!is_static) { + filename = prompt_save_file("Save Screenshot", + GTK_WINDOW(ssdlg->window), + basename, dir, + "GIF images", "*.gif", + "All files", "*", + NULL); + } + else { + /* FIXME: perhaps check the list of supported output + formats (gdk_pixbuf_get_formats()) - e.g., tiff is + usually supported, although it requires libtiff + installed (png and jpeg also require external + libraries, but we need those libraries anyway for + other reasons) */ + filename = prompt_save_file("Save Screenshot", + GTK_WINDOW(ssdlg->window), + basename, dir, + "PNG images", "*.png", + "GIF images", "*.gif", + "BMP images", "*.bmp", + "JPEG images", "*.jpg;*.jpe;*.jpeg", + "All files", "*", + NULL); + } + + g_free(basename); + g_free(dir); + + if (!filename) + return FALSE; + + if (!is_static) { + format = g_strdup("gif"); + } + else { + basename = g_path_get_basename(filename); + format = strrchr(basename, '.'); + if (!format) { + messagebox00(ssdlg->window, GTK_MESSAGE_ERROR, + "Unable to save screenshot", + "File name does not have a" + " recognized suffix"); + g_free(filename); + g_free(basename); + return FALSE; + } + else { + format = g_strdup(format + 1); + } + } + + tilem_animation_save(anim, filename, format, NULL, NULL, &err); + + dir = g_path_get_dirname(filename); + + if (err) { + messagebox01(ssdlg->window, GTK_MESSAGE_ERROR, + "Unable to save screenshot", + "%s", err->message); + g_error_free(err); + g_free(dir); + g_free(filename); + g_free(format); + return FALSE; + } + + if (is_static) + format_opt = "static_format/s"; + else + format_opt = NULL; + + if (is_wide_screen(ssdlg->emu)) { + width_opt = "width_128x64/i"; + height_opt = "height_128x64/i"; + } + else { + width_opt = "width_96x64/i"; + height_opt = "height_96x64/i"; + } + + tilem_config_set("screenshot", + "directory/f", dir, + "grayscale/b", ssdlg->current_anim_grayscale, + "foreground/c", &fg, + "background/c", &bg, + width_opt, width, + height_opt, height, + format_opt, format, + NULL); + + g_free(dir); + g_free(filename); + g_free(format); + return TRUE; +} + +/* Callback for record button */ +static void begin_animation(G_GNUC_UNUSED GtkButton *btn, + TilemScreenshotDialog *ssdlg) +{ + gboolean grayscale = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(ssdlg->grayscale_tb)); + + gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->animation_speed), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->screenshot), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->record), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->stop), TRUE); + gtk_dialog_set_response_sensitive(GTK_DIALOG(ssdlg->window), + GTK_RESPONSE_ACCEPT, FALSE); + + tilem_calc_emulator_begin_animation(ssdlg->emu, grayscale); + ssdlg->current_anim_grayscale = grayscale; + + /* You can choose to hide current animation while recording or not + It's as you prefer... For the moment I hide it */ + /*gtk_widget_hide(GTK_WIDGET(ssdlg->screenshot_preview_image)); */ + + //set_current_animation(ssdlg, NULL); +} + +/* Callback for stop button (stop the recording) */ +static void end_animation(G_GNUC_UNUSED GtkButton *btn, + TilemScreenshotDialog *ssdlg) +{ + TilemAnimation *anim; + + if (ssdlg->emu->anim) { + anim = tilem_calc_emulator_end_animation(ssdlg->emu); + set_current_animation(ssdlg, anim); + g_object_unref(anim); + } + else { + set_current_animation(ssdlg, NULL); + } + + gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->animation_speed), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->screenshot), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->record), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->stop), FALSE); +} + +/* Callback for screenshot button (take a screenshot) */ +static void grab_screen(G_GNUC_UNUSED GtkButton *btn, + TilemScreenshotDialog *ssdlg) +{ + TilemAnimation *anim; + + gboolean grayscale = gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(ssdlg->grayscale_tb)); + + anim = tilem_calc_emulator_get_screenshot(ssdlg->emu, grayscale); + ssdlg->current_anim_grayscale = grayscale; + set_current_animation(ssdlg, anim); + g_object_unref(anim); +} + diff --git a/tool/tilem-src/gui/sendfile.c b/tool/tilem-src/gui/sendfile.c new file mode 100644 index 0000000..13eb32d --- /dev/null +++ b/tool/tilem-src/gui/sendfile.c @@ -0,0 +1,526 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "emucore.h" +#include "filedlg.h" +#include "ti81prg.h" + + +/* Send a series of files */ +static void send_files(TilemCalcEmulator *emu, char **filenames, int *slots) +{ + int i; + + for (i = 0; filenames[i]; i++) { + tilem_link_send_file(emu, filenames[i], + slots ? slots[i] : -1, + (i == 0), + (filenames[i + 1] == NULL)); + + /* FIXME: macros should record slot numbers */ + if (emu->isMacroRecording) + tilem_macro_add_action(emu->macro, 1, filenames[i]); + } +} + +static int string_to_slot(const char *str) +{ + if (!g_ascii_strncasecmp(str, "prgm", 4)) + str += 4; + else if (!g_ascii_strncasecmp(str, "ti81_", 5)) + str += 5; + else + return TI81_SLOT_AUTO; + + if (g_ascii_isdigit(str[0]) && !g_ascii_isalnum(str[1])) + return TI81_SLOT_0 + str[0] - '0'; + else if (g_ascii_isalpha(str[0]) && !g_ascii_isalnum(str[1])) + return TI81_SLOT_A + g_ascii_toupper(str[0]) - 'A'; + else if (str[0] == '@' + || !g_ascii_strncasecmp(str, "theta", 5) + || !strncmp(str, "\316\270", 2) + || !strncmp(str, "\316\230", 2)) + return TI81_SLOT_THETA; + else + return TI81_SLOT_AUTO; +} + +/* Guess program slot for a filename */ +static int guess_slot(const char *filename) +{ + char *base; + int slot; + base = g_filename_display_basename(filename); + slot = string_to_slot(base); + g_free(base); + return slot; +} + +static int display_index_to_slot(int i) +{ + if (i < 9) + return i + 1; + else if (i == 9) + return 0; + else + return i; +} + +struct slotdialog { + int nfiles; + char **filenames; + int *slots; + TI81ProgInfo info[TI81_SLOT_MAX + 1]; + GtkTreeModel *prgm_model; + GtkTreeModel *slot_model; +}; + +static void slot_edited(G_GNUC_UNUSED GtkCellRendererText *cell, + gchar *pathstr, gchar *text, gpointer data) +{ + struct slotdialog *slotdlg = data; + GtkTreeIter iter; + int n, slot; + char *end; + + slot = string_to_slot(text); + if (slot < 0) + return; + + n = strtol(pathstr, &end, 10); + gtk_tree_model_iter_nth_child(slotdlg->prgm_model, &iter, NULL, n); + gtk_list_store_set(GTK_LIST_STORE(slotdlg->prgm_model), + &iter, 1, text, -1); + + slotdlg->slots[n] = slot; +} + +/* Prompt user to assign program slots to filenames */ +static void prompt_program_slots(TilemCalcEmulator *emu, + struct slotdialog *slotdlg) +{ + GtkWidget *parent, *dlg, *vbox, *vbox2, *sw, *tv, *lbl; + GtkListStore *prgmstore, *slotstore; + GtkTreeIter iter; + GtkCellRenderer *cell; + GtkTreeViewColumn *col; + int i, j, slot; + int used[TI81_SLOT_MAX + 1]; + char *slotstr, *namestr; + char *slotlabel[TI81_SLOT_MAX + 1]; + + if (emu->ewin) + parent = emu->ewin->window; + else + parent = NULL; + + /* Generate list of existing programs */ + + slotstore = gtk_list_store_new(1, G_TYPE_STRING); + slotdlg->slot_model = GTK_TREE_MODEL(slotstore); + + for (i = 0; i <= TI81_SLOT_MAX; i++) { + slot = display_index_to_slot(i); + slotstr = ti81_program_slot_to_string(slot); + namestr = ti81_program_name_to_string(slotdlg->info[slot].name); + + if (slotdlg->info[slot].size == 0) { + slotlabel[slot] = g_strdup(slotstr); + used[slot] = 0; + } + else if (namestr && namestr[0]) { + slotlabel[slot] = g_strdup_printf("%s (in use: %s)", + slotstr, namestr); + used[slot] = 1; + } + else { + slotlabel[slot] = g_strdup_printf("%s (in use)", slotstr); + used[slot] = 1; + } + + gtk_list_store_append(slotstore, &iter); + gtk_list_store_set(slotstore, &iter, 0, slotlabel[slot], -1); + g_free(slotstr); + g_free(namestr); + } + + /* Assign default slots to files */ + + for (i = 0; i < slotdlg->nfiles; i++) { + slot = guess_slot(slotdlg->filenames[i]); + if (slotdlg->slots[i] < 0) + slotdlg->slots[i] = slot; + if (slot >= 0) + used[slot] = 1; + } + + for (i = 0; i < slotdlg->nfiles; i++) { + if (slotdlg->slots[i] < 0) { + for (j = 0; j <= TI81_SLOT_MAX; j++) { + slot = display_index_to_slot(j); + if (!used[slot]) { + slotdlg->slots[i] = slot; + used[slot] = 1; + break; + } + } + } + + if (slotdlg->slots[i] < 0) + slotdlg->slots[i] = TI81_SLOT_1; + } + + /* Generate list of filenames and assigned slots */ + + prgmstore = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + slotdlg->prgm_model = GTK_TREE_MODEL(prgmstore); + + for (i = 0; i < slotdlg->nfiles; i++) { + namestr = g_filename_display_basename(slotdlg->filenames[i]); + slot = slotdlg->slots[i]; + + gtk_list_store_append(prgmstore, &iter); + gtk_list_store_set(prgmstore, &iter, + 0, namestr, + 1, slotlabel[slot], + -1); + g_free(namestr); + } + + for (i = 0; i <= TI81_SLOT_MAX; i++) + g_free(slotlabel[i]); + + /* Create tree view */ + + tv = gtk_tree_view_new_with_model(slotdlg->prgm_model); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tv), TRUE); + + cell = gtk_cell_renderer_text_new(); + col = gtk_tree_view_column_new_with_attributes + ("File", cell, "text", 0, NULL); + gtk_tree_view_column_set_expand(col, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col); + + cell = gtk_cell_renderer_combo_new(); + g_object_set(cell, "model", slotstore, "text-column", 0, + "editable", TRUE, "has-entry", FALSE, NULL); + col = gtk_tree_view_column_new_with_attributes + ("Slot", cell, "text", 1, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(tv), col); + + g_signal_connect(cell, "edited", G_CALLBACK(slot_edited), slotdlg); + + /* Create dialog */ + + dlg = gtk_dialog_new_with_buttons("Select Program Slots", + GTK_WINDOW(parent), GTK_DIALOG_MODAL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK); + + gtk_window_set_default_size(GTK_WINDOW(dlg), -1, 250); + + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(sw), tv); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); + + lbl = gtk_label_new("Select a slot where each program should be" + " loaded. If a program slot is already in use," + " its contents will be overwritten."); + gtk_misc_set_alignment(GTK_MISC(lbl), 0.0, 0.0); + gtk_label_set_line_wrap(GTK_LABEL(lbl), TRUE); + gtk_label_set_width_chars(GTK_LABEL(lbl), 45); + gtk_box_pack_start(GTK_BOX(vbox), lbl, FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); + gtk_widget_show_all(vbox); + + vbox2 = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); + gtk_box_pack_start(GTK_BOX(vbox2), vbox, TRUE, TRUE, 0); + + if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) + send_files(emu, slotdlg->filenames, slotdlg->slots); + + gtk_widget_destroy(dlg); +} + +/* Check status of existing programs */ +static gboolean check_prog_slots_main(TilemCalcEmulator *emu, gpointer data) +{ + struct slotdialog *slotdlg = data; + int i; + + tilem_em_wake_up(emu, TRUE); + for (i = 0; i <= TI81_SLOT_MAX; i++) + ti81_get_program_info(emu->calc, i, &slotdlg->info[i]); + + return TRUE; +} + +static void check_prog_slots_finished(TilemCalcEmulator *emu, gpointer data, + gboolean cancelled) +{ + struct slotdialog *slotdlg = data; + + if (!cancelled) + prompt_program_slots(emu, slotdlg); + + g_free(slotdlg->slots); + g_strfreev(slotdlg->filenames); + g_slice_free(struct slotdialog, slotdlg); +} + + +#define PAT_TI81 "*.prg" +#define PAT_TI73 "*.73?" +#define PAT_TI73_NUM "*.73n;*.73l;*.73m;*.73i" +#define PAT_TI82 "*.82?" +#define PAT_TI82_NUM "*.82n;*.82l;*.82m;*.82i" +#define PAT_TI82_TEXT "*.82s;*.82y;*.82p" +#define PAT_TI83 "*.83?" +#define PAT_TI83_NUM "*.83n;*.83l;*.83m;*.83i" +#define PAT_TI83_TEXT "*.83s;*.83y;*.83p" +#define PAT_TI83P "*.8x?;*.8xgrp" +#define PAT_TI83P_NUM "*.8xn;*.8xl;*.8xm;*.8xi" +#define PAT_TI83P_TEXT "*.8xs;*.8xy;*.8xp" +#define PAT_TI85 "*.85?" +#define PAT_TI86 "*.86?" +#define PAT_TIG "*.tig" + +#define FLT_TI81 "TI-81 programs", PAT_TI81 +#define FLT_TI73 "TI-73 files", PAT_TI73 +#define FLT_TI82 "TI-82 files", PAT_TI82 +#define FLT_TI83 "TI-83 files", PAT_TI83 +#define FLT_TI83P "TI-83 Plus files", PAT_TI83P +#define FLT_TI85 "TI-85 files", PAT_TI85 +#define FLT_TI86 "TI-86 files", PAT_TI86 +#define FLT_TIG "TIGroup files", PAT_TIG +#define FLT_ALL "All files", "*" + +#define DESC_COMPAT "All compatible files" + +#define FLT_TI73_COMPAT DESC_COMPAT, (PAT_TI73 ";" PAT_TIG ";" \ + PAT_TI82_NUM ";" \ + PAT_TI83_NUM ";" \ + PAT_TI83P_NUM) + +#define FLT_TI82_COMPAT DESC_COMPAT, (PAT_TI82 ";" PAT_TIG ";" \ + PAT_TI83_TEXT ";" PAT_TI83_NUM ";" \ + PAT_TI83P_TEXT ";" PAT_TI83P_NUM ";" \ + PAT_TI73_NUM) + +#define FLT_TI83_COMPAT DESC_COMPAT, (PAT_TI83 ";" PAT_TIG ";" \ + PAT_TI82_TEXT ";" PAT_TI82_NUM ";" \ + PAT_TI83P_TEXT ";" PAT_TI83P_NUM ";" \ + PAT_TI73_NUM) + +#define FLT_TI83P_COMPAT DESC_COMPAT, (PAT_TI83P ";" PAT_TIG ";" \ + PAT_TI82_TEXT ";" PAT_TI82_NUM ";" \ + PAT_TI83_TEXT ";" PAT_TI83_NUM ";" \ + PAT_TI73_NUM) + +#define FLT_TI8586_COMPAT DESC_COMPAT, (PAT_TI85 ";" PAT_TI86 ";" PAT_TIG) + +static char ** prompt_link_files(const char *title, + GtkWindow *parent, + const char *dir, + int model) +{ + switch (model) { + case TILEM_CALC_TI73: + return prompt_open_files(title, parent, dir, + FLT_TI73_COMPAT, FLT_TI73, + FLT_TI82, FLT_TI83, FLT_TI83P, + FLT_TIG, FLT_ALL, NULL); + case TILEM_CALC_TI81: + return prompt_open_files(title, parent, dir, + FLT_TI81, FLT_ALL, NULL); + case TILEM_CALC_TI82: + return prompt_open_files(title, parent, dir, + FLT_TI82_COMPAT, FLT_TI73, + FLT_TI82, FLT_TI83, FLT_TI83P, + FLT_TIG, FLT_ALL, NULL); + case TILEM_CALC_TI83: + case TILEM_CALC_TI76: + return prompt_open_files(title, parent, dir, + FLT_TI83_COMPAT, FLT_TI73, + FLT_TI82, FLT_TI83, FLT_TI83P, + FLT_TIG, FLT_ALL, NULL); + case TILEM_CALC_TI83P: + case TILEM_CALC_TI83P_SE: + case TILEM_CALC_TI84P: + case TILEM_CALC_TI84P_SE: + case TILEM_CALC_TI84P_NSPIRE: + return prompt_open_files(title, parent, dir, + FLT_TI83P_COMPAT, FLT_TI73, + FLT_TI82, FLT_TI83, FLT_TI83P, + FLT_TIG, FLT_ALL, NULL); + case TILEM_CALC_TI85: + case TILEM_CALC_TI86: + return prompt_open_files(title, parent, dir, + FLT_TI8586_COMPAT, FLT_TI85, + FLT_TI86, FLT_TIG, FLT_ALL, NULL); + default: + return prompt_open_files(title, parent, dir, FLT_ALL, NULL); + } +} + +/* Load a list of files through the GUI. The list of filenames must + end with NULL. */ +void load_files(TilemEmulatorWindow *ewin, char **filenames) +{ + struct slotdialog *slotdlg; + int i; + + g_return_if_fail(ewin->emu->calc != NULL); + + if (ewin->emu->calc->hw.model_id == TILEM_CALC_TI81) { + slotdlg = g_slice_new0(struct slotdialog); + slotdlg->filenames = g_strdupv(filenames); + slotdlg->nfiles = g_strv_length(filenames); + slotdlg->slots = g_new(int, slotdlg->nfiles); + for (i = 0; i < slotdlg->nfiles; i++) + slotdlg->slots[i] = TI81_SLOT_AUTO; + tilem_calc_emulator_begin(ewin->emu, &check_prog_slots_main, + &check_prog_slots_finished, slotdlg); + } + else { + send_files(ewin->emu, filenames, NULL); + } +} + +static int get_cmdline_slot(const char *str, const char **name) +{ + char *e; + int n; + + n = strtol(str, &e, 10); + if (*e == '=') { + *name = e + 1; + return n; + } + + if (g_ascii_isalpha(str[0]) && str[1] == '=') { + *name = str + 2; + return TI81_SLOT_A + g_ascii_toupper(str[0]) - 'A'; + } + + if (str[0] == '@' && str[1] == '=') { + *name = str + 2; + return TI81_SLOT_THETA; + } + + if (!g_ascii_strncasecmp(str, "theta=", 6)) { + *name = str + 6; + return TI81_SLOT_THETA; + } + + *name = str; + return TI81_SLOT_AUTO; +} + +/* Load a list of files from the command line. Filenames may begin + with an optional slot designation. */ +void load_files_cmdline(TilemEmulatorWindow *ewin, char **filenames) +{ + struct slotdialog *slotdlg; + int i; + gboolean need_prompt = FALSE; + const char *name; + + g_return_if_fail(ewin->emu->calc != NULL); + + slotdlg = g_slice_new0(struct slotdialog); + slotdlg->nfiles = g_strv_length(filenames); + slotdlg->slots = g_new(int, slotdlg->nfiles); + slotdlg->filenames = g_new0(char *, slotdlg->nfiles + 1); + + for (i = 0; i < slotdlg->nfiles; i++) { + slotdlg->slots[i] = get_cmdline_slot(filenames[i], &name); + slotdlg->filenames[i] = g_strdup(name); + + if (slotdlg->slots[i] < 0) + need_prompt = TRUE; + } + + if (need_prompt && ewin->emu->calc->hw.model_id == TILEM_CALC_TI81) { + tilem_calc_emulator_begin(ewin->emu, &check_prog_slots_main, + &check_prog_slots_finished, slotdlg); + } + else { + send_files(ewin->emu, slotdlg->filenames, slotdlg->slots); + g_free(slotdlg->slots); + g_strfreev(slotdlg->filenames); + g_slice_free(struct slotdialog, slotdlg); + } +} + +/* Prompt user to load a file */ +void load_file_dialog(TilemEmulatorWindow *ewin) +{ + char **filenames, *dir; + + tilem_config_get("upload", + "sendfile_recentdir/f", &dir, + NULL); + + filenames = prompt_link_files("Send File", + GTK_WINDOW(ewin->window), + dir, ewin->emu->calc->hw.model_id); + g_free(dir); + + if (!filenames || !filenames[0]) { + g_free(filenames); + return; + } + + dir = g_path_get_dirname(filenames[0]); + tilem_config_set("upload", + "sendfile_recentdir/f", dir, + NULL); + g_free(dir); + + load_files(ewin, filenames); + g_strfreev(filenames); +} diff --git a/tool/tilem-src/gui/skinops.c b/tool/tilem-src/gui/skinops.c new file mode 100644 index 0000000..6a23acc --- /dev/null +++ b/tool/tilem-src/gui/skinops.c @@ -0,0 +1,299 @@ +/* + * skinedit - a skin editor for the TiEmu emulator + * Copyright (C) 2002 Julien BLACHE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* +contra-sh : + This file is a (quasi) perfect copy of the tiemu skinops.c file ... + Thank's to rom's and JB for this wonderful work. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "skinops.h" + +#include +#include + +#define SKIN_ERROR g_quark_from_static_string("skin-error") +enum { + SKIN_ERROR_INVALID +}; + +/* + Determine skin type +*/ +int skin_get_type(SKIN_INFOS *si, const char *filename) +{ + FILE *fp; + char str[17]; + + fp = g_fopen(filename, "rb"); + if (fp == NULL) { + fprintf(stderr, "Unable to open this file: <%s>\n", filename); + return -1; + } + + memset(str, 0, sizeof(str)); + fread(str, 16, 1, fp); + + if(!strncmp(str, "TiEmu v2.00", 16)) + si->type = SKIN_TYPE_TIEMU; + else if(!strncmp(str, "TilEm v2.00 ", 16)) + si->type = SKIN_TYPE_TIEMU; + else if(!strncmp(str, "VTIv2.1 ", 8)) + si->type = SKIN_TYPE_OLD_VTI; + else if(!strncmp(str, "VTIv2.5 ", 8)) + si->type = SKIN_TYPE_VTI; + else { + fprintf(stderr, "Bad skin format\n"); + return -1; + } + + return 0; +} + +/* + Read TilEm skin informations (header) +*/ +int skin_read_header(SKIN_INFOS *si, FILE *fp) +{ + int i; + uint32_t endian; + uint32_t jpeg_offset; + uint32_t length; + char str[17]; + + /* signature & offsets */ + fread(str, 16, 1, fp); + if ((strncmp(str, "TilEm v2.00", 16)) + && (strncmp(str, "TiEmu v2.00", 16))) { + return -1; + } + fread(&endian, 4, 1, fp); + fread(&jpeg_offset, 4, 1, fp); + + if (endian != ENDIANNESS_FLAG) + jpeg_offset = GUINT32_SWAP_LE_BE(jpeg_offset); + + /* Skin name */ + fread(&length, 4, 1, fp); + if (endian != ENDIANNESS_FLAG) + length = GUINT32_SWAP_LE_BE(length); + + if (length > 0) { + si->name = (char *)malloc(length + 1); + if (si->name == NULL) + return -1; + + memset(si->name, 0, length + 1); + fread(si->name, length, 1, fp); + } + + /* Skin author */ + fread(&length, 4, 1, fp); + if (endian != ENDIANNESS_FLAG) + length = GUINT32_SWAP_LE_BE(length); + + if (length > 0) { + si->author = (char *)malloc(length + 1); + if (si->author == NULL) + return -1; + + memset(si->author, 0, length + 1); + fread(si->author, length, 1, fp); + } + + /* LCD colors */ + fread(&si->colortype, 4, 1, fp); + fread(&si->lcd_white, 4, 1, fp); + fread(&si->lcd_black, 4, 1, fp); + + /* Calc type */ + fread(si->calc, 8, 1, fp); + + /* LCD position */ + fread(&si->lcd_pos.left, 4, 1, fp); + fread(&si->lcd_pos.top, 4, 1, fp); + fread(&si->lcd_pos.right, 4, 1, fp); + fread(&si->lcd_pos.bottom, 4, 1, fp); + + /* Number of RECT struct to read */ + fread(&length, 4, 1, fp); + if (endian != ENDIANNESS_FLAG) + length = GUINT32_SWAP_LE_BE(length); + + if (length > SKIN_KEYS) + return -1; + + for (i = 0; i < (int)length; i++) { + fread(&si->keys_pos[i].left, 4, 1, fp); + fread(&si->keys_pos[i].top, 4, 1, fp); + fread(&si->keys_pos[i].right, 4, 1, fp); + fread(&si->keys_pos[i].bottom, 4, 1, fp); + } + + if (endian != ENDIANNESS_FLAG) { + si->colortype = GUINT32_SWAP_LE_BE(si->colortype); + si->lcd_white = GUINT32_SWAP_LE_BE(si->lcd_white); + si->lcd_black = GUINT32_SWAP_LE_BE(si->lcd_black); + + si->lcd_pos.top = GUINT32_SWAP_LE_BE(si->lcd_pos.top); + si->lcd_pos.left = GUINT32_SWAP_LE_BE(si->lcd_pos.left); + si->lcd_pos.bottom = GUINT32_SWAP_LE_BE(si->lcd_pos.bottom); + si->lcd_pos.right = GUINT32_SWAP_LE_BE(si->lcd_pos.right); + + for (i = 0; i < (int)length; i++) { + si->keys_pos[i].top = GUINT32_SWAP_LE_BE(si->keys_pos[i].top); + si->keys_pos[i].bottom = GUINT32_SWAP_LE_BE(si->keys_pos[i].bottom); + si->keys_pos[i].left = GUINT32_SWAP_LE_BE(si->keys_pos[i].left); + si->keys_pos[i].right = GUINT32_SWAP_LE_BE(si->keys_pos[i].right); + } + } + + si->jpeg_offset = ftell(fp); + + return 0; +} + +/* + Read skin image (pure jpeg data) +*/ +int skin_read_image(SKIN_INFOS *si, FILE *fp, GError **err) +{ + GdkPixbufLoader *loader; + gboolean result; + guchar *buf; + gsize count; + struct stat st; + + // Extract image from skin + fseek(fp, si->jpeg_offset, SEEK_SET); + fstat(fileno(fp), &st); + count = st.st_size - si->jpeg_offset; + + buf = g_malloc(count * sizeof(guchar)); + count = fread(buf, sizeof(guchar), count, fp); + + // Feed the pixbuf loader with our jpeg data + loader = gdk_pixbuf_loader_new(); + result = gdk_pixbuf_loader_write(loader, buf, count, err); + g_free(buf); + + if(result == FALSE) { + g_object_unref(loader); + return -1; + } + + result = gdk_pixbuf_loader_close(loader, err); + if(result == FALSE) { + g_object_unref(loader); + return -1; + } + + // and get the pixbuf + si->raw = gdk_pixbuf_loader_get_pixbuf(loader); + if(si->raw == NULL) { + g_set_error(err, SKIN_ERROR, SKIN_ERROR_INVALID, + "Unable to load background image"); + g_object_unref(loader); + return -1; + } + + si->sx = si->sy = 1.0; + si->image = g_object_ref(si->raw); + g_object_ref(si->raw); + + // Get new skin size + si->width = gdk_pixbuf_get_width(si->image); + si->height = gdk_pixbuf_get_height(si->image); + + g_object_unref(loader); + + return 0; +} + +/* Load a skin (TilEm v2.00 only) */ +int skin_load(SKIN_INFOS *si, const char *filename, GError **err) +{ + FILE *fp; + int ret = 0, errnum; + char *dname; + + g_return_val_if_fail(err == NULL || *err == NULL, -1); + + fp = g_fopen(filename, "rb"); + if (fp == NULL) { + errnum = errno; + dname = g_filename_display_basename(filename); + g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errnum), + "Unable to open %s for reading: %s", + dname, g_strerror(errnum)); + g_free(dname); + return -1; + } + + ret = skin_read_header(si, fp); + if (ret) { + fclose(fp); + dname = g_filename_display_basename(filename); + g_set_error(err, SKIN_ERROR, SKIN_ERROR_INVALID, + "The file %s is not a valid skin.", dname); + g_free(dname); + return -1; + } + + ret = skin_read_image(si, fp, err); + + fclose(fp); + + return ret; +} + +/* Unload skin by freeing allocated memory */ +int skin_unload(SKIN_INFOS *si) +{ + if (si->image != NULL) { + g_object_unref(si->image); + si->image = NULL; + } + + if (si->raw) { + g_object_unref(si->raw); + si->raw = NULL; + } + + free(si->name); + free(si->author); + + memset(si, 0, sizeof(SKIN_INFOS)); + + return 0; +} + diff --git a/tool/tilem-src/gui/skinops.h b/tool/tilem-src/gui/skinops.h new file mode 100644 index 0000000..8c5c5a6 --- /dev/null +++ b/tool/tilem-src/gui/skinops.h @@ -0,0 +1,133 @@ +/* Hey EMACS -*- linux-c -*- */ +/* $Id$ */ + +/* + * skinedit - a skin editor for the TiEmu emulator + * Copyright (C) 2002 Julien BLACHE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* +From Romain Lievins(?) : + Most of these definitions and code comes from the JB's SkinEdit + which is based on TiEmu skin code. TiEmu skin code is also based on + VTi's skin code. + +contra-sh : + This file is a (quasi ?) perfect copy of the tiemu skinops.h file ... + Thank's to Romain Lievins and Julien Blache for this wonderful work. + +*/ + + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + + +/***************/ +/* Definitions */ +/***************/ + +#define LCD_COLORTYPE_LOW 0 +#define LCD_COLORTYPE_HIGH 1 +#define LCD_COLORTYPE_CUSTOM 2 + +#define LCD_HI_WHITE 0xb0ccae +#define LCD_HI_BLACK 0x8a6f53 + +#define LCD_LOW_WHITE 0xcfe0cc +#define LCD_LOW_BLACK 0x222e31 + +#define MAX_COLORS (256 - 16) // we need to keep 16 colors for grayscales +#define SKIN_KEYS 80 + +#define SKIN_TI73 "TI-73" +#define SKIN_TI82 "TI-82" +#define SKIN_TI83 "TI-83" +#define SKIN_TI83P "TI-83+" +#define SKIN_TI85 "TI-85" +#define SKIN_TI86 "TI-86" +#define SKIN_TI89 "TI-89" +#define SKIN_TI92 "TI-92" +#define SKIN_TI92P "TI-92+" +#define SKIN_V200 "V200PLT" +#define SKIN_TI89T "TI-89TM" + +#define SKIN_TYPE_TIEMU 10 +#define SKIN_TYPE_VTI 2 +#define SKIN_TYPE_OLD_VTI 1 +#define SKIN_TYPE_NEW 0 + +#define ENDIANNESS_FLAG 0xfeedbabe +#define TIEMU_SKIN_ID "TiEmu v2.00" + + + +/*********/ +/* Types */ +/*********/ + + +typedef struct +{ + uint32_t left; + uint32_t top; + uint32_t right; + uint32_t bottom; +} RECT; + + +typedef struct +{ + int type; + + GdkPixbuf *image; + + int width; + int height; + + GdkPixbuf *raw; // raw jpeg image + double sx, sy; // scaling factor + + char calc[9]; + uint32_t colortype; + + uint32_t lcd_black; + uint32_t lcd_white; + + char *name; + char *author; + + RECT lcd_pos; + RECT keys_pos[SKIN_KEYS]; + + long jpeg_offset; + +} SKIN_INFOS; + +/*************/ +/* Functions */ +/*************/ + +int skin_load(SKIN_INFOS *infos, const char *filename, GError **err); +int skin_unload(SKIN_INFOS *infos); + diff --git a/tool/tilem-src/gui/ti81prg.c b/tool/tilem-src/gui/ti81prg.c new file mode 100644 index 0000000..afd407e --- /dev/null +++ b/tool/tilem-src/gui/ti81prg.c @@ -0,0 +1,410 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "ti81prg.h" + +#define tSpace 0x56 +#define t0 0x10 +#define t9 0x19 +#define tDecPt 0x1A +#define tA 0x59 +#define tZ 0x72 +#define tTheta 0x73 + +#define ramStart 0xE000 +#define cxCurApp 0xE347 +#define cxOldApp (cxCurApp + 1) +#define DimX_old 0xEEF9 +#define DimX_new 0xF12D +#define prgm0Name 0xF1D3 +#define prgm0Start 0xF2FB +#define prgm0End (prgm0Start + 2) +#define prgmThetaEnd (prgm0End + 36*2) +#define progMem 0xF347 +#define progMemEnd 0xFCA6 + +#define cxPrgmEdit 2 +#define cxPrgmExec 10 +#define cxMenu 11 + +TI81Program * ti81_program_new(int size) +{ + TI81Program *prgm = tilem_new0(TI81Program, 1); + + prgm->info.slot = TI81_SLOT_AUTO; + memset(prgm->info.name, tSpace, 8); + + if (size > 0) { + prgm->info.size = size; + prgm->data = tilem_new_atomic(byte, size); + } + + return prgm; +} + +void ti81_program_free(TI81Program *prgm) +{ + if (!prgm) + return; + + if (prgm->data) + tilem_free(prgm->data); + tilem_free(prgm); +} + +static byte *get_byte_ptr(const TilemCalc *calc, dword addr) +{ + if (addr < ramStart || addr > 0xffff) + return NULL; + + return &calc->ram[addr - ramStart]; +} + +static int read_byte(const TilemCalc *calc, dword addr) +{ + const byte *p = get_byte_ptr(calc, addr); + + if (!p) + return -1; + else + return *p; +} + +static dword read_word(const TilemCalc *calc, dword addr) +{ + const byte *p = get_byte_ptr(calc, addr); + + if (!p) + return 0; + else + return (p[0] | p[1] << 8); +} + +static void write_word(TilemCalc *calc, dword addr, dword value) +{ + byte *p = get_byte_ptr(calc, addr); + + if (p) { + p[0] = value & 0xff; + p[1] = (value >> 8) & 0xff; + } +} + +static int check_busy(const TilemCalc *calc) +{ + int cur, old; + + cur = read_byte(calc, cxCurApp); + old = read_byte(calc, cxCurApp); + + if (cur == cxPrgmEdit || cur == cxPrgmExec) + return 1; + else if (cur == cxMenu && (old == cxPrgmEdit || old == cxPrgmExec)) + return 1; + else + return 0; +} + +static dword get_free_mem_end(const TilemCalc *calc) +{ + const byte *p; + int n, i; + + p = get_byte_ptr(calc, DimX_new); + + /* DimX is always a small positive integer, so the first byte + must be between 80h and 82h, and the last five bytes must + always be zero. On 1.1K, DimX_new is part of textShadow, + so none of these byte values make any sense. */ + + if (p[0] < 0x80 || p[0] > 0x82 || p[7] != 0) + p = get_byte_ptr(calc, DimX_old); + + if (p[0] < 0x80 || p[0] > 0x82) + return 0; + + for (i = 3; i < 7; i++) + if (p[i]) + return 0; + + n = ((p[2] & 0xf) + + ((p[2] >> 4) * 10) + + ((p[1] & 0xf) * 100) + + ((p[1] >> 4) * 1000)); + + for (i = p[0]; i < 0x83; i++) { + if (n % 10) + return 0; + n /= 10; + } + + return (progMemEnd + 1 - 16 * n); +} + +int ti81_get_program_info(const TilemCalc *calc, int slot, TI81ProgInfo *info) +{ + const byte *p; + dword progstart, progend; + + if (slot < 0 || slot > TI81_SLOT_MAX) + return TI81_ERR_INTERNAL; + + if (check_busy(calc)) + return TI81_ERR_BUSY; + + progstart = read_word(calc, prgm0Start + 2 * slot); + progend = read_word(calc, prgm0Start + 2 * slot + 2); + + if (progstart < ramStart || progend < ramStart || progend < progstart) + return TI81_ERR_BUSY; + + info->slot = slot; + info->size = progend - progstart; + info->addr = progstart; + + p = get_byte_ptr(calc, prgm0Name + 8 * slot); + if (!p) return TI81_ERR_INTERNAL; + memcpy(info->name, p, 8); + + return 0; +} + +int ti81_get_program(const TilemCalc *calc, int slot, TI81Program **prgm) +{ + TI81ProgInfo info; + const byte *p; + int s; + + if ((s = ti81_get_program_info(calc, slot, &info))) { + *prgm = NULL; + return s; + } + + *prgm = ti81_program_new(info.size); + (*prgm)->info = info; + if (info.size > 0 && (p = get_byte_ptr(calc, info.addr))) + memcpy((*prgm)->data, p, info.size); + + return 0; +} + +int ti81_load_program(TilemCalc *calc, const TI81Program *prgm) +{ + TI81ProgInfo info; + int slot = prgm->info.slot; + int s, i; + dword progs_start, progs_end, mem_end, x; + byte *p; + + if (slot == TI81_SLOT_AUTO) { + for (slot = 0; slot <= TI81_SLOT_MAX; slot++) { + if ((s = ti81_get_program_info(calc, slot, &info))) + return s; + if (info.size == 0 && info.name[0] == tSpace) + break; + } + + if (slot > TI81_SLOT_MAX) + return TI81_ERR_SLOTS_FULL; + } + + if ((s = ti81_get_program_info(calc, slot, &info))) + return s; + + /* move later programs forward/backward in memory */ + + progs_start = info.addr + info.size; + progs_end = read_word(calc, prgmThetaEnd); + if (progs_end < progs_start) + return TI81_ERR_BUSY; + + mem_end = get_free_mem_end(calc); + if (progs_end + prgm->info.size - info.size > mem_end) + return TI81_ERR_MEMORY; + + if (prgm->info.size != info.size && progs_start != progs_end) { + p = get_byte_ptr(calc, progs_start); + if (!p) return TI81_ERR_INTERNAL; + memmove(p + prgm->info.size - info.size, p, + progs_end - progs_start); + } + + /* update program pointers */ + + for (i = slot; i <= TI81_SLOT_MAX; i++) { + x = read_word(calc, prgm0End + 2 * i); + write_word(calc, prgm0End + 2 * i, + x + prgm->info.size - info.size); + } + + /* copy program data */ + + if (prgm->info.size != 0) { + p = get_byte_ptr(calc, info.addr); + if (!p) return TI81_ERR_INTERNAL; + memcpy(p, prgm->data, prgm->info.size); + } + + /* copy program name */ + + p = get_byte_ptr(calc, prgm0Name + 8 * slot); + if (!p) return TI81_ERR_INTERNAL; + memcpy(p, prgm->info.name, 8); + + return 0; +} + +int ti81_read_prg_file(FILE *f, TI81Program **prgm) +{ + byte buf[20]; + unsigned int size, i; + unsigned int sum = 0; + TI81Program *p; + + *prgm = NULL; + + if (fread(buf, 1, 20, f) != 20) + return TI81_ERR_INVALID_FILE; + + if (strcmp((char *) buf, "**TI81**") || buf[9] != 0x6e) + return TI81_ERR_INVALID_FILE; + + size = buf[10] | buf[11] << 8; + + p = ti81_program_new(size); + + memcpy(p->info.name, buf + 12, 8); + + for (i = 0; i < 8; i++) + sum += buf[12 + i]; + + if (fread(p->data, 1, size, f) != size) { + ti81_program_free(p); + return TI81_ERR_INVALID_FILE; + } + + for (i = 0; i < size; i++) + sum += p->data[i]; + + if (fread(buf, 1, 2, f) != 2) { + ti81_program_free(p); + return TI81_ERR_INVALID_FILE; + } + + sum -= (buf[0] | buf[1] << 8); + if (sum & 0xffff) + fprintf(stderr, "warning: checksum incorrect\n"); + + *prgm = p; + return 0; +} + +int ti81_write_prg_file(FILE *f, const TI81Program *prgm) +{ + byte buf[20]; + unsigned int size, i; + unsigned int sum = 0; + + memcpy(buf, "**TI81**\0n", 10); + size = prgm->info.size; + buf[10] = size & 0xff; + buf[11] = (size >> 8) & 0xff; + + memcpy(buf + 12, prgm->info.name, 8); + + for (i = 0; i < 8; i++) + sum += buf[12 + i]; + + if (fwrite(buf, 1, 20, f) != 20) + return TI81_ERR_FILE_IO; + + if (fwrite(prgm->data, 1, size, f) != size) + return TI81_ERR_FILE_IO; + + for (i = 0; i < size; i++) + sum += prgm->data[i]; + + buf[0] = sum & 0xff; + buf[1] = (sum >> 8) & 0xff; + if (fwrite(buf, 1, 2, f) != 2) + return TI81_ERR_FILE_IO; + + return 0; +} + +char * ti81_program_slot_to_string(int slot) +{ + char buf[50]; + char *s; + + if (slot == TI81_SLOT_AUTO) + strcpy(buf, "Automatic"); + else if (slot < 0 || slot > 36) + strcpy(buf, "?"); + else if (slot < 10) + sprintf(buf, "Prgm%c", slot + '0'); + else if (slot < 36) + sprintf(buf, "Prgm%c", slot + 'A' - 10); + else + strcpy(buf, "Prgm\316\270"); + + s = tilem_new_atomic(char, strlen(buf) + 1); + strcpy(s, buf); + return s; +} + +char * ti81_program_name_to_string(const byte *prgname) +{ + char buf[50]; + char *s; + int i, j; + + for (i = j = 0; i < 8; i++) { + if (prgname[i] == tSpace) + buf[j++] = '_'; + else if (prgname[i] == tDecPt) + buf[j++] = '.'; + else if (prgname[i] == tTheta) { + buf[j++] = '\316'; + buf[j++] = '\270'; + } + else if (prgname[i] >= t0 && prgname[i] <= t9) + buf[j++] = '0' + prgname[i] - t0; + else if (prgname[i] >= tA && prgname[i] <= tZ) + buf[j++] = 'A' + prgname[i] - tA; + else + buf[j++] = '?'; + } + + while (j > 0 && buf[j - 1] == '_') + j--; + buf[j] = 0; + + s = tilem_new_atomic(char, strlen(buf) + 1); + strcpy(s, buf); + return s; +} diff --git a/tool/tilem-src/gui/ti81prg.h b/tool/tilem-src/gui/ti81prg.h new file mode 100644 index 0000000..fc65224 --- /dev/null +++ b/tool/tilem-src/gui/ti81prg.h @@ -0,0 +1,89 @@ +/* + * TilEm II + * + * Copyright (c) 2011 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +enum { + TI81_SLOT_AUTO = -1, + TI81_SLOT_0, TI81_SLOT_1, TI81_SLOT_2, TI81_SLOT_3, TI81_SLOT_4, + TI81_SLOT_5, TI81_SLOT_6, TI81_SLOT_7, TI81_SLOT_8, TI81_SLOT_9, + TI81_SLOT_A, TI81_SLOT_B, TI81_SLOT_C, TI81_SLOT_D, TI81_SLOT_E, + TI81_SLOT_F, TI81_SLOT_G, TI81_SLOT_H, TI81_SLOT_I, TI81_SLOT_J, + TI81_SLOT_K, TI81_SLOT_L, TI81_SLOT_M, TI81_SLOT_N, TI81_SLOT_O, + TI81_SLOT_P, TI81_SLOT_Q, TI81_SLOT_R, TI81_SLOT_S, TI81_SLOT_T, + TI81_SLOT_U, TI81_SLOT_V, TI81_SLOT_W, TI81_SLOT_X, TI81_SLOT_Y, + TI81_SLOT_Z, TI81_SLOT_THETA +}; + +#define TI81_SLOT_MAX TI81_SLOT_THETA + +typedef struct _TI81ProgInfo { + int slot; /* program slot number */ + int size; /* size of program contents */ + dword addr; /* address of program contents */ + byte name[8]; /* program name, tokens */ +} TI81ProgInfo; + +typedef struct _TI81Program { + TI81ProgInfo info; + byte *data; +} TI81Program; + +/* Error codes */ +enum { + TI81_ERR_FILE_IO = 1, /* File I/O error */ + TI81_ERR_INVALID_FILE, /* PRG file is invalid */ + TI81_ERR_MEMORY, /* Not enough memory to load program */ + TI81_ERR_SLOTS_FULL, /* No free program slots */ + TI81_ERR_BUSY, /* Calculator is busy and unable + to load/save programs */ + TI81_ERR_INTERNAL +}; + +/* Create a new TI81Program with the given size. */ +TI81Program * ti81_program_new(int size) + TILEM_ATTR_MALLOC; + +/* Free a TI81Program. */ +void ti81_program_free(TI81Program *prgm); + +/* Get information about the program in the given slot. */ +int ti81_get_program_info(const TilemCalc *calc, int slot, TI81ProgInfo *info); + +/* Retrieve a program from calculator memory. Free the resulting + program with ti81_program_free() when you're done with it. */ +int ti81_get_program(const TilemCalc *calc, int slot, TI81Program **prgm); + +/* Load a program into calculator memory. */ +int ti81_load_program(TilemCalc *calc, const TI81Program *prgm); + +/* Read a program from a PRG file. Free the resulting program with + ti81_program_free() when you're done with it. */ +int ti81_read_prg_file(FILE *f, TI81Program **prgm); + +/* Write a program to a PRG file. */ +int ti81_write_prg_file(FILE *f, const TI81Program *prgm); + +/* Convert program slot number into a UTF-8 string. Free the result + with tilem_free() when you're done with it. */ +char * ti81_program_slot_to_string(int slot) + TILEM_ATTR_MALLOC; + +/* Convert program name to a UTF-8 string. Free the result with + tilem_free() when you're done with it. */ +char * ti81_program_name_to_string(const byte *prgname) + TILEM_ATTR_MALLOC; diff --git a/tool/tilem-src/gui/tilem2.c b/tool/tilem-src/gui/tilem2.c new file mode 100644 index 0000000..4be876a --- /dev/null +++ b/tool/tilem-src/gui/tilem2.c @@ -0,0 +1,309 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010-2012 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "gui.h" +#include "files.h" +#include "icons.h" +#include "msgbox.h" + +/* CMD LINE OPTIONS */ +static gchar* cl_romfile = NULL; +static gchar* cl_skinfile = NULL; +static gchar* cl_model = NULL; +static gchar* cl_statefile = NULL; +static gchar** cl_files_to_load = NULL; +static gboolean cl_skinless_flag = FALSE; +static gboolean cl_reset_flag = FALSE; +static gchar* cl_getvar = NULL; +static gchar* cl_macro_to_run = NULL; +static gboolean cl_debug_flag = FALSE; +static gboolean cl_normalspeed_flag = FALSE; +static gboolean cl_fullspeed_flag = FALSE; + + +static GOptionEntry entries[] = +{ + { "rom", 'r', 0, G_OPTION_ARG_FILENAME, &cl_romfile, "The rom file to run", "FILE" }, + { "skin", 'k', 0, G_OPTION_ARG_FILENAME, &cl_skinfile, "The skin file to use", "FILE" }, + { "model", 'm', 0, G_OPTION_ARG_STRING, &cl_model, "The model to use", "NAME" }, + { "state-file", 's', 0, G_OPTION_ARG_FILENAME, &cl_statefile, "The state-file to use", "FILE" }, + { "without-skin", 'l', 0, G_OPTION_ARG_NONE, &cl_skinless_flag, "Start in skinless mode", NULL }, + { "reset", 0, 0, G_OPTION_ARG_NONE, &cl_reset_flag, "Reset the calc at startup", NULL }, + { "get-var", 0, 0, G_OPTION_ARG_STRING, &cl_getvar, "Get a var at startup", "FILE" }, + { "play-macro", 'p', 0, G_OPTION_ARG_FILENAME, &cl_macro_to_run, "Run this macro at startup", "FILE" }, + { "debug", 'd', 0, G_OPTION_ARG_NONE, &cl_debug_flag, "Launch debugger", NULL }, + { "normal-speed", 0, 0, G_OPTION_ARG_NONE, &cl_normalspeed_flag, "Run at normal speed", NULL }, + { "full-speed", 0, 0, G_OPTION_ARG_NONE, &cl_fullspeed_flag, "Run at maximum speed", NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &cl_files_to_load, NULL, "FILE" }, + { 0, 0, 0, 0, 0, 0, 0 } +}; + + +/* ######### MAIN ######### */ + +/* Order of preference for automatic model selection. */ +static const char model_search_order[] = + { TILEM_CALC_TI81, + TILEM_CALC_TI73, + TILEM_CALC_TI82, + TILEM_CALC_TI83, + TILEM_CALC_TI76, + TILEM_CALC_TI84P_SE, + TILEM_CALC_TI84P, + TILEM_CALC_TI83P_SE, + TILEM_CALC_TI83P, + TILEM_CALC_TI84P_NSPIRE, + TILEM_CALC_TI85, + TILEM_CALC_TI86, 0 }; + +/* Check if given calc model should be used for these file types. */ +static gboolean check_file_types(int calc_model, + const int *file_models, + int nfiles) +{ + /* Only choose a calc model if it supports all of the given + file types, and at least one of the files is of the calc's + "preferred" type. This means if we have a mixture of 82Ps + and 83Ps, we can use either a TI-83 or TI-76.fr ROM image, + but not a TI-83 Plus. */ + + gboolean preferred = FALSE; + int i; + + calc_model = model_to_base_model(calc_model); + + for (i = 0; i < nfiles; i++) { + if (file_models[i] == calc_model) + preferred = TRUE; + else if (!model_supports_file(calc_model, file_models[i])) + return FALSE; + } + + return preferred; +} + +static void load_initial_rom(TilemCalcEmulator *emu, + const char *cmdline_rom_name, + const char *cmdline_state_name, + char **cmdline_files, + int model) +{ + GError *err = NULL; + char *modelname; + int nfiles, *file_models, i; + + /* If a ROM file is specified on the command line, use that + (and no other) */ + + if (cmdline_rom_name) { + if (tilem_calc_emulator_load_state(emu, cmdline_rom_name, + cmdline_state_name, + model, &err)) + return; + else if (!err) + exit(0); + else { + g_printerr("%s\n", err->message); + exit(1); + } + } + + /* Choose model by file names */ + + if (!model && cmdline_files) { + nfiles = g_strv_length(cmdline_files); + file_models = g_new(int, nfiles); + + /* determine model for each filename */ + for (i = 0; i < nfiles; i++) + file_models[i] = file_to_model(cmdline_files[i]); + + /* iterate over all known models... */ + for (i = 0; model_search_order[i]; i++) { + model = model_search_order[i]; + + /* check if this model supports the named files */ + if (!check_file_types(model, file_models, nfiles)) + continue; + + /* try to load model, but no error message if + no ROM is present in config */ + if (tilem_calc_emulator_load_state(emu, NULL, NULL, + model, &err)) { + g_free(file_models); + return; + } + else if (!err) + exit(0); + else if (!g_error_matches(err, TILEM_EMULATOR_ERROR, + TILEM_EMULATOR_ERROR_NO_ROM)) { + messagebox01(NULL, GTK_MESSAGE_ERROR, + "Unable to load calculator state", + "%s", err->message); + } + g_clear_error(&err); + } + + g_free(file_models); + model = 0; + } + + /* If no model specified on command line (either explicitly or + implicitly), then choose the most recently used model */ + + if (!model && !cmdline_files) { + tilem_config_get("recent", "last_model/s", &modelname, NULL); + if (modelname) + model = name_to_model(modelname); + } + + /* Try to load the most recently used ROM for chosen model */ + + if (model) { + if (tilem_calc_emulator_load_state(emu, NULL, NULL, + model, &err)) + return; + else if (!err) + exit(0); + else { + messagebox01(NULL, GTK_MESSAGE_ERROR, + "Unable to load calculator state", + "%s", err->message); + g_clear_error(&err); + } + } + + /* Prompt user for a ROM file */ + + while (!emu->calc) { + if (!tilem_calc_emulator_prompt_open_rom(emu)) + exit(0); + } +} + +int main(int argc, char **argv) +{ + TilemCalcEmulator* emu; + char *menurc_path; + GOptionContext *context; + GError *error = NULL; + int model = 0; + + g_thread_init(NULL); + gtk_init(&argc, &argv); + set_program_path(argv[0]); + g_set_application_name("TilEm"); + + menurc_path = get_shared_file_path("menurc", NULL); + if (menurc_path) + gtk_accel_map_load(menurc_path); + g_free(menurc_path); + + init_custom_icons(); + gtk_window_set_default_icon_name("tilem"); + + emu = tilem_calc_emulator_new(); + + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, entries, NULL); + g_option_context_add_group(context, gtk_get_option_group(TRUE)); + if (!g_option_context_parse(context, &argc, &argv, &error)) + { + g_printerr("%s: %s\n", g_get_prgname(), error->message); + exit (1); + } + + if (cl_model) { + model = name_to_model(cl_model); + if (!model) { + g_printerr("%s: unknown model %s\n", + g_get_prgname(), cl_model); + return 1; + } + } + + load_initial_rom(emu, cl_romfile, cl_statefile, cl_files_to_load, model); + + emu->ewin = tilem_emulator_window_new(emu); + + if (cl_skinless_flag) + tilem_emulator_window_set_skin_disabled(emu->ewin, TRUE); + else if (cl_skinfile) { + tilem_emulator_window_set_skin(emu->ewin, cl_skinfile); + tilem_emulator_window_set_skin_disabled(emu->ewin, FALSE); + } + + gtk_widget_show(emu->ewin->window); + + ticables_library_init(); + tifiles_library_init(); + ticalcs_library_init(); + + if (cl_reset_flag) + tilem_calc_emulator_reset(emu); + + if (cl_fullspeed_flag) + tilem_calc_emulator_set_limit_speed(emu, FALSE); + else if (cl_normalspeed_flag) + tilem_calc_emulator_set_limit_speed(emu, TRUE); + + if (cl_files_to_load) + load_files_cmdline(emu->ewin, cl_files_to_load); + if (cl_macro_to_run) + tilem_macro_load(emu, cl_macro_to_run); + if (cl_getvar) + tilem_link_receive_matching(emu, cl_getvar, "."); + + if (cl_debug_flag) + launch_debugger(emu->ewin); + else + tilem_calc_emulator_run(emu); + + g_signal_connect(emu->ewin->window, "destroy", + G_CALLBACK(gtk_main_quit), NULL); + + gtk_main(); + + tilem_calc_emulator_pause(emu); + + tilem_emulator_window_free(emu->ewin); + tilem_calc_emulator_free(emu); + + menurc_path = get_config_file_path("menurc", NULL); + gtk_accel_map_save(menurc_path); + g_free(menurc_path); + + ticables_library_exit(); + tifiles_library_exit(); + ticalcs_library_exit(); + + return 0; +} diff --git a/tool/tilem-src/gui/tilem2.ico b/tool/tilem-src/gui/tilem2.ico new file mode 100644 index 0000000..633d49c Binary files /dev/null and b/tool/tilem-src/gui/tilem2.ico differ diff --git a/tool/tilem-src/gui/tilem2.rc.in b/tool/tilem-src/gui/tilem2.rc.in new file mode 100644 index 0000000..7e451ad --- /dev/null +++ b/tool/tilem-src/gui/tilem2.rc.in @@ -0,0 +1,32 @@ +#include + +101 ICON "@srcdir@/tilem2.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION BUILD_VERSION + PRODUCTVERSION BUILD_VERSION + FILEFLAGSMASK 0 + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "Linux Programmer Group" + VALUE "FileDescription", "TilEm" + VALUE "FileVersion", "@PACKAGE_VERSION@" + VALUE "InternalName", "tilem2" + VALUE "LegalCopyright", "Copyright \xa9 2012 The TilEm Project" + VALUE "OriginalFilename", "tilem2.exe" + VALUE "ProductName", "@PACKAGE_NAME@" + VALUE "ProductVersion", "@PACKAGE_VERSION@" + VALUE "Comments", "Licensed under the GNU GPLv3+ (see http://www.gnu.org/licenses/.)" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END + END diff --git a/tool/tilem-src/gui/tool.c b/tool/tilem-src/gui/tool.c new file mode 100644 index 0000000..417df05 --- /dev/null +++ b/tool/tilem-src/gui/tool.c @@ -0,0 +1,433 @@ +/* + * TilEm II + * + * Copyright (c) 2010-2011 Thibault Duponchelle + * Copyright (c) 2010 Benjamin Moody + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "gui.h" + +/* Create a frame around the given widget, with a boldface label in + the GNOME style */ +GtkWidget* new_frame(const gchar* label, GtkWidget* contents) +{ + GtkWidget *frame, *align; + char *str; + + str = g_strconcat("", label, "", NULL); + frame = gtk_frame_new(str); + g_free(str); + + g_object_set(gtk_frame_get_label_widget(GTK_FRAME(frame)), + "use-markup", TRUE, NULL); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); + + align = gtk_alignment_new(0.5, 0.5, 1.0, 1.0); + gtk_alignment_set_padding(GTK_ALIGNMENT(align), 6, 0, 12, 0); + gtk_widget_show(align); + gtk_container_add(GTK_CONTAINER(frame), align); + gtk_container_add(GTK_CONTAINER(align), contents); + gtk_widget_show(frame); + + return frame; +} + +/* Get model name (abbreviation) for a TilEm model ID. */ +const char * model_to_name(int model) +{ + const TilemHardware **models; + int nmodels, i; + + tilem_get_supported_hardware(&models, &nmodels); + for (i = 0; i < nmodels; i++) + if (models[i]->model_id == model) + return models[i]->name; + + return NULL; +} + +/* Convert model name to a model ID. */ +int name_to_model(const char *name) +{ + char *s; + const TilemHardware **models; + int nmodels, i, j; + + s = g_new(char, strlen(name) + 1); + for (i = j = 0; name[i]; i++) { + if (name[i] == '+') + s[j++] = 'p'; + else if (name[i] != '-') + s[j++] = g_ascii_tolower(name[i]); + } + s[j] = 0; + + tilem_get_supported_hardware(&models, &nmodels); + for (i = 0; i < nmodels; i++) { + if (!strcmp(s, models[i]->name)) { + g_free(s); + return models[i]->model_id; + } + } + + g_free(s); + return 0; +} + +/* Convert TilEm model ID to tifiles2 model ID. */ +CalcModel model_to_calcmodel(int model) +{ + switch (model) { + case TILEM_CALC_TI73: + return CALC_TI73; + + case TILEM_CALC_TI82: + return CALC_TI82; + + case TILEM_CALC_TI83: + case TILEM_CALC_TI76: + return CALC_TI83; + + case TILEM_CALC_TI83P: + case TILEM_CALC_TI83P_SE: + return CALC_TI83P; + + case TILEM_CALC_TI84P: + case TILEM_CALC_TI84P_SE: + case TILEM_CALC_TI84P_NSPIRE: + return CALC_TI84P; + + case TILEM_CALC_TI85: + return CALC_TI85; + + case TILEM_CALC_TI86: + return CALC_TI86; + + default: + return CALC_NONE; + } +} + +/* Convert tifiles2 model ID to TilEm model ID. */ +int calcmodel_to_model(CalcModel model) +{ + switch (model) { + case CALC_TI73: + return TILEM_CALC_TI73; + case CALC_TI82: + return TILEM_CALC_TI82; + case CALC_TI83: + return TILEM_CALC_TI83; + case CALC_TI83P: + return TILEM_CALC_TI83P; + case CALC_TI84P: + return TILEM_CALC_TI84P; + case CALC_TI85: + return TILEM_CALC_TI85; + case CALC_TI86: + return TILEM_CALC_TI86; + default: + return 0; + } +} + +/* Get model ID for a given file. */ +int file_to_model(const char *name) +{ + const char *p; + TigContent *tig; + int model; + + p = strrchr(name, '.'); + if (!p || strlen(p) < 4 || strchr(p, '/') || strchr(p, '\\')) + return 0; + p++; + + if (!g_ascii_strcasecmp(p, "prg")) + return TILEM_CALC_TI81; + + if (!g_ascii_strncasecmp(p, "73", 2)) + return TILEM_CALC_TI73; + if (!g_ascii_strncasecmp(p, "82", 2)) + return TILEM_CALC_TI82; + if (!g_ascii_strncasecmp(p, "83", 2)) + return TILEM_CALC_TI83; + if (!g_ascii_strncasecmp(p, "8x", 2)) + return TILEM_CALC_TI83P; + if (!g_ascii_strncasecmp(p, "85", 2)) + return TILEM_CALC_TI85; + if (!g_ascii_strncasecmp(p, "86", 2)) + return TILEM_CALC_TI86; + + if (!g_ascii_strcasecmp(p, "tig") + || !g_ascii_strcasecmp(p, "zip")) { + /* read file and see what tifiles thinks the type is */ + tig = tifiles_content_create_tigroup(CALC_NONE, 0); + tifiles_file_read_tigroup(name, tig); + model = calcmodel_to_model(tig->model); + tifiles_content_delete_tigroup(tig); + return model; + } + + return 0; +} + +/* Get "base" model for file type support. */ +int model_to_base_model(int calc_model) +{ + switch (calc_model) { + case TILEM_CALC_TI83: + case TILEM_CALC_TI76: + return TILEM_CALC_TI83; + + case TILEM_CALC_TI83P: + case TILEM_CALC_TI83P_SE: + case TILEM_CALC_TI84P: + case TILEM_CALC_TI84P_SE: + case TILEM_CALC_TI84P_NSPIRE: + return TILEM_CALC_TI83P; + + default: + return calc_model; + } +} + +/* Check if calc is compatible with given file type. */ +gboolean model_supports_file(int calc_model, int file_model) +{ + calc_model = model_to_base_model(calc_model); + file_model = model_to_base_model(file_model); + + if (file_model == calc_model) + return TRUE; + + if (file_model == TILEM_CALC_TI82 + && (calc_model == TILEM_CALC_TI83 + || calc_model == TILEM_CALC_TI83P)) + return TRUE; + + if (file_model == TILEM_CALC_TI83 + && (calc_model == TILEM_CALC_TI83P)) + return TRUE; + + if (file_model == TILEM_CALC_TI85 + && (calc_model == TILEM_CALC_TI86)) + return TRUE; + + return FALSE; +} + +/* A popup which is used to let the user choose the model at startup */ +char choose_rom_popup(GtkWidget *parent_window, const char *filename, + char default_model) +{ + const TilemHardware **models; + GtkWidget *dlg, *vbox, *frame, *btn; + GtkToggleButton **btns; + char *ids, id = 0; + int nmodels, noptions, i, j, defoption = 0, response; + dword romsize; + char *fn, *msg; + + tilem_get_supported_hardware(&models, &nmodels); + + /* determine ROM size for default model */ + for (i = 0; i < nmodels; i++) + if (models[i]->model_id == default_model) + break; + + g_return_val_if_fail(i < nmodels, 0); + + romsize = models[i]->romsize; + + /* all other models with same ROM size are candidates */ + noptions = 0; + for (i = 0; i < nmodels; i++) { + if (models[i]->model_id == default_model) + defoption = noptions; + if (models[i]->romsize == romsize) + noptions++; + } + + if (noptions < 2) /* no choice */ + return default_model; + + dlg = gtk_dialog_new_with_buttons("Select Calculator Type", + GTK_WINDOW(parent_window), + GTK_DIALOG_MODAL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + gtk_dialog_set_default_response(GTK_DIALOG(dlg), + GTK_RESPONSE_OK); + + vbox = gtk_vbox_new(TRUE, 0); + + /* create radio buttons */ + + btns = g_new(GtkToggleButton*, noptions); + ids = g_new(char, noptions); + btn = NULL; + for (i = j = 0; i < nmodels; i++) { + if (models[i]->romsize == romsize) { + btn = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON(btn), models[i]->desc); + btns[j] = GTK_TOGGLE_BUTTON(btn); + ids[j] = models[i]->model_id; + gtk_box_pack_start(GTK_BOX(vbox), btn, TRUE, TRUE, 3); + j++; + } + } + + gtk_toggle_button_set_active(btns[defoption], TRUE); + + fn = g_filename_display_basename(filename); + msg = g_strdup_printf("Calculator type for %s:", fn); + frame = new_frame(msg, vbox); + g_free(fn); + g_free(msg); + + gtk_container_set_border_width(GTK_CONTAINER(frame), 6); + gtk_widget_show_all(frame); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg)); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0); + + response = gtk_dialog_run(GTK_DIALOG(dlg)); + + if (response == GTK_RESPONSE_OK) { + for (i = 0; i < noptions; i++) { + if (gtk_toggle_button_get_active(btns[i])) { + id = ids[i]; + break; + } + } + } + else { + id = 0; + } + + gtk_widget_destroy(dlg); + g_free(btns); + g_free(ids); + + return id; +} + +/* Convert UTF-8 to filename encoding. Use ASCII digits in place of + subscripts if necessary. If conversion fails utterly, fall back to + the UTF-8 name, which is broken but better than nothing. */ +char * utf8_to_filename(const char *utf8str) +{ + gchar *result, *ibuf, *obuf, *p; + gsize icount, ocount; + const gchar **charsets; + GIConv ic; + gunichar c; + + if (g_get_filename_charsets(&charsets)) + return g_strdup(utf8str); + + ic = g_iconv_open(charsets[0], "UTF-8"); + if (!ic) { + g_warning("utf8_to_filename: unsupported charset %s", + charsets[0]); + return g_strdup(utf8str); + } + + ibuf = (gchar*) utf8str; + icount = strlen(utf8str); + ocount = icount * 2; /* be generous */ + result = obuf = g_new(gchar, ocount + 1); + + while (g_iconv(ic, &ibuf, &icount, &obuf, &ocount) == (gsize) -1) { + if (errno != EILSEQ) { + g_warning("utf8_to_filename: error in conversion"); + g_free(result); + g_iconv_close(ic); + return g_strdup(utf8str); + } + + c = g_utf8_get_char(ibuf); + if (c >= 0x2080 && c <= 0x2089) + *obuf = c - 0x2080 + '0'; + else + *obuf = '_'; + obuf++; + ocount--; + + p = g_utf8_next_char(ibuf); + icount -= p - ibuf; + ibuf = p; + } + + *obuf = 0; + g_iconv_close(ic); + return result; +} + +/* Convert UTF-8 to a subset of UTF-8 that is compatible with the + locale */ +char * utf8_to_restricted_utf8(const char *utf8str) +{ + char *p, *q; + p = utf8_to_filename(utf8str); + q = g_filename_to_utf8(p, -1, NULL, NULL, NULL); + g_free(p); + if (q) + return q; + else + return g_strdup(utf8str); +} + +/* Generate default filename (UTF-8) for a variable */ +char * get_default_filename(const TilemVarEntry *tve) +{ + GString *str = g_string_new(""); + + if (tve->slot_str) { + g_string_append(str, tve->slot_str); + if (tve->name_str && tve->name_str[0]) { + g_string_append_c(str, '-'); + g_string_append(str, tve->name_str); + } + } + else if (tve->name_str && tve->name_str[0]) { + g_string_append(str, tve->name_str); + } + else { + g_string_append(str, "untitled"); + } + g_string_append_c(str, '.'); + g_string_append(str, tve->file_ext); + return g_string_free(str, FALSE); +} diff --git a/tool/tilem-src/install-sh b/tool/tilem-src/install-sh new file mode 100755 index 0000000..6781b98 --- /dev/null +++ b/tool/tilem-src/install-sh @@ -0,0 +1,520 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2009-04-28.21; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + trap '(exit $?); exit' 1 2 13 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dst_arg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writeable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + -*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test -z "$d" && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/tool/tilem-src/installer/win32/COPYING-PIXMAN b/tool/tilem-src/installer/win32/COPYING-PIXMAN new file mode 100644 index 0000000..42c4912 --- /dev/null +++ b/tool/tilem-src/installer/win32/COPYING-PIXMAN @@ -0,0 +1,39 @@ +License for pixman +------------------ + + Copyright 1987, 1988, 1989, 1998 The Open Group + Copyright 1987, 1988, 1989 Digital Equipment Corporation + Copyright 1999, 2004, 2008 Keith Packard + Copyright 2000 SuSE, Inc. + Copyright 2000 Keith Packard, member of The XFree86 Project, Inc. + Copyright 2004, 2005, 2007, 2008, 2009, 2010 Red Hat, Inc. + Copyright 2004 Nicholas Miell + Copyright 2005 Lars Knoll & Zack Rusin, Trolltech + Copyright 2005 Trolltech AS + Copyright 2007 Luca Barbato + Copyright 2008 Aaron Plattner, NVIDIA Corporation + Copyright 2008 Rodrigo Kumpera + Copyright 2008 André Tupinambá + Copyright 2008 Mozilla Corporation + Copyright 2008 Frederic Plourde + Copyright 2009, Oracle and/or its affiliates. All rights reserved. + Copyright 2009, 2010 Nokia Corporation + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. diff --git a/tool/tilem-src/installer/win32/COPYING-ZLIB b/tool/tilem-src/installer/win32/COPYING-ZLIB new file mode 100644 index 0000000..ad5a38c --- /dev/null +++ b/tool/tilem-src/installer/win32/COPYING-ZLIB @@ -0,0 +1,23 @@ +License for zlib +---------------- + + (C) 1995-2010 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu diff --git a/tool/tilem-src/installer/win32/Makefile.in b/tool/tilem-src/installer/win32/Makefile.in new file mode 100644 index 0000000..898f1b9 --- /dev/null +++ b/tool/tilem-src/installer/win32/Makefile.in @@ -0,0 +1,184 @@ +MAKENSIS = @MAKENSIS@ +STRIP = @STRIP@ +OBJDUMP = @OBJDUMP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +SHELL = @SHELL@ +LN_S = @LN_S@ + +abs_builddir = @abs_builddir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +@SET_MAKE@ + +main_program = tilem2.exe + +docs = README KEYS NEWS COPYING CHANGELOG THANKS +extra_docs = COPYING-PIXMAN COPYING-ZLIB + +install_files = $(main_program) *.dll *.txt +install_subdirs = etc lib share + +extra_gtk_files = lib/gtk-2.0/2.10.0/engines/libwimp.dll +extra_dlls = + +GTK_BINDIR = @GTK_BINDIR@ +TICALCS_BINDIR = @TICALCS_BINDIR@ +DLLPATH = @DLLPATH@ +PATH_SEPARATOR = @PATH_SEPARATOR@ + +system_dlls = advapi32.dll cfgmgr32.dll comctl32.dll comdlg32.dll dnsapi.dll \ + gdi32.dll gdiplus.dll imm32.dll kernel32.dll msimg32.dll \ + msvcrt.dll ole32.dll setupapi.dll shell32.dll shlwapi.dll \ + user32.dll usp10.dll winspool.drv ws2_32.dll wsock32.dll + +all: install + +#### Build the installer + +dist: install installer.nsi + rm -f files.nsi rmfiles.nsi rmdirs.nsi + set -e ; for file in $(install_files) ; do \ + name=`echo "$$file" | sed 's,/,\\\\,g'` ; \ + echo "File \"$$name\"" >> files.nsi ; \ + echo "Delete \"\$$INSTDIR\\$$name\"" >> rmfiles.nsi ; \ + done + set -e ; for dir in $(install_subdirs) ; do \ + if [ -d $$dir ] ; then \ + echo "File /r \"$$dir\"" >> files.nsi ; \ + fi ; \ + done + set -e ; LC_ALL=C ; export LC_ALL ; \ + for file in `find -L $(install_subdirs) | sort -r` ; do \ + name=`echo "$$file" | sed 's,/,\\\\,g'` ; \ + if [ -d $$file ] ; then \ + echo "RmDir \"\$$INSTDIR\\$$name\"" >> rmdirs.nsi ; \ + else \ + echo "Delete \"\$$INSTDIR\\$$name\"" >> rmfiles.nsi ; \ + fi ; \ + done + $(MAKENSIS) installer.nsi + +installer.nsi: installer.nsi.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status installer/win32/installer.nsi + +#### Install files into this directory + +install: install-bin install-data install-config install-extra install-libs + +# Install the program itself and data files + +install-bin: $(main_program) + +$(main_program): $(top_builddir)/gui/$(main_program) + cd $(top_builddir)/gui && $(MAKE) install bindir=$(abs_builddir) + $(STRIP) $(main_program) + +install-data: + cd $(top_builddir)/data && $(MAKE) install prefix=$(abs_builddir) + rm -rf share/mime + rm -rf share/applications + rm -rf share/icons + set -e ; for file in $(docs) ; do \ + sed 's/\r*$$/\r/' < $(top_srcdir)/$$file > $$file.txt ; \ + done + set -e ; for file in $(extra_docs) ; do \ + sed 's/\r*$$/\r/' < $(srcdir)/$$file > $$file.txt ; \ + done + +# Install GTK+ configuration + +install-config: + $(INSTALL) -d -m 755 etc/gtk-2.0 + $(INSTALL_DATA) $(srcdir)/gtkrc etc/gtk-2.0 + +# Install additional files + +install-extra: install-extra-stamp + +install-extra-stamp: + set -e ; for file in $(extra_gtk_files) ; do \ + rm -f $$file ; \ + $(INSTALL) -d -m 755 `dirname $$file` ; \ + for dir in `echo $(DLLPATH) | tr $(PATH_SEPARATOR) ' '` ; do \ + if [ -d $$dir ] && [ -f $$dir/../$$file ] ; then \ + $(LN_S) $$dir/../$$file $$file ; \ + break ; \ + fi ; \ + done ; \ + if ! [ -f $$file ] ; then \ + echo "** Cannot find $$file **" ; \ + echo ; \ + echo " Directories searched: $(DLLPATH)" ; \ + echo ; \ + exit 1 ; \ + fi ; \ + done + touch install-extra-stamp + +# Install required libraries + +install-libs: install-libs-stamp + +install-libs-stamp: $(main_program) install-extra-stamp + rm -f dlls missing-dlls + echo $(main_program) > binfiles + ls $(extra_gtk_files) | grep 'dll$$' >> binfiles + echo $(extra_dlls) > dlls + set -e ; LC_ALL=C ; export LC_ALL ; \ + while [ -s binfiles ] ; do \ + for bin in `cat binfiles` ; do \ + $(OBJDUMP) -p $$bin | \ + sed -n '/DLL Name:/{s/.*DLL Name: *\([^ ]*\).*/\1/;p}' | \ + tr A-Z a-z >> dlls ; \ + done ; \ + rm -f binfiles ; \ + for lib in `cat dlls` ; do \ + if ! [ -f ./$$lib ] && ! echo $(system_dlls) | grep -q $$lib ; then \ + for dir in `echo $(DLLPATH) | tr $(PATH_SEPARATOR) ' '` ; do \ + if [ -d $$dir ] && [ -f $$dir/$$lib ] ; then \ + $(LN_S) $$dir/$$lib . ; \ + echo $$lib >> binfiles ; \ + break ; \ + fi ; \ + done ; \ + if ! [ -f $$lib ] ; then \ + echo $$lib >> missing-dlls ; \ + fi ; \ + fi ; \ + done ; \ + rm -f dlls ; \ + done + @if [ -s missing-dlls ] ; then \ + echo "** Cannot find the following libraries **" ; \ + cat missing-dlls ; \ + echo ; \ + echo " If these libraries are part of a standard Windows" ; \ + echo " installation, add them to the list of 'system_dlls'" ; \ + echo " in Makefile.in." ; \ + echo ; \ + echo " Directories searched: $(DLLPATH)" ; \ + echo ; \ + exit 1 ; \ + fi + touch install-libs-stamp + + +clean: + rm -f install-libs-stamp install-extra-stamp + rm -f $(install_files) + rm -rf $(install_subdirs) + rm -f dlls missing-dlls binfiles + rm -f files.nsi rmfiles.nsi rmdirs.nsi + + +Makefile: Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status + +$(top_builddir)/config.status: $(top_srcdir)/configure + cd $(top_builddir) && $(SHELL) ./config.status --recheck + +.PRECIOUS: Makefile $(top_builddir)/config.status +.PHONY: all clean install install-bin install-data install-config install-libraries install-extra diff --git a/tool/tilem-src/installer/win32/gtkrc b/tool/tilem-src/installer/win32/gtkrc new file mode 100644 index 0000000..8942b20 --- /dev/null +++ b/tool/tilem-src/installer/win32/gtkrc @@ -0,0 +1,75 @@ +gtk-icon-sizes = "gtk-menu=13,13:gtk-small-toolbar=16,16:gtk-large-toolbar=24,24:gtk-dnd=32,32" +gtk-toolbar-icon-size = small-toolbar +gtk-toolbar-style = GTK_TOOLBAR_ICONS + +# disable images in buttons. i've only seen ugly delphi apps use this feature. +gtk-button-images = 0 + +# enable/disable images in menus. most "stock" microsoft apps don't use these, except sparingly. +# the office apps use them heavily, though. +gtk-menu-images = 0 + +# use the win32 button ordering instead of the GNOME HIG one, where applicable +gtk-alternative-button-order = 1 + +# use the win32 sort indicators direction, as in Explorer +gtk-alternative-sort-arrows = 1 + +# Windows users don't expect the PC Speaker beeping at them when they backspace in an empty textview and stuff like that +gtk-error-bell = 0 + +style "msw-default" +{ + GtkWidget::interior-focus = 1 + GtkOptionMenu::indicator-size = { 9, 5 } + GtkOptionMenu::indicator-spacing = { 7, 5, 2, 2 } + GtkSpinButton::shadow-type = in + + # Owen and I disagree that these should be themable + #GtkUIManager::add-tearoffs = 0 + #GtkComboBox::add-tearoffs = 0 + + GtkComboBox::appears-as-list = 1 + GtkComboBox::focus-on-click = 0 + + GOComboBox::add_tearoffs = 0 + + GtkTreeView::allow-rules = 0 + GtkTreeView::expander-size = 12 + + GtkExpander::expander-size = 12 + + GtkScrolledWindow::scrollbar_spacing = 1 + + GtkSeparatorMenuItem::horizontal-padding = 2 + + engine "wimp" + { + } +} +class "*" style "msw-default" + +binding "ms-windows-tree-view" +{ + bind "Right" { "expand-collapse-cursor-row" (1,1,0) } + bind "Left" { "expand-collapse-cursor-row" (1,0,0) } +} + +class "GtkTreeView" binding "ms-windows-tree-view" + +style "msw-combobox-thickness" = "msw-default" +{ + xthickness = 0 + ythickness = 0 +} + +widget_class "*TreeView*ComboBox*" style "msw-combobox-thickness" +widget_class "*ComboBox*GtkFrame*" style "msw-combobox-thickness" + +# work around issue with "list-style" cellrenderercombos not emitting +# the "edited" signal - GNOME bug #317387 +style "menu-style-combo" +{ + GtkComboBox::appears-as-list = 0 +} +widget_class "*.GtkTreeView.GtkComboBox" style "menu-style-combo" diff --git a/tool/tilem-src/installer/win32/installer.nsi.in b/tool/tilem-src/installer/win32/installer.nsi.in new file mode 100644 index 0000000..f662691 --- /dev/null +++ b/tool/tilem-src/installer/win32/installer.nsi.in @@ -0,0 +1,82 @@ +Name "@PACKAGE_NAME@" +OutFile "@PACKAGE_TARNAME@-@PACKAGE_VERSION@.exe" +SetCompressor /solid lzma + +!define MULTIUSER_EXECUTIONLEVEL Highest +!define MULTIUSER_MUI +!define MULTIUSER_INSTALLMODE_INSTDIR "@PACKAGE_NAME@" +!include "MultiUser.nsh" +!include "MUI2.nsh" + +Var StartMenuFolder + +!define MUI_FINISHPAGE_RUN "" +!define MUI_FINISHPAGE_RUN_TEXT "Create a desktop shortcut" +!define MUI_FINISHPAGE_RUN_FUNCTION desktopicon + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MULTIUSER_PAGE_INSTALLMODE +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_WELCOME +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_UNPAGE_FINISH + +!insertmacro MUI_LANGUAGE "English" + +Function .onInit + !insertmacro MULTIUSER_INIT +FunctionEnd + +Function un.onInit + UserInfo::GetAccountType + Pop $MultiUser.Privileges + ReadINIStr $0 "$INSTDIR\uninstall.ini" "Uninstall" "InstallMode" + ${if} $0 == "AllUsers" + call un.MultiUser.InstallMode.AllUsers + ${else} + call un.MultiUser.InstallMode.CurrentUser + ${endif} +FunctionEnd + +Function desktopicon + SetShellVarContext current + CreateShortCut "$DESKTOP\TilEm.lnk" "$INSTDIR\tilem2.exe" +FunctionEnd + +Section + SetOutPath "$INSTDIR" + !include "files.nsi" + + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + CreateDirectory "$SMPROGRAMS\$StartMenuFolder" + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\TilEm.lnk" "$INSTDIR\tilem2.exe" + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall TilEm.lnk" "$INSTDIR\uninstall.exe" + WriteINIStr "$INSTDIR\uninstall.ini" "Uninstall" "StartMenuFolder" $StartMenuFolder + !insertmacro MUI_STARTMENU_WRITE_END + + WriteINIStr "$INSTDIR\uninstall.ini" "Uninstall" "InstallMode" $MultiUser.InstallMode + WriteUninstaller "$INSTDIR\uninstall.exe" +SectionEnd + +Section "Uninstall" + !include "rmfiles.nsi" + !include "rmdirs.nsi" + + ReadINIStr $StartMenuFolder "$INSTDIR\uninstall.ini" "Uninstall" "StartMenuFolder" + ${if} $StartMenuFolder != "" + Delete "$SMPROGRAMS\$StartMenuFolder\TilEm.lnk" + Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall TilEm.lnk" + RmDir "$SMPROGRAMS\$StartMenuFolder" + ${endif} + + SetShellVarContext current + Delete "$DESKTOP\TilEm.lnk" + + Delete "$INSTDIR\uninstall.ini" + Delete "$INSTDIR\uninstall.exe" + RmDir "$INSTDIR" +SectionEnd