From 56774401be5d6d8a7a1038b0d0345f1e169adcde Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 23 Feb 2020 00:54:26 -0700 Subject: [PATCH] updated the readme, moved 2mg, fixed bugs --- 2mg.c | 344 ------- 65816.h | 374 -------- Makefile | 13 +- README.md | 246 ++--- addresses.h | 418 --------- disasm.c | 467 ---------- disasm.h | 10 - handle.h | 41 - map.c | 162 ---- map.h | 38 - omf.c | 584 ------------ parser.c | 66 -- parser.h | 22 - prodos16.h | 144 --- prodos8.h | 56 -- regs.c | 137 --- scan.c | 187 ---- scan.h | 11 - smartport.h | 47 - src/2mg.cc | 317 +++++++ src/Makefile | 8 +- src/api.cc | 149 +++ src/api.h | 3 + src/disasm.cc | 35 +- src/disasm.h | 2 +- src/handle.cc | 4 + src/handle.h | 1 + src/main.cc | 27 +- src/map.cc | 20 +- src/map.h | 1 + src/omf.cc | 4 + prodos_types.h => src/prodos_types.h | 0 src/scanner.cc | 3 + src/scanner.h | 1 + tools.h | 1290 -------------------------- 35 files changed, 674 insertions(+), 4558 deletions(-) delete mode 100644 2mg.c delete mode 100644 65816.h delete mode 100644 addresses.h delete mode 100644 disasm.c delete mode 100644 disasm.h delete mode 100644 handle.h delete mode 100644 map.c delete mode 100644 map.h delete mode 100644 omf.c delete mode 100644 parser.c delete mode 100644 parser.h delete mode 100644 prodos16.h delete mode 100644 prodos8.h delete mode 100644 regs.c delete mode 100644 scan.c delete mode 100644 scan.h delete mode 100644 smartport.h create mode 100644 src/2mg.cc rename prodos_types.h => src/prodos_types.h (100%) delete mode 100644 tools.h diff --git a/2mg.c b/2mg.c deleted file mode 100644 index 6e5f99a..0000000 --- a/2mg.c +++ /dev/null @@ -1,344 +0,0 @@ -/** - * @copyright 2018 Sean Kasun - * Extracts 2mg and other prodos disk images into a directory structure - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "handle.h" -#include "prodos_types.h" - -static void doDirectory(uint16_t key, uint8_t *disk, uint32_t disklen, - int depth); -static void doEntry(uint8_t *entry, uint8_t *disk, uint32_t disklen, - int depth); -static void doFile(uint16_t key, uint32_t len, char *name, uint8_t *disk, - uint32_t disklen, int type); -static void doGSOS(uint16_t key, char *name, uint8_t filetype, uint8_t *disk, - uint32_t disklen, int depth); - -const char *argp_program_version = "2mg 0.2"; -const char *argp_program_bug_address = "sean@seancode.com"; -static char doc[] = "Extract ProDOS disk images"; -static char args_doc[] = "DISKIMAGE"; -static struct argp_option options[] = { - {"list", 'l', 0, 0, "List files"}, - { 0 } -}; - -struct arguments { - char *diskimage; - bool list; -}; - -static error_t parse_opt(int key, char *arg, struct argp_state *state) { - struct arguments *arguments = state->input; - switch (key) { - case 'l': - arguments->list = true; - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) { - argp_usage(state); - } - arguments->diskimage = arg; - break; - case ARGP_KEY_END: - if (state->arg_num < 1) { - argp_usage(state); - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -static struct argp argp = { options, parse_opt, args_doc, doc }; - -int main(int argc, char **argv) { - struct arguments arguments; - arguments.list = false; - - argp_parse(&argp, argc, argv, 0, 0, &arguments); - - setlocale(LC_NUMERIC, ""); - - FILE *f = fopen(arguments.diskimage, "rb"); - if (!f) { - fprintf(stderr, "Failed to open '%s'\n", arguments.diskimage); - return -1; - } - fseek(f, 0, SEEK_END); - size_t len = ftell(f); - fseek(f, 0, SEEK_SET); - if (len < 64) { - fprintf(stderr, "%s is not a valid disk image\n", argv[1]); - fclose(f); - return -1; - } - - uint8_t *header = malloc(64); - fread(header, 64, 1, f); - - uint32_t disklen = len; - uint32_t diskofs = 0; - - if (r4(header) == fourcc("2IMG")) { - if (r32(header + 0xc) != 1) { - fprintf(stderr, "Not a ProDOS disk image\n"); - fclose(f); - return -1; - } - disklen = r32(header + 0x14) * 512; - diskofs = r32(header + 0x18); - } - free(header); - - fseek(f, diskofs, SEEK_SET); - uint8_t *disk = malloc(disklen); - fread(disk, disklen, 1, f); - fclose(f); - - doDirectory(2, disk, disklen, arguments.list ? 0 : -1); - free(disk); - return 0; -} - -static void readFilename(uint8_t *filename, uint8_t length, char *outname) { - for (int i = 0; i < length; i++) { - char ch = filename[i]; - if (isalnum(ch) || ch == '_' || ch == '.' || ch == ' ') { - *outname++ = ch; - } else { - *outname++ = 'x'; - char hi = ch >> 4; - char lo = ch & 0xf; - if (hi > 9) { - *outname++ = 'a' + (hi - 10); - } else { - *outname++ = '0' + hi; - } - if (lo > 9) { - *outname++ = '0' + (lo - 10); - } else { - *outname++ = '0' + lo; - } - } - } - *outname = 0; -} - -static void indent(int depth) { - for (int i = 0; i < depth; i++) { - printf(" "); - } -} - -static void doDirectory(uint16_t key, uint8_t *disk, uint32_t disklen, - int depth) { - uint8_t *block = disk + key * 512; - if ((block[4] & 0xf0) != 0xf0 && (block[4] & 0xf0) != 0xe0) { - fprintf(stderr, "Invalid ProDOS disk\n"); - return; - } - - char dirname[50]; - readFilename(block + 5, block[4] & 0xf, dirname); - - if (depth < 0) { - mkdir(dirname, 0777); - chdir(dirname); - } else { - indent(depth); - printf("%s \n", dirname); - } - - uint8_t entryLength = block[0x23]; - uint8_t entriesPerBlock = block[0x24]; - uint16_t fileCount = r16(block + 0x25); - uint8_t *entry = block + entryLength + 4; - uint8_t curEntry = 1; - uint16_t curFile = 0; - - while (curFile < fileCount) { - if (entry[0] != 0) { - doEntry(entry, disk, disklen, depth < 0 ? -1 : depth + 1); - curFile++; - } - curEntry++; - entry += entryLength; - if (curEntry == entriesPerBlock) { - curEntry = 0; - block = disk + r16(block + 2) * 512; - entry = block + 4; - } - } - - if (depth < 0) { - chdir(".."); - } -} - -static void printDateTime(uint16_t date, uint16_t time) { - printf("%02d-%02d-%02d %02d:%02d", - (date >> 9) & 0x7f, (date >> 5) & 0xf, date & 0x1f, - (time >> 8) & 0x1f, time & 0x3f); -} - -static void doEntry(uint8_t *entry, uint8_t *disk, uint32_t disklen, - int depth) { - uint8_t filetype = entry[0x10]; - uint16_t key = r16(entry + 0x11); - uint32_t eof = r24(entry + 0x15); - - char filename[50]; - readFilename(entry + 1, entry[0] & 0xf, filename); - - switch (entry[0] & 0xf0) { - case 0x10: // seedling - case 0x20: // sapling - case 0x30: // tree - if (depth < 0) { - doFile(key, eof, filename, disk, disklen, entry[0] >> 4); - } else { - uint16_t createDate = r16(entry + 0x18); - uint16_t createTime = r16(entry + 0x1a); - uint16_t aux = r16(entry + 0x1f); - uint16_t modDate = r16(entry + 0x21); - uint16_t modTime = r16(entry + 0x23); - indent(depth); - printf("%s\n", filename); - indent(depth); - printf(" Size: %'d", eof); - printf(" Created: "); - printDateTime(createDate, createTime); - printf(" Modified: "); - printDateTime(modDate, modTime); - printf("\n"); - indent(depth); - printf(" Type: $%02x Aux: $%04x ", filetype, aux); - uint32_t typeaux = filetype | (aux << 8); - for (int i = 0; i < numTypes; i++) { - if ((typeaux & fileTypes[i].mask) == fileTypes[i].id) { - printf("%s/%s\n", fileTypes[i].ext, fileTypes[i].desc); - break; - } - } - } - break; - case 0x50: - doGSOS(key, filename, filetype, disk, disklen, depth); - break; - case 0xd0: - doDirectory(key, disk, disklen, depth < 0 ? -1 : depth + 1); - break; - default: - fprintf(stderr, "Unknown file type: %x\n", entry[0] & 0xf0); - return; - } -} - -static void dumpSeedling(uint8_t *block, uint32_t len, FILE *f) { - if (block == NULL) { - fseek(f, len, SEEK_CUR); - } else { - fwrite(block, len, 1, f); - } -} - -static void dumpSapling(uint8_t *index, uint32_t len, FILE *f, uint8_t *disk, - uint32_t disklen) { - if (index == NULL) { - fseek(f, len, SEEK_CUR); - return; - } - - while (len > 0) { - uint16_t blockid = index[0] | (index[256] << 8); - uint8_t *block = NULL; - if (blockid && (blockid + 1) * 512 <= disklen) { - block = disk + blockid * 512; - } - uint32_t blen = len > 512 ? 512 : len; - dumpSeedling(block, blen, f); - len -= blen; - index++; - } -} - -static void dumpTree(uint8_t *index, uint32_t len, FILE *f, uint8_t *disk, - uint32_t disklen) { - if (index == NULL) { - fseek(f, len, SEEK_CUR); - return; - } - - while (len > 0) { - uint16_t blockid = index[0] | (index[256] << 8); - uint8_t *block = NULL; - if (blockid && (blockid + 1) * 512 <= disklen) { - block = disk + blockid * 512; - } - uint32_t blen = len > 256 * 512 ? 256 * 512 : len; - dumpSapling(block, blen, f, disk, disklen); - len -= blen; - index++; - } -} - -static void doGSOS(uint16_t key, char *name, uint8_t filetype, uint8_t *disk, - uint32_t disklen, int depth) { - uint8_t *block = disk + key * 512; - int type = *block; - uint16_t subkey = r16(block + 1); - uint32_t eof = r24(block + 5); - if (depth < 0) { - doFile(subkey, eof, name, disk, disklen, type); - } - - char resname[50]; - strncpy(resname, name, 50); - strncat(resname, ".res", 50); - block = disk + key * 512 + 0x100; - type = *block; - subkey = r16(block + 1); - uint32_t reof = r24(block + 5); - if (depth < 0) { - doFile(subkey, reof, resname, disk, disklen, type); - } else { - indent(depth); - printf("%s %d bytes resource: %d bytes\n", name, eof, reof); - } -} - -static void doFile(uint16_t key, uint32_t len, char *name, uint8_t *disk, - uint32_t disklen, int type) { - uint8_t *block = disk + key * 512; - FILE *f = fopen(name, "wb"); - if (!f) { - fprintf(stderr, "Failed to create '%s'\n", name); - return; - } - - switch (type) { - case 1: - dumpSeedling(block, len, f); - break; - case 2: - dumpSapling(block, len, f, disk, disklen); - break; - case 3: - dumpTree(block, len, f, disk, disklen); - break; - } - fclose(f); -} diff --git a/65816.h b/65816.h deleted file mode 100644 index a017976..0000000 --- a/65816.h +++ /dev/null @@ -1,374 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * Defines the 65816 opcodes, their addressing modes, and sizes - */ - -/** - Addressing modes: - imp implied - imm #$const (l) - immm #$const (l | h) if m16 - immx #$const (l | h) if x16 - imms #$const (l | h) - abs $addr (addrl | addrh) - abl $addr (addrl | addrh | bank) - abx $addr,x (addrl | addrh) - aby $addr,y (addrl | addrh) - ablx $addr,x (addrl | addrh | bank) - aix ($addr,x) (addrl | addrh) - zp $zp (offset) - zpx $zp,x (offset) - zpy $zp,y (offset) - zps $zp,s (offset) - ind ($addr) (addrl | addrh) - inz ($zp) (offset) - inl [$zp] (offset) - inx ($zp,x) (offset) - iny ($zp),y (offset) - inly [$zp],y (offset) - ins ($zp,s),y (offset) - rel $r (signed byte + pc) - rell $r (offsetl | offseth) + pc - bank $sb,$db (dstbnk | srcbnk) - db $op - dw $op - dd $op - */ - -typedef enum { - IMP = 0, - IMM, - IMMM, - IMMX, - IMMS, - ABS, - ABL, - ABX, - ABY, - ABLX, - AIX, - ZP, - ZPX, - ZPY, - ZPS, - IND, - INZ, - INL, - INX, - INY, - INLY, - INS, - REL, - RELL, - BANK, - DB, - DW, - DD -} Address; - -typedef enum { - BREAK = 0, - NORMAL = 1, - BRANCH = 2, - CALL = 3, - RETURN = 4, - JUMP = 5 -} OpType; - -typedef struct { - const char *inst; - Address address; - OpType type; -} Opcode; - -static Opcode opcodes[] = { - {"brk", IMP, BREAK}, // 00 - {"ora", INX, NORMAL}, // 01 - {"cop", IMP, NORMAL}, // 02 - {"ora", ZPS, NORMAL}, // 03 - {"tsb", ZP, NORMAL}, // 04 - {"ora", ZP, NORMAL}, // 05 - {"asl", ZP, NORMAL}, // 06 - {"ora", INL, NORMAL}, // 07 - {"php", IMP, NORMAL}, // 08 - {"ora", IMMM, NORMAL}, // 09 - {"asl", IMP, NORMAL}, // 0a - {"phd", IMP, NORMAL}, // 0b - {"tsb", ABS, NORMAL}, // 0c - {"ora", ABS, NORMAL}, // 0d - {"asl", ABS, NORMAL}, // 0e - {"ora", ABL, NORMAL}, // 0f - {"bpl", REL, BRANCH}, // 10 - {"ora", INY, NORMAL}, // 11 - {"ora", INZ, NORMAL}, // 12 - {"ora", INS, NORMAL}, // 13 - {"trb", ZP, NORMAL}, // 14 - {"ora", ZPX, NORMAL}, // 15 - {"asl", ZPX, NORMAL}, // 16 - {"ora", INLY, NORMAL}, // 17 - {"clc", IMP, NORMAL}, // 18 - {"ora", ABY, NORMAL}, // 19 - {"inc", IMP, NORMAL}, // 1a - {"tcs", IMP, NORMAL}, // 1b - {"trb", ABS, NORMAL}, // 1c - {"ora", ABX, NORMAL}, // 1d - {"asl", ABX, NORMAL}, // 1e - {"ora", ABLX, NORMAL}, // 1f - {"jsr", ABS, CALL}, // 20 - {"and", INX, NORMAL}, // 21 - {"jsl", ABL, CALL}, // 22 - {"and", ZPS, NORMAL}, // 23 - {"bit", ZP, NORMAL}, // 24 - {"and", ZP, NORMAL}, // 25 - {"rol", ZP, NORMAL}, // 26 - {"and", INL, NORMAL}, // 27 - {"plp", IMP, NORMAL}, // 28 - {"and", IMMM, NORMAL}, // 29 - {"rol", IMP, NORMAL}, // 2a - {"pld", IMP, NORMAL}, // 2b - {"bit", ABS, NORMAL}, // 2c - {"and", ABS, NORMAL}, // 2d - {"rol", ABS, NORMAL}, // 2e - {"and", ABL, NORMAL}, // 2f - {"bmi", REL, BRANCH}, // 30 - {"and", INY, NORMAL}, // 31 - {"and", INZ, NORMAL}, // 32 - {"and", INS, NORMAL}, // 33 - {"bit", ZPX, NORMAL}, // 34 - {"and", ZPX, NORMAL}, // 35 - {"rol", ZPX, NORMAL}, // 36 - {"and", INLY, NORMAL}, // 37 - {"sec", IMP, NORMAL}, // 38 - {"and", ABY, NORMAL}, // 39 - {"dec", IMP, NORMAL}, // 3a - {"tsc", IMP, NORMAL}, // 3b - {"bit", ABX, NORMAL}, // 3c - {"and", ABX, NORMAL}, // 3d - {"rol", ABX, NORMAL}, // 3e - {"and", ABLX, NORMAL}, // 3f - {"rti", IMP, RETURN}, // 40 - {"eor", INX, NORMAL}, // 41 - {"db", DB, BREAK}, // 42 - {"eor", ZPS, NORMAL}, // 43 - {"mvp", BANK, NORMAL}, // 44 - {"eor", ZP, NORMAL}, // 45 - {"lsr", ZP, NORMAL}, // 46 - {"eor", INL, NORMAL}, // 47 - {"pha", IMP, NORMAL}, // 48 - {"eor", IMMM, NORMAL}, // 49 - {"lsr", IMP, NORMAL}, // 4a - {"phk", IMP, NORMAL}, // 4b - {"jmp", ABS, JUMP}, // 4c - {"eor", ABS, NORMAL}, // 4d - {"lsr", ABS, NORMAL}, // 4e - {"eor", ABL, NORMAL}, // 4f - {"bvc", REL, BRANCH}, // 50 - {"eor", INY, NORMAL}, // 51 - {"eor", INZ, NORMAL}, // 52 - {"eor", INS, NORMAL}, // 53 - {"mvn", BANK, NORMAL}, // 54 - {"eor", ZPX, NORMAL}, // 55 - {"lsr", ZPX, NORMAL}, // 56 - {"eor", INLY, NORMAL}, // 57 - {"cli", IMP, NORMAL}, // 58 - {"eor", ABY, NORMAL}, // 59 - {"phy", IMP, NORMAL}, // 5a - {"tcd", IMP, NORMAL}, // 5b - {"jmp", ABL, JUMP}, // 5c - {"eor", ABX, NORMAL}, // 5d - {"lsr", ABX, NORMAL}, // 5e - {"eor", ABLX, NORMAL}, // 5f - {"rts", IMP, RETURN}, // 60 - {"adc", INX, NORMAL}, // 61 - {"per", REL, NORMAL}, // 62 - {"adc", ZPS, NORMAL}, // 63 - {"stz", ZP, NORMAL}, // 64 - {"adc", ZP, NORMAL}, // 65 - {"ror", ZP, NORMAL}, // 66 - {"adc", INL, NORMAL}, // 67 - {"pla", IMP, NORMAL}, // 68 - {"adc", IMMM, NORMAL}, // 69 - {"ror", IMP, NORMAL}, // 6a - {"rtl", IMP, RETURN}, // 6b - {"jmp", IND, JUMP}, // 6c - {"adc", ABS, NORMAL}, // 6d - {"ror", ABS, NORMAL}, // 6e - {"adc", ABL, NORMAL}, // 6f - {"bvs", REL, BRANCH}, // 70 - {"adc", INY, NORMAL}, // 71 - {"adc", INZ, NORMAL}, // 72 - {"adc", INS, NORMAL}, // 73 - {"stz", ZPX, NORMAL}, // 74 - {"adc", ZPX, NORMAL}, // 75 - {"ror", ZPX, NORMAL}, // 76 - {"adc", INLY, NORMAL}, // 77 - {"sei", IMP, NORMAL}, // 78 - {"adc", ABY, NORMAL}, // 79 - {"ply", IMP, NORMAL}, // 7a - {"tdc", IMP, NORMAL}, // 7b - {"jmp", AIX, JUMP}, // 7c - {"adc", ABX, NORMAL}, // 7d - {"ror", ABX, NORMAL}, // 7e - {"adc", ABLX, NORMAL}, // 7f - {"bra", REL, JUMP}, // 80 - {"sta", INX, NORMAL}, // 81 - {"brl", RELL, JUMP}, // 82 - {"sta", ZPS, NORMAL}, // 83 - {"sty", ZP, NORMAL}, // 84 - {"sta", ZP, NORMAL}, // 85 - {"stx", ZP, NORMAL}, // 86 - {"sta", INL, NORMAL}, // 87 - {"dey", IMP, NORMAL}, // 88 - {"bit", IMMM, NORMAL}, // 89 - {"txa", IMP, NORMAL}, // 8a - {"phb", IMP, NORMAL}, // 8b - {"sty", ABS, NORMAL}, // 8c - {"sta", ABS, NORMAL}, // 8d - {"stx", ABS, NORMAL}, // 8e - {"sta", ABL, NORMAL}, // 8f - {"bcc", REL, BRANCH}, // 90 - {"sta", INY, NORMAL}, // 91 - {"sta", INZ, NORMAL}, // 92 - {"sta", INS, NORMAL}, // 93 - {"sty", ZPX, NORMAL}, // 94 - {"sta", ZPX, NORMAL}, // 95 - {"stx", ZPY, NORMAL}, // 96 - {"sta", INLY, NORMAL}, // 97 - {"tya", IMP, NORMAL}, // 98 - {"sta", ABY, NORMAL}, // 99 - {"txs", IMP, NORMAL}, // 9a - {"txy", IMP, NORMAL}, // 9b - {"stz", ABS, NORMAL}, // 9c - {"sta", ABX, NORMAL}, // 9d - {"stz", ABX, NORMAL}, // 9e - {"sta", ABLX, NORMAL}, // 9f - {"ldy", IMMX, NORMAL}, // a0 - {"lda", INX, NORMAL}, // a1 - {"ldx", IMMX, NORMAL}, // a2 - {"lda", ZPS, NORMAL}, // a3 - {"ldy", ZP, NORMAL}, // a4 - {"lda", ZP, NORMAL}, // a5 - {"ldx", ZP, NORMAL}, // a6 - {"lda", INL, NORMAL}, // a7 - {"tay", IMP, NORMAL}, // a8 - {"lda", IMMM, NORMAL}, // a9 - {"tax", IMP, NORMAL}, // aa - {"plb", IMP, NORMAL}, // ab - {"ldy", ABS, NORMAL}, // ac - {"lda", ABS, NORMAL}, // ad - {"ldx", ABS, NORMAL}, // ae - {"lda", ABL, NORMAL}, // af - {"bcs", REL, BRANCH}, // b0 - {"lda", INY, NORMAL}, // b1 - {"lda", INZ, NORMAL}, // b2 - {"lda", INS, NORMAL}, // b3 - {"ldy", ZPX, NORMAL}, // b4 - {"lda", ZPX, NORMAL}, // b5 - {"ldx", ZPY, NORMAL}, // b6 - {"lda", INLY, NORMAL}, // b7 - {"clv", IMP, NORMAL}, // b8 - {"lda", ABY, NORMAL}, // b9 - {"tsx", IMP, NORMAL}, // ba - {"tyx", IMP, NORMAL}, // bb - {"ldy", ABX, NORMAL}, // bc - {"lda", ABX, NORMAL}, // bd - {"ldx", ABY, NORMAL}, // be - {"lda", ABLX, NORMAL}, // bf - {"cpy", IMMX, NORMAL}, // c0 - {"cmp", INX, NORMAL}, // c1 - {"rep", IMM, NORMAL}, // c2 - {"cmp", ZPS, NORMAL}, // c3 - {"cpy", ZP, NORMAL}, // c4 - {"cmp", ZP, NORMAL}, // c5 - {"dec", ZP, NORMAL}, // c6 - {"cmp", INL, NORMAL}, // c7 - {"iny", IMP, NORMAL}, // c8 - {"cmp", IMMM, NORMAL}, // c9 - {"dex", IMP, NORMAL}, // ca - {"wai", IMP, NORMAL}, // cb - {"cpy", ABS, NORMAL}, // cc - {"cmp", ABS, NORMAL}, // cd - {"dec", ABS, NORMAL}, // ce - {"cmp", ABL, NORMAL}, // cf - {"bne", REL, BRANCH}, // d0 - {"cmp", INY, NORMAL}, // d1 - {"cmp", INZ, NORMAL}, // d2 - {"cmp", INS, NORMAL}, // d3 - {"pei", ZP, NORMAL}, // d4 - {"cmp", ZPX, NORMAL}, // d5 - {"dec", ZPX, NORMAL}, // d6 - {"cmp", INLY, NORMAL}, // d7 - {"cld", IMP, NORMAL}, // d8 - {"cmp", ABY, NORMAL}, // d9 - {"phx", IMP, NORMAL}, // da - {"stp", IMP, BREAK}, // db - {"jmp", IND, JUMP}, // dc - {"cmp", ABX, NORMAL}, // dd - {"dec", ABX, NORMAL}, // de - {"cmp", ABLX, NORMAL}, // df - {"cpx", IMMX, NORMAL}, // e0 - {"sbc", INX, NORMAL}, // e1 - {"sep", IMM, NORMAL}, // e2 - {"sbc", ZPS, NORMAL}, // e3 - {"cpx", ZP, NORMAL}, // e4 - {"sbc", ZP, NORMAL}, // e5 - {"inc", ZP, NORMAL}, // e6 - {"sbc", INL, NORMAL}, // e7 - {"inx", IMP, NORMAL}, // e8 - {"sbc", IMMM, NORMAL}, // e9 - {"nop", IMP, NORMAL}, // ea - {"xba", IMP, NORMAL}, // eb - {"cpx", ABS, NORMAL}, // ec - {"sbc", ABS, NORMAL}, // ed - {"inc", ABS, NORMAL}, // ee - {"sbc", ABL, NORMAL}, // ef - {"beq", REL, BRANCH}, // f0 - {"sbc", INY, NORMAL}, // f1 - {"sbc", INZ, NORMAL}, // f2 - {"sbc", INS, NORMAL}, // f3 - {"pea", IMMS, NORMAL}, // f4 - {"sbc", ZPX, NORMAL}, // f5 - {"inc", ZPX, NORMAL}, // f6 - {"sbc", INLY, NORMAL}, // f7 - {"sed", IMP, NORMAL}, // f8 - {"sbc", ABY, NORMAL}, // f9 - {"plx", IMP, NORMAL}, // fa - {"xce", IMP, NORMAL}, // fb - {"jsr", AIX, CALL}, // fc - {"sbc", ABX, NORMAL}, // fd - {"inc", ABX, NORMAL}, // fe - {"sbc", ABLX, NORMAL} // ff -}; - -static uint8_t addressSizes[] = { - 1, // IMP - 2, // IMM - 3, // IMMM - 3, // IMMX - 3, // IMMS - 3, // ABS - 4, // ABL - 3, // ABX - 3, // ABY - 4, // ABLX - 3, // AIX - 2, // ZP - 2, // ZPX - 2, // ZPY - 2, // ZPS - 3, // IND - 2, // INZ - 2, // INL - 2, // INX - 2, // INY - 2, // INLY - 2, // INS - 2, // REL - 3, // RELL - 3, // BANK - 1, // DB - 2, // DW - 4 // DD -}; diff --git a/Makefile b/Makefile index 8892ba0..7333c40 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,11 @@ CFLAGS=-Wall all: 2mg regs -2mg: 2mg.o - $(CC) $(CFLAGS) -o $@ -largp $^ +2mg: FORCE + $(MAKE) -C src ../2mg -regs: src/iigs.h - $(MAKE) -C src +regs: FORCE src/iigs.h + $(MAKE) -C src ../regs src/iigs.h: iigs.dat xxd -i $< $@ @@ -18,10 +18,9 @@ iigs.dat: docmaker/docmaker iigs docmaker/docmaker: $(MAKE) -C docmaker -%.o: %.c - $(CC) -c $(CFLAGS) -o $@ $< +FORCE: clean: - rm -f *.o 2mg regs + rm -f *.o $(MAKE) -C docmaker clean $(MAKE) -C src clean diff --git a/README.md b/README.md index b1467a4..28116bd 100644 --- a/README.md +++ b/README.md @@ -1,214 +1,214 @@ # What is this? -This is a set of command-line tools designed specifically to reverse engineer Apple IIgs software. It is comprised of 3 separate tools; `2mg`, `omf`, and `regs`. +This is a set of command-line tools designed specifically to reverse engineer Apple IIgs software. It is comprised of 2 separate tools; `2mg` and `regs`. The first is a simple tool to extract disk images, the second is a more complicated tool that disassembles executables and allows you to query API information. +# 2mg +`2mg` extracts .2mg and .po prodos disk images. When you give it a disk image filename, it will create a folder with the name of the disk, and extract all the files into that folder with the proper hierarchy. -## 2mg +You can also use it to list the contents of the disk image with the `-l` or `--list` command line argument. Listing out the files will also give you the metadata associated with each file, such as creation date and file type. -`2mg` extracts .2mg and .po prodos disk images. You can also just list the contents of the disk image with the `-l` or `--list` command line argument. Otherwise, it will create a folder with the name of the disk and extract all the files into that folder. +# regs -Listing out the files will also give you the metadata associated with each -file. In particular, it will tell you the type and auxiliary type for -the files. +`regs` is a combination of a couple of disassembly tools. It can disassemble raw binary files, it can disassemble OMF files (like sys16 or tool files), and it can be used to inspect the Apple IIgs API. +Since it is a tracing disassembler, it will start disassembly at a given entrypoint and follow all possible paths. Everything not disassembled will be assumed to be data and shown as a hex dump. -## omf +The first time `regs` is used to disassemble a specific file, it will create a `.regs` file with auto-detected information. -`omf` is a rather complicated tool which is designed to extract relocatable segments from OMF files. Apple IIgs executables (.s16 files) and system tools (ex. SYSTEM/TOOLS/TOOL025) are in OMF format. +## regs commandline arguments -You first run this tool and pass it an OMF file and it will generate a .map file. This map file is a simple text file that you may edit. Each line is in the format: +`regs` has a few flags that you can use to customize its disassembly. If you are disassembling an OMF file, none of these flags do anything; the OMF file overrides this information. -`segment:memory location` +These flags are really only useful for the initial disassembly. Once a `.regs` file is created, you should use that to customize all future disassembly of that particular file. -The `segment`is the segment number from the OMF file, and `memory location` is where in memory to relocate that segment. +For regular binary executables, you can specify where in memory they should be loaded with the `-o` or `--org` flag. The passed address can be in decimal, or in hexadecimal if preceded with `$` or `0x`. Be sure to wrap the argument in single-quotes if you use `$`, otherwise your shell will interpret it as a variable. This address will also be used as the entrypoint of the executable. -`omf` does its best to automatically pack all the relocatable segments into the smallest memory possible, starting at `$2/0000`. You can change the starting memory address with the `-o` or `--org` argument. If you wish to manually specify where each segment should go in memory, feel free to edit the .map file however you wish. +You can force the disassembler to start in emulation mode with the `-e` flag. By default, the disassembler starts in native mode. You can force the disassembler to use an 8-bit accumulator with '-m' and 8-bit indices with '-x'. However, emulation mode assumes both of those flags already, just like on the actual hardware. -The next step is to run `omf` again, this time specifying the map file with `-m` or `--map`. This will apply the .map to the OMF and output each segment along with a corresponding .map file. `omf` will throw an error if the segments cannot be mapped as the .map file dictates (for example, you modified the map file so that the segments accidentally overlap). +You'll notice there is also a `-l` flag, this is used for API queries, as described below. -`omf` will modify each segment, applying the proper relocations before outputting the segment. If you wish to change where a segment is in memory, you should modify the original .map file and re-run `omf`. The resulting output files will be hardcoded for those specific memory locations. +## .regs files -The segment outputs will be named `segX` and `segX.map`, where `X` is the segment number, in hex. If you wish to change the prefix from `seg` use the `-p` or `--prefix` argument. +As stated earlier, the first time you execute the disassembler, it creates a `.regs` file for that given executable. The format of this file is fairly starightforward and lets you customize disassembly further. -At this point, you can use the resulting segment files and map files as input to the `regs` tool. +Each line in this file starts with an address. This address is of the format `$bank/offset` where both the dollar sign `$` and bank divider `/` are optional. +If the address is followed by an exclamation point `!`, it means this address is used as the point in memory the executable is loaded at. (OMF files will ignore this). If more than one address is followed by an exclamation point, only the first address encountered will be used. +If the address is followed by a colon `:`, it means this address is considered an entrypoint. Disassembly will start at this address. There can be as many entrypoints as you wish. In fact, as you disassemble a file, you may notice that disassembly is halted by indirect jumps. You can add entrypoints to the `.regs` file to continue disassembly at the destination of those jumps. -## regs +After the colon, you can optionally specify "e" "m", "x" or any combination of those characters to force the disassembler state when disassembly starts at that entry point. -`regs`is my 65816 tracing disassembler. It can be used in conjunction with the .map files created by `omf` or on its own. Since it is a tracing disassembler, it will start disassembly at a given location, and keep disassembling, following all possible paths. Everything not disassembled will be assumed to be data and shown in a hex view. +Finally, if the address is followed by a angle-bracket `<`, then this address has a symbolic name. The symbolic name should be terminated with a closing angle-bracket `>`. If an address has a symbolic name, this name will be used in place of the address whenever it appears in disassembly. - - -#### regs by itself - -You can call `regs` and simply pass it a binary file and it will attempt to disassemble it. You can specify where in memory it should load the file before disassembly with the `-o` or `--org` argument. You can also control whether or not the disassembler is in emulation mode or 16-bit mode with various command-line arguments. Emulation mode is also useful for disassembling 8-bit Apple II software. Use `-e` to start in emulation mode (default native mode), `-m` to start with an 8-bit accumulator (default 16-bit), and `-x` to start with 8-bit index registers (default 16-bit). - - - -#### regs with .map files - -`regs` with .map files is where the disassembler really shines. You can call `regs` and pass it a .map file generated by `omf` and have real control over the disassembly. The .map file is designed to be edited by hand. The format is as follows: +An address can be followed by any and all of the preceding markers. For example, say you have an executable that is loaded at `$300`, which is also the entry point, and you want to start in emulation mode, and give the entrypoint a meaningful name. This is a very common scenario and would result in a `.regs` file with a single line: ``` -gMAP "seg1" -sORG $30000 -$30000: -$30053:mx -$31066:e -$35440:d +$300!:e ``` -The first line specifies the segment file that this map applies to. The filename in the quotes should be relative to the current directory. +Notice I left out the bank divider since it wasn't needed (the bank would naturally be 0), and also the 'm' and 'x' flags are always on in emulation mode, so I didn't need to include them. -The next line specifies where the segment belongs in memory. **Do not edit this** if the segment was created by `omf`, since it has also been hardcoded in the binary. +The `.regs` file is actually rewritten every time you run the disassembler, so the format will be standardized and addresses will be sorted automatically for you. -The next lines are a list of entry points to begin disassembly at. If, when analyzing the disassembly you find a switch case encoded as an indirect jump, you can take that list of jumps and add them to the map file and re-run `regs` to disassemble the previously un-disassembled data. As you work through a disassembly, you may end up with a map file with hundreds of entry points, that's normal. +## Workflow -The flags after the colon are optional, and specify whether emulation mode should be enabled, or 16-bit or 8-bit accumulator and index registers should be used. It defaults to native mode with 16-bit registers. +Since it may not be obvious if you're used to other disassemblers, the workflow for this disassembler is of constant iteration. You run the disassembler on an executable, then tweak the `.regs` file to add entrypoints as necessary, and add symbols to memory addresses as you identify their purpose. Then re-run the disassembler each time. The end result is a disassembly that gets cleaner and clearer and easier to follow. -After the flags, you may optionally give the address a symbol name. Whenever -this memory location is referenced in the code, the symbol name will appear as a -comment. +## Disassembly notation -You can also use bank-separators if you wish. `$3/1066` is the same as `$31066`. +There are few things to note about the disassembly style of `regs`. The first is that since we traced the code flow, branch destinations are preceded by a line that shows up to 7 source addresses and whether they are above or below the current line. This will help you figure out code flow. -The `d` flag is unique in that it identifies the address as a data location and -not an entry point. This is used to give variables symbol names. +Next is that tool calls like `NewHandle` are shown as instructions. This helps dramatically clean up the disassembly. Instead of showing the code that loads X with the tool number and then jumping to the tool dispatch address, we replace it all with just the name of the tool called. This is controlled by the fingerprints in the iigs folder, described below. +Finally, and most unusual, I include "B:" and "D:" flags when the addressing uses DBR and Direct modes. This is because the IIgs can change the direct page and DBR register and thus those addresses cannot be depended to be accurate. +If an address starts with "B:", then the current value of the DBR register should be added to the address. If an address starts with "D:", then the current value of the Direct register should be added to the address. -## Pascal folder +Since they might be misleading, I also do not do symbol swapping on those addresses. Instead any matching symbols are included as a comment on the same line. That way if the IIgs is in a standard setup, then you know what those addresses represent, and if not, you can ignore the comment. -You'll notice a pascal folder in this repository. These are the original GS/OS pascal header files. This is to make it easier for you to look up the arguments and structures of various tool calls you'll come across when disassembling. +# Docmaker, the API, and the iigs folder +You'll notice a iigs folder that contains a bunch of text files that contain structures and function definitions related to the Apple IIgs API. These files are parsed and compiled into the `regs` program for two purposes. + +One, the functions have fingerprints attached to them that the disassembler can use to identify when the program is calling those functions and replace the system calls with actual function names. + +Two, you can query `regs` for any given structure or function and even have it calculate field offsets for you. This will help with disassembly. + +Docmaker is the program that will read in the entire directory and generate a data file, which will get included when you compile `regs`. + +## Querying the API + +Querying the API is fairly simple, call `regs` with the `-l` flag, followed by a keyword. No need to provide a file to disassemble when the `-l` flag is included. Regs will then search the API for a data type or function that contains that keyword and output information about it. + +If you're querying a structure, you can also use the `-o` or `--org` flag to specify where in ram that structure should start, and it will include the calculated offsets for each field. + +For example, say the program you're disassembling calls `FrameRgn` and passes the value `$2/43a9` to the function. You can do the following: + +``` +$ regs -l framergn +FrameRgn: ( + aRgnHandle: RgnHandle, +) +``` + +This lets you know that it takes a single argument which is of type `RgnHandle`. You can query on `RgnHandle` and find out it's a double pointer to `Region`. Next you can do: + +``` +$ regs -l region -o '$2/43a9' +Region: struct { // $a bytes + rgnSize: int16 // $02/43a9 + rgnBBox: Rect // $02/43ab + data: int16[] // $02/43b3 +} +``` + +Now you know exactly what the variables at those memory addresses are and that might help with disassembly further down the line. You might even want to add the fields back as symbols in the `.regs` file for future disassembly. # Examples The flexibility of these tools makes their use a little complicated. So here are some examples of how to go about disassembling various things. -### Disassembling an S16 +## Disassembling an S16 -I'll be using the S16 from Dream Zone as an example. +I'll use the S16 from Dream Zone as an example. Generate an initial map and first disassembly: -Generate a basic map of your S16: +`$ regs dream.sys16` -`$ omf dream.s16` +This will create a dream.sys16.regs file with an (unused) org address of `$00/0000`, and an entry point of `$01/0000`. It will also create 5 files from `seg1` to `seg5`, this is the disassembly for each segment in the S16. -This will create a file called `dream.s16.map`, which we could edit if we choose. We'll leave it as it is. Extract the segments of the OMF with: +Looking at the seg1 disassembly, we notice that after calling a tool like `MMStartUp` or `NewHandle` the accumulator is stored at `$01/e4d0`. I know from experience that this is the toolErr variable. So let's add a symbol for it so we know what's happening if the code inspects it later. Edit the dream.sys16.regs and add a line: -`$ omf --map=dream.s16.map dream.s16 --prefix=dream` +``` +$01/e4d0 +``` -This will create files `dream1`to `dream5` as well as `dream1.map` to `dream5.map`. - -The program's entry point is always the beginning of the first segment, so we'll start there. - -`$ regs dream1.map > dream1.s` - -This will disassemble the entry point. We can then modify the map to further refine the disassembly if we wish. +Rerun `regs` and now the disassembly properly references toolErr! +In a real project, you'll be adding hundreds or thousands of symbols. The tools are designed for that. ### Disassembling a Tool This works the same as disassembling an S16, but with an important difference. -We'll start the same, generating a map and extracting it. +We want to start without any entry points. You can accomplish this by creating +an empty `.regs` file. Then run regs on the tool file. ``` -$ omf TOOL025 -$ omf --map=TOOL025.map TOOL025 +$ touch TOOL025.regs +$ regs TOOL025 ``` -Now, we'll remove all disassembly instructions from the map. You'll see why in a second. So we edit the map file to look like the following: +Without any entrypoints, the entire file is just a hex dump. However, it's a hex dump of the segment content, so it is missing all of the overhead and format information found in an OMF. Thats why you can't just hex dump the original tool file for this. + +All tools start with a tool table. The first dword specifies the number of tools in this file. The next dwords contain return addresses of the various tool entry points. + +Let's say I want to disassemble `NoteOn`. Running `regs -l noteon` shows me that it's tool $0b inside the $19 toolset. Convert that to decimal to discover that's inside the TOOL025 file (the same file we already started using, how convenient). We can calculate the offset to tool $0b. + +`$0b * 4 + $1/0000 = $1/002c` + +We added `$1/0000` since that's where the toolset is disassembled, which we know from inspecting `seg1`. Back to `seg1` at that offset, we see "99 02 01 00" which is little endian for the address `$1/299`. Since these are return addresses, we need to increment that to get the actual entry point of `NoteOn`. + +Add that entrypoint to the `.regs` file. ``` -gMAP "seg1" -sORG $20000 +$00/0000! +$01/029a: ``` -That's it.. no disassembly instructions. Now we run the disassembler: - -`$ regs seg1.map > seg1.s` - -This will just give us a hex dump of the segment. That's actually what we want. All tools start with a tool table. The first dword specifies the number of tools in this toolset. The next dwords all contain addresses (minus 1) of the various tool entry points. - -Let's say I want to disassemble NoteOn. We check the pascal folder and discover that it's tool $0b inside the $19 toolset. Which is the TOOL025 file we're working on (the tool numbers in the filenames are in decimal). So we calculate the offset to that entry point. - -`$0b * 4 + $20000 = $2:002c` - -If we look at the hex dump at that location we'll discover the entry point of NoteOn: `$2:02dd`. Well that's minus one, so we add the real entry point to the .map file: - -``` -gMAP "seg1" -sORG $20000 -$2/02de: -``` - -And rerun the disassembler. - -`$ regs seg1.map > seg1.s` - -We have just disassembled the NoteOn function. - +and rerun regs. Re-check seg1, and down at `$01/29a` we have the disassembly for the `NoteOn` function, awesome! ### Disassembling a Specific Tool Call in ROM -Let's say I want to disassemble WriteRamBlock. We discover it's in the sound toolset $08. If you search, you'll discover that there isn't a TOOL008 anywhere, so we'll have to pull it from ROM. I'll be using an older 128k ROM just because it's convenient. +Let's say I want to disassemble WriteRamBlock. `regs -l writeramblock` shows us that it's tool 9 in toolset 8. If you search, you'll discover that there isn't a TOOL008 anywhere because it's a toolset that's never been patched. Instead we'll need to disassemble it directly from ROM. I'll be using the 128k ROM01 just because I'm more familiar with it. You can use ROM00 or ROM03 instead, it's just the locations of things will be different. -First thing I do, is actually hand make a `rom.map` file for the ROM. +First thing I do, is actually handmake a `.regs` file for the ROM. ``` -gMAP "APPLE2GS.ROM" -sORG $fe0000 -$fe/0000: +$fe/0000!: ``` -and disassemble it. +Because that's where a 128k ROM should be loaded into memory, and I happen to know that the tool bootstrap is also located at that address. For ROM03, you'll want to load the ROM into `$fc/0000`. The tool bootstrap initializes the dispatches in bank `$e1`. We see that code copies 16 bytes from `$fe/0051` to `$e1/0000`, which is the main tool dispatch. Let's add that entrypoint and disassemble again: -`$ regs rom.map > rom.s` +``` +$fe/0051: +``` -This is actually the bootstrap that initializes the `$e1/0000` tool call entrypoint. I notice it copies over a block of memory from `$fe/0051` into `$e1/0000`. So we add `$fe/0051` to the disassembly list of the map file, and disassemble it again. +Stepping through this dispatch code, we see it first looks up your toolset from a table at `$fe/012f`. Since we're after toolset $08, we calculate the offset: `$08 * 4 + $fe/012f = $fe/014f`. We see at that location "00 3e ff 00" which is the little-endian representation of the address `$ff/3e00`. -Following along with the disassembly, we discover that there's a toolset list starting at `$fe/012f`. It starts with a dword with the number of toolsets in the ROM, followed by a list of offsets to the various toolsets. We want toolset 8 for the sound toolset. +The dispatch code then uses another table at that address to determine the return address of the entry point of the tool you want. Since WriteRamBlock is tool 9, we calculate the offset into the new table: `$08 * 4 + $ff/3e00 = $ff/3e24`. -`$8 * 4 + $fe012f = $fe014f` +At that location is the value "a4 41 ff 00" which becomes `$ff/41a4` but since it's a return address, we should increment it before disassembling. -Look up the dword in that location and I find that the toolset is located at `$ff/3e00`. If you then jump to that location, you'll find this is in the exact same format as a tool on disk. It starts with a tool table. WriteRamBlock is tool 9. +``` +$ff/41a5: +``` -`$9 * 4 + $ff3e00 = $ff3e24` - -At that location, we discover the offset to the tool entry point is `$ff/41a4` so we'll add `$ff/41a5`to the map file and rerun the disassembly. - -Boom, we have just disassembled a specific tool call from ram. +Boom, we have just disassembled a specific tool call found in rom. ### Disassembling a simple ProDOS executable -ProDOS binaries aren't relocatable and don't have anything inside them that -specifies where in RAM they should be loaded. However, the filesystem -itself does have that information. +ProDOS binaries aren't relocatable and don't have anything inside them that specifies where in RAM they should be loaded. However, the filesystem itself does have that information. -Using `2mg` with the `-l` or `--list` argument will give a list of the -files along with metadata associated with the files. Let's use `BASIC.SYSTEM` -as an example. +Using `2mg` with the `-l` or `--list` argument will give a list of the files along with metadata associated with the files. Let's use `BASIC.SYSTEM` as an example. -You'll see that `BASIC.SYSTEM` has a type of `$ff` and auxtype of -`$2000`, and `2mg` identifies it as a "sys/ProDOS System File". This is -indeed a simple executable. +You'll see that `BASIC.SYSTEM` has a type of `$ff` and auxtype of `$2000`, and `2mg` identifies it as a "sys/ProDOS System File". This is indeed a simple executable. -The aux type specifies where in RAM to load this executable, in this -case, it's `$2000`. +The aux type specifies where in RAM to load this executable. In this case, it's `$2000`. -It is also important to note that these executables should start with 8-bit -registers. +It is also important to note that these executables should start disassembly in emulation mode, since they're actually 8-bit executables. -So we can use all of that information to disassemble this file. +We can use all of that information to disassemble this file. -`$ regs --org=0x2000 -m -x BASIC.SYSTEM > basic.s` +`$ regs --org=0x2000 -e BASIC.SYSTEM` -This tells regs to start with 8-bit accumulator and indices, and load the -file starting at `$2000` before disassembling it. +This tells regs to start in emulation mode with 8-bit accumulator and indices, and load the file starting at `$2000` before disassembling it. + +Since it's not an OMF file, there will be only 1 output file, `seg1`. Note, however, that we still have a `BASIC.SYSTEM.regs` file so we can add symbols and alternative entrypoints if we wish (remember to include the 'e' flag on any alternative entrypoints, since regs defaults to native mode unless told otherwise). + +The next time we want to disassemble this file, we don't have to pass any arguments since the flags and org are set properly in the `.regs` file. diff --git a/addresses.h b/addresses.h deleted file mode 100644 index 6824bb2..0000000 --- a/addresses.h +++ /dev/null @@ -1,418 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * Defines important IIgs memory addresses. - */ - -typedef struct { - uint32_t address; - const char *comment; -} MemAddress; - -static MemAddress addresses[] = { - {0x03d0, "Enter BASIC"}, - {0x03d2, "Reconnect DOS"}, - {0x03d9, "Cow Sound"}, - {0x03ea, "Reconnect IO"}, - {0x03f2, "Control-Reset Vector"}, - {0x03f5, "Ampersand Vector"}, - {0x03f8, "Control-Y Vector"}, - {0x0400, "Text Screen"}, - {0x0800, "Text Screen 2"}, - {0x0803, "Enter assembler"}, - {0x2000, "Hires screen"}, - {0x4000, "Hires screen 2"}, - {0x9dbf, "Reconnect DOS 3.3"}, - {0xa56e, "CATALOG"}, - {0xc000, "KBD / 80STOREOFF"}, - {0xc001, "80STOREON"}, - {0xc002, "RDMAINRAM"}, - {0xc003, "RDCARDRAM"}, - {0xc004, "WRMAINRAM"}, - {0xc005, "WRCARDRAM"}, - {0xc006, "SETSLOTCXROM"}, - {0xc007, "SETINTCXROM"}, - {0xc008, "SETSTDZP"}, - {0xc009, "SETALTZP"}, - {0xc00a, "SETINTC3ROM"}, - {0xc00b, "SETSLOTC3ROM"}, - {0xc00c, "CLR80VID"}, - {0xc00d, "SET80VID"}, - {0xc00e, "CLRALTCHAR"}, - {0xc00f, "SETALTCHAR"}, - {0xc010, "KBDSTRB"}, - {0xc011, "RDLCBNK2"}, - {0xc012, "RDLCRAM"}, - {0xc013, "RDRAMRD"}, - {0xc014, "RDRAMWRT"}, - {0xc015, "RDCXROM"}, - {0xc016, "RDALTZP"}, - {0xc017, "RDC3ROM"}, - {0xc018, "RD80STORE"}, - {0xc019, "RDVBL"}, - {0xc01a, "RDTEXT"}, - {0xc01b, "RDMIXED"}, - {0xc01c, "RDPAGE2"}, - {0xc01d, "RDHIRES"}, - {0xc01e, "RDALTCHAR"}, - {0xc01f, "RD80VID"}, - {0xc020, "TAPEOUT"}, - {0xc021, "MONOCOLOR"}, - {0xc022, "TBCOLOR"}, - {0xc023, "VGCINT"}, - {0xc024, "MOUSEDATA"}, - {0xc025, "KEYMODREG"}, - {0xc026, "DATAREG"}, - {0xc027, "KMSTATUS"}, - {0xc028, "ROMBANK"}, - {0xc029, "NEWVIDEO"}, - {0xc02b, "LANGSEL"}, - {0xc02c, "CHARROM"}, - {0xc02d, "SLTROMSEL"}, - {0xc02e, "VERTCNT"}, - {0xc02f, "HORIZCNT"}, - {0xc030, "SPKR"}, - {0xc031, "DISKREG"}, - {0xc032, "SCANINT"}, - {0xc033, "CLOCKDATA"}, - {0xc034, "CLOCKCTL"}, - {0xc035, "SHADOW"}, - {0xc036, "CYAREG"}, - {0xc037, "DMAREG"}, - {0xc038, "SCCBREG"}, - {0xc039, "SCCAREG"}, - {0xc03a, "SCCBDATA"}, - {0xc03b, "SCCADATA"}, - {0xc03c, "SOUNDCTL"}, - {0xc03d, "SOUNDDATA"}, - {0xc03e, "SOUNDADRL"}, - {0xc03f, "SOUNDADRH"}, - {0xc040, "STROBE"}, - {0xc041, "INTEN"}, - {0xc044, "MMDELTAX"}, - {0xc045, "MMDELTAY"}, - {0xc046, "DIAGTYPE"}, - {0xc047, "CLRVBLINT"}, - {0xc048, "CLRXYINT"}, - {0xc050, "TXTCLR"}, - {0xc051, "TXTSET"}, - {0xc052, "MIXCLR"}, - {0xc053, "MIXSET"}, - {0xc054, "TXTPAGE1"}, - {0xc055, "TXTPAGE2"}, - {0xc056, "LORES"}, - {0xc057, "HIRES"}, - {0xc058, "CLRAN0"}, - {0xc059, "SETAN0"}, - {0xc05a, "CLRAN1"}, - {0xc05b, "SETAN1"}, - {0xc05c, "CLRAN2"}, - {0xc05d, "SETAN2"}, - {0xc05e, "DHIRESON"}, - {0xc05f, "DHIRESOFF"}, - {0xc060, "TAPEIN"}, - {0xc061, "RDBTN0"}, - {0xc062, "RDBTN1"}, - {0xc063, "RDBTN2"}, - {0xc064, "PADDL0"}, - {0xc065, "PADDL1"}, - {0xc066, "PADDL2"}, - {0xc067, "PADDL3"}, - {0xc068, "STATEREG"}, - {0xc06d, "TESTREG"}, - {0xc06e, "CLTRM"}, - {0xc06f, "ENTM"}, - {0xc070, "PTRIG"}, - {0xc073, "BANKSEL"}, - {0xc07e, "IOUDISON"}, - {0xc07f, "IOUDISOFF"}, - {0xc081, "ROMIN"}, - {0xc083, "LCBANK2"}, - {0xc08b, "LCBANK1"}, - {0xc0e0, "PH0 off"}, - {0xc0e1, "PH0 on"}, - {0xc0e2, "PH1 off"}, - {0xc0e3, "PH1 on"}, - {0xc0e4, "PH2 off"}, - {0xc0e5, "PH2 on"}, - {0xc0e6, "PH3 off"}, - {0xc0e7, "PH3 on"}, - {0xc0e8, "motor off"}, - {0xc0e9, "motor on"}, - {0xc0ea, "drive 1"}, - {0xc0eb, "drive 2"}, - {0xc0ec, "q6 off"}, - {0xc0ed, "q6 on"}, - {0xc0ee, "q7 off"}, - {0xc0ef, "q7 on"}, - {0xc311, "AUXMOVE"}, - {0xc314, "XFER"}, - {0xc50d, "Smartport"}, - {0xc70d, "Smartport"}, - {0xcfff," CLRROM"}, - {0xd1fc, "Hires Find"}, - {0xd2c9, "Hires bg"}, - {0xd331, "Hires graphics bg"}, - {0xd33a, "Hires DRAW1"}, - {0xd3b9, "Hires SHLOAD"}, - {0xd683, "Clear FOR"}, - {0xdafb, "Carriage Return"}, - {0xe000, "Reset Int Basic"}, - {0xe04b, "IntBASIC LIST"}, - {0xe5ad, "NEW"}, - {0xe5b7, "PLOT"}, - {0xe836, "IntBASIC CHAIN"}, - {0xefec, "IntBASIC RUN"}, - {0xf07c, "IntBASIC LOAD"}, - {0xf0e0, "Leave monitor"}, - {0xf123, "DRAW shape"}, - {0xf14f, "Plot point"}, - {0xf171, "IntBASIC TRACE ON"}, - {0xf176, "IntBASIC TRACE OFF"}, - {0xf30a, "IntBASIC CON"}, - {0xf317, "RESUME"}, - {0xf328, "Clear error"}, - {0xf3de, "HGR"}, - {0xf3e4, "Show hires"}, - {0xf3f2, "Clear hires"}, - {0xf3f6, "Clear hires color"}, - {0xf666, "Enter assembler"}, - {0xf800, "PLOT"}, - {0xf80e, "PLOT1"}, - {0xf819, "HLINE"}, - {0xf828, "VLINE"}, - {0xf832, "CLRSCR"}, - {0xf836, "CLRTOP"}, - {0xf838, "Clear lores y"}, - {0xf83c, "Clear rect"}, - {0xf847, "GBASCALC"}, - {0xf85e, "Add 3 COLOR"}, - {0xf85f, "NXTCOL"}, - {0xf864, "SETCOL"}, - {0xf871, "SCRN"}, - {0xf88c, "INSDS1.2"}, - {0xf88e, "INSDS2"}, - {0xf890, "GET816LEN"}, - {0xf8d0, "INSTDSP"}, - {0xf940, "PRNTYX"}, - {0xf941, "PRNTAX"}, - {0xf944, "PRNTX"}, - {0xf948, "PRBLNK"}, - {0xf94a, "PRBL2"}, - {0xf94c, "Print X blank"}, - {0xf953, "PCADJ"}, - {0xf962, "TEXT2COPY"}, - {0xfa40, "OLDIRQ"}, - {0xfa4c, "BREAK"}, - {0xfa59, "OLDBRK"}, - {0xfa62, "RESET"}, - {0xfaa6, "PWRUP"}, - {0xfaba, "SLOOP"}, - {0xfad7, "REGDSP"}, - {0xfb19, "RTBL"}, - {0xfb1e, "PREAD"}, - {0xfb21, "PREAD4"}, - {0xfb2f, "INIT"}, - {0xfb39, "SETTXT"}, - {0xfb40, "SETGR"}, - {0xfb4b, "SETWND"}, - {0xfb51, "SETWND2"}, - {0xfb5b, "TABV"}, - {0xfb60, "APPLEII"}, - {0xfb6f, "SETPWRC"}, - {0xfb78, "VIDWAIT"}, - {0xfb88, "KBDWAIT"}, - {0xfbb3, "VERSION"}, - {0xfbbf, "ZIDBYTE2"}, - {0xfbc0, "ZIDBYTE"}, - {0xfbc1, "BASCALC"}, - {0xfbdd, "BELL1"}, - {0xfbe2, "BELL1.2"}, - {0xfbe4, "BELL2"}, - {0xfbf0, "STORADV"}, - {0xfbf4, "ADVANCE"}, - {0xfbfd, "VIDOUT"}, - {0xfc10, "BS"}, - {0xfc1a, "UP"}, - {0xfc22, "VTAB"}, - {0xfc24, "VTABZ"}, - {0xfc2c, "ESC"}, - {0xfc42, "CLREOP"}, - {0xfc58, "HOME"}, - {0xfc62, "CR"}, - {0xfc66, "LF"}, - {0xfc70, "SCROLL"}, - {0xfc9c, "CLREOL"}, - {0xfc9e, "CLREOLZ"}, - {0xfca8, "WAIT"}, - {0xfcb4, "NXTA4"}, - {0xfcba, "NXTA1"}, - {0xfcc9, "HEADR"}, - {0xfd0c, "RDKEY"}, - {0xfd10, "FD10"}, - {0xfd18, "RDKEY1"}, - {0xfd1b, "KEYIN"}, - {0xfd35, "RDCHAR"}, - {0xfd5a, "Wait return"}, - {0xfd5c, "Ring bell wait"}, - {0xfd67, "GETLNZ"}, - {0xfd6a, "GETLN"}, - {0xfd6c, "GETLN0"}, - {0xfd6f, "GETLN1"}, - {0xfd75, "Wait line"}, - {0xfd8b, "CROUT1"}, - {0xfd8e, "CROUT"}, - {0xfd92, "PRA1"}, - {0xfda3, "Print memory"}, - {0xfdda, "PRBYTE"}, - {0xfde3, "PRHEX"}, - {0xfded, "COUT"}, - {0xfdf0, "COUT1"}, - {0xfdf6, "COUTZ"}, - {0xfe1f, "IDROUTINE"}, - {0xfe2c, "MOVE"}, - {0xfe5e, "LIST"}, - {0xfe61, "Disassembler"}, - {0xfe80, "INVERSE"}, - {0xfe84, "NORMAL"}, - {0xfe86, "Set I"}, - {0xfe89, "SETKBD"}, - {0xfe8b, "INPORT"}, - {0xfe93, "SETVID"}, - {0xfe95, "OUTPORT"}, - {0xfeb0, "Jump BASIC"}, - {0xfeb6, "GO"}, - {0xfebf, "Display regs"}, - {0xfec2, "Perform trace"}, - {0xfecd, "WRITE"}, - {0xfefd, "READ"}, - {0xff2d, "PRERR"}, - {0xff3a, "BELL"}, - {0xff3f, "RESTORE"}, - {0xff44, "RSTR1"}, - {0xff4a, "SAVE"}, - {0xff4c, "SAV1"}, - {0xff58, "IORTS"}, - {0xff59, "OLDRST"}, - {0xff65, "MON"}, - {0xff69, "MONZ"}, - {0xff6c, "MONZ2"}, - {0xff70, "MONZ4"}, - {0xff8a, "DIG"}, - {0xffa7, "GETNUM"}, - {0xffad, "NXTCHR"}, - {0xffbe, "TOSUB"}, - {0xffc7, "ZMODE"}, - {0xe01e04, "StdText"}, - {0xe01e08, "StdLine"}, - {0xe01e0c, "StdRect"}, - {0xe01e10, "StdRRect"}, - {0xe01e14, "StdOval"}, - {0xe01e18, "StdArc"}, - {0xe01e1c, "StdPoly"}, - {0xe01e20, "StdRgn"}, - {0xe01e24, "StdPixels"}, - {0xe01e28, "StdComment"}, - {0xe01e2c, "StdTxMeas"}, - {0xe01e30, "StdTxBnds"}, - {0xe01e34, "StdGetPic"}, - {0xe01e38, "StdPutPic"}, - {0xe01e98, "ShieldCursor"}, - {0xe01e9c, "UnshieldCursor"}, - {0xe10000, "System Tool dispatch"}, - {0xe10004, "System Tool dispatch"}, - {0xe10008, "User Tool dispatch"}, - {0xe1000c, "User Tool dispatch"}, - {0xe10010, "Interrupt manager"}, - {0xe10014, "COP manager"}, - {0xe10018, "Abort manager"}, - {0xe1001c, "System death manager"}, - {0xe10020, "AppleTalk interrupt"}, - {0xe10024, "Serial interrupt"}, - {0xe10028, "Scanline interrupt"}, - {0xe1002c, "Sound interrupt"}, - {0xe10030, "VBlank interrupt"}, - {0xe10034, "Mouse interrupt"}, - {0xe10038, "250ms interrupt"}, - {0xe1003c, "Keyboard interrupt"}, - {0xe10040, "ADB Response"}, - {0xe10044, "ADB SRQ"}, - {0xe10048, "DA manager"}, - {0xe1004c, "Flush Buffer"}, - {0xe10050, "KbdMicro interrupt"}, - {0xe10054, "1s interrupt"}, - {0xe10058, "External VGC interrupt"}, - {0xe1005c, "Ohter interrupt"}, - {0xe10060, "Cursor update"}, - {0xe10064, "IncBusy"}, - {0xe10068, "DecBusy"}, - {0xe1006c, "Bell vector"}, - {0xe10070, "Break vector"}, - {0xe10074, "Trace vector"}, - {0xe10078, "Step vector"}, - {0xe1007c, "ROM disk"}, - {0xe10080, "ToWriteBram"}, - {0xe10084, "ToReadBram"}, - {0xe10088, "ToWriteTime"}, - {0xe1008c, "ToReadTime"}, - {0xe10090, "ToCtrlPanel"}, - {0xe10094, "ToBramSetup"}, - {0xe10098, "ToPrintMsg8"}, - {0xe1009c, "ToPrintMsg16"}, - {0xe100a0, "Native Ctl-Y"}, - {0xe100a4, "ToAltDispCDA"}, - {0xe100a8, "Prodos 16"}, - {0xe100ac, "OS vector"}, - {0xe100b0, "GS/OS"}, - {0xe100b4, "P8 Switch"}, - {0xe100b8, "Public Flags"}, - {0xe100bc, "OS Kind"}, - {0xe100bd, "OS Boot"}, - {0xe100be, "OS Busy"}, - {0xe100c0, "MsgPtr"}, - {0xe100ca, "System Volume"}, - {0xe10180, "ToBusyStrip"}, - {0xe10184, "ToStrip"}, - {0xe101b2, "MidiInputPoll"}, - {0xe10200, "Memory manager"}, - {0xe10204, "Set System Speed"}, - {0xe10208, "Slot Arbiter"}, - {0xe10220, "Hypercard callback"}, - {0xe10224, "WordForRTL"}, - {0xe11004, "ATLK Basic"}, - {0xe11008, "ATLK Pascal"}, - {0xe1100c, "ATLK RamGoComp"}, - {0xe11010, "ATLK SoftReset"}, - {0xe11014, "ATLK RamDispatch"}, - {0xe11018, "ATLK RamForbid"}, - {0xe1101c, "ATLK RamPermit"}, - {0xe11020, "ATLK ProEntry"}, - {0xe11022, "ATLK ProDOS"}, - {0xe11026, "ATLK SerStatus"}, - {0xe1102a, "ATLK SerWrite"}, - {0xe1102e, "ATLK SerRead"}, - {0xe1103e, "ATLK PFI"}, - {0xe1d600, "ATLK CmdTable"}, - {0xe1da00, "ATLK TickCount"} -}; - -#define numAddresses (sizeof(addresses) / sizeof(addresses[0])) - -static const char *addressLookup(uint32_t addr, Map *map) { - for (int i = 0; i < numAddresses; i++) { - if (addresses[i].address >= addr) { - if (addresses[i].address == addr) - return addresses[i].comment; - break; - } - } - for (Rule *rule = map->rules; rule != NULL; rule = rule->next) { - if (rule->address == addr && rule->symbol != NULL) { - return rule->symbol; - } - } - if (addr & ~0xffff) - return addressLookup(addr & 0xffff, map); // try pageless - return NULL; -} diff --git a/disasm.c b/disasm.c deleted file mode 100644 index 489ed2b..0000000 --- a/disasm.c +++ /dev/null @@ -1,467 +0,0 @@ -/** - * @copyright 2018 Sean Kasun - * The main disassembler. - */ - -#include -#include -#include -#include -#include "disasm.h" -#include "65816.h" -#include "handle.h" -#include "addresses.h" -#include "prodos8.h" -#include "prodos16.h" -#include "smartport.h" -#include "tools.h" - -static void dumphex(uint8_t *ptr, uint32_t addr, uint32_t len) { - uint8_t *eof = ptr + len; - for (int line = 0; ptr < eof; line++) { - printf("%02x/%04x: ", addr >> 16, addr & 0xffff); - uint8_t *p = ptr; - int skip = addr & 0xf; - int i = 0; - for (; i < skip; i++) { - if (i == 8) { - printf(" "); - } - printf(" "); - } - for (; i < 16 && p < eof; i++) { - if (i == 8) { - printf(" "); - } - printf("%02x ", *p++); - } - for (; i < 16; i++) { - if (i == 8) { - printf(" "); - } - printf(" "); - } - printf("| "); - i = 0; - for (; i < skip; i++) { - if (i == 8) { - printf(" "); - } - printf(" "); - } - for (; i < 16 && ptr < eof; i++) { - if (i == 8) { - printf(" "); - } - uint8_t c = *ptr++; - addr++; - printf("%c", (c >= ' ' && c <= '~') ? c : '.'); - } - printf("\n"); - } -} - -void disassemble(uint8_t *data, size_t len, Map *map) { - uint8_t *ptr = data; - uint8_t *end = data + len; - - uint16_t x = 0; - uint32_t val; - int8_t delta; - int16_t delta16; - uint32_t d6; - bool smart = false, dos8 = false, dos16 = false; - - uint32_t addr = map->minMemory; - - while (ptr < end) { - MapFlags flags = map->mem[addr - map->minMemory]; - if ((flags & IsOpcode) || smart || dos8 || dos16) { - printf("%02x/%04x:", addr >> 16, addr & 0xffff); - uint8_t *start = ptr; - uint8_t opcode = *ptr++; - - const char *inst = opcodes[opcode].inst; - Address mode = opcodes[opcode].address; - OpType type = opcodes[opcode].type; - - if (smart || dos8) { - mode = DB; - inst = "db"; - } - if (dos16) { - mode = DW; - inst = "dw"; - } - - uint16_t width = addressSizes[mode]; - if (mode == IMMM && (flags & (IsEmu | IsM8))) { - width--; - } - if (mode == IMMX && (flags & (IsEmu | IsX8))) { - width--; - } - addr += width; - flags &= IsFlags; // clear changed flags - MapFlags oldFlags = flags; - - for (int i = 0; i < width; i++) { - printf(" %02x", start[i]); - } - for (int i = 0; i < 4 - width; i++) { - printf(" "); - } - printf(" %s", inst); - for (int i = strlen(inst); i < 8; i++) { - printf(" "); - } - - const char *comments = NULL; - uint8_t oprlen = 0; - uint8_t oper = 0; - - switch (mode) { - case IMP: - break; - case IMM: - oper = *ptr++; - printf("#$%02x", oper); - oprlen = 4; - if (opcode == 0xe2) { - flags |= oper & IsFlags; - } else if (opcode == 0xc2) { - flags &= ~oper; - } - if ((flags ^ oldFlags) & IsX8) { - flags |= IsX8Changed; - } - if ((flags ^ oldFlags) & IsM8) { - flags |= IsM8Changed; - } - break; - case IMMM: - if (flags & (IsEmu | IsM8)) { - printf("#$%02x", *ptr++); - oprlen = 4; - } else { - val = r16(ptr); ptr += 2; - printf("#$%04x", val); - oprlen = 6; - comments = addressLookup(val, map); - } - break; - case IMMX: - if (flags & (IsEmu | IsX8)) { - x = *ptr++; - printf("#$%02x", x); - oprlen = 4; - } else { - x = r16(ptr); ptr += 2; - printf("#$%04x", x); - oprlen = 6; - comments = addressLookup(x, map); - } - break; - case IMMS: - val = r16(ptr); ptr += 2; - printf("#$%04x", val); - oprlen = 6; - comments = addressLookup(x, map); - break; - case ABS: - val = r16(ptr); ptr += 2; - if (type == JUMP || type == CALL) { - val |= addr & 0xff0000; // K - } else { - val |= flags & 0xff0000; // B - } - printf("$%02x/%04x", val >> 16, val & 0xffff); - oprlen = 8; - comments = addressLookup(val, map); - break; - case ABL: - val = r24(ptr); ptr += 3; - printf("$%02x/%04x", val >> 16, val & 0xffff); - oprlen = 8; - comments = addressLookup(val, map); - break; - case ABX: - val = r16(ptr); ptr += 2; - if (type == JUMP || type == CALL) { - val |= addr & 0xff0000; // K - } else { - val |= flags & 0xff0000; // B - } - printf("$%02x/%04x, x", val >> 16, val & 0xffff); - oprlen = 11; - comments = addressLookup(val, map); - break; - case ABY: - val = r16(ptr); ptr += 2; - if (type == JUMP || type == CALL) { - val |= addr & 0xff0000; // K - } else { - val |= flags & 0xff0000; // B - } - printf("$%02x/%04x, y", val >> 16, val & 0xffff); - oprlen = 11; - comments = addressLookup(val, map); - break; - case ABLX: - val = r24(ptr); ptr += 3; - printf("$%02x/%04x, x", val >> 16, val & 0xffff); - oprlen = 11; - comments = addressLookup(val, map); - break; - case AIX: - val = r16(ptr); ptr += 2; - if (type == JUMP || type == CALL) { - val |= addr & 0xff0000; // K - } else { - val |= flags & 0xff0000; // B - } - printf("($%02x/%04x, x)", val >> 16, val & 0xffff); - oprlen = 13; - comments = addressLookup(val, map); - break; - case ZP: - printf("$%02x", *ptr++); - oprlen = 3; - break; - case ZPX: - printf("$%02x, x", *ptr++); - oprlen = 6; - break; - case ZPY: - printf("$%02x, y", *ptr++); - oprlen = 6; - break; - case ZPS: - printf("$%02x, s", *ptr++); - oprlen = 6; - break; - case IND: - val = r16(ptr); ptr += 2; - if (type == JUMP || type == CALL) { - val |= addr & 0xff0000; // K - } else { - val |= flags & 0xff0000; // B - } - printf("($%02x/%04x)", val >> 16, val & 0xffff); - oprlen = 10; - comments = addressLookup(val, map); - break; - case INZ: - printf("($%02x)", *ptr++); - oprlen = 5; - break; - case INL: - printf("[$%02x]", *ptr++); - oprlen = 5; - break; - case INX: - printf("($%02x, x)", *ptr++); - oprlen = 8; - break; - case INY: - printf("($%02x), y", *ptr++); - oprlen = 8; - break; - case INLY: - printf("[$%02x], y", *ptr++); - oprlen = 8; - break; - case INS: - printf("($%02x, s), y", *ptr++); - oprlen = 11; - break; - case REL: - delta = *ptr++; - d6 = delta + addr; - printf("$%02x/%04x", d6 >> 16, d6 & 0xffff); - oprlen = 8; - comments = addressLookup(d6, map); - break; - case RELL: - delta16 = r16(ptr); ptr += 2; - d6 = delta16 + addr; - printf("$%02x/%04x", d6 >> 16, d6 & 0xffff); - oprlen = 8; - comments = addressLookup(d6, map); - break; - case BANK: - val = *ptr++; - printf("$%02x, $%02x", *ptr++, val); - oprlen = 8; - break; - case DB: - printf("$%02x", opcode); - oprlen = 3; - break; - case DW: - val = opcode | (*ptr++ << 8); - printf("$%04x", val); - oprlen = 5; - break; - case DD: - printf("$%08x", opcode | r24(ptr) << 8); ptr += 3; - oprlen = 9; - break; - } - - - if (smart) { - comments = smartportLookup(opcode); - smart = false; - } - if (dos8) { - comments = prodos8Lookup(opcode); - dos8 = false; - } - if (dos16) { - comments = prodos16Lookup(val); - dos16 = false; - } - - // track plb - if (opcode == 0xab) { // plb, was it previously a9 xx 48? - bool bset = false; - ptr -= 4; - uint8_t lda = *ptr++; - uint8_t b = *ptr++; - uint8_t pha = *ptr++; - ptr++; - if (lda == 0xa9 && pha == 0x48) { - flags &= 0xffff; - flags |= b << 16; // set B - bset = true; - } - if (!bset) { - ptr -= 2; - uint8_t phk = *ptr++; - ptr++; - if (phk == 0x4b) { - flags &= 0xffff; - flags |= addr & 0xff0000; // B = K - bset = true; - } - } - if (!bset) { - ptr -= 7; - uint8_t lda = *ptr++; - uint8_t b = r16(ptr); ptr += 2; - ptr += 2; // sep #20 - uint8_t pha = *ptr++; - ptr++; - if (lda == 0xa9 && pha == 0x48) { - flags &= 0xffff; - flags |= b << 16; // set B - bset = true; - } - } - } - - if (opcode == 0x18) { - if (*ptr == 0xfb) { // clc xce = 16 bit mode - flags &= 0xffffff ^ IsEmu; - } - } - if (opcode == 0x38) { - if (*ptr == 0xfb) { // sec xce = 8 bit mode - flags |= IsEmu; - } - } - if ((flags ^ oldFlags) & IsEmu) { - flags |= IsEmuChanged; - } - - if (opcode == 0xa2) { // ldx - if (*ptr == 0x22) { // jsl - if (r24(ptr + 1) == 0xe10000) { // jsl el/0000 - comments = toolLookup(x); - } - } - } - if (opcode == 0x20) { // jsr - if (val == 0xc50d || val == 0xc70d) { - smart = true; - } - if (val == 0xbf00) { - dos8 = true; - } - } - if (opcode == 0x22) { // jsl - if (val == 0xe100a8) { - dos16 = true; - } - } - - if (flags & (IsEmuChanged | IsM8Changed | IsX8Changed)) { - for (int i = oprlen; i < 16; i++) { - printf(" "); - } - printf("; "); - } - - if (flags & IsEmuChanged) { - if (flags & IsEmu) { - printf(" 8-bit mode"); - } else { - printf(" 16-bit mode"); - } - } - if (flags & IsM8Changed) { - if (flags & IsM8) { - printf(" a.b"); - } else { - printf(" a.w"); - } - } - if (flags & IsX8Changed) { - if (flags & IsX8) { - printf(" x.b"); - } else { - printf(" x.w"); - } - } - - if (comments != NULL) { - for (int i = oprlen; i < 16; i++) { - printf(" "); - } - printf("; %s", comments); - } - printf("\n"); - } else { - uint32_t dlen = 0; - uint8_t *p = ptr; - while (p < end && !(map->mem[addr + dlen - map->minMemory] & IsOpcode)) { - p++; - dlen++; - } - uint32_t val; - switch (dlen) { - case 4: - printf("%02x/%04x:", addr >> 16, addr & 0xffff); - val = r32(ptr); - printf(" %02x %02x %02x %02x dd $%08x\n", val & 0xff, - (val >> 8) & 0xff, (val >> 16) & 0xff, val >> 24, val); - break; - case 2: - printf("%02x/%04x:", addr >> 16, addr & 0xffff); - val = r16(ptr); - printf(" %02x %02x dw $%04x\n", val & 0xff, val >> 8, val); - break; - case 1: - printf("%02x/%04x:", addr >> 16, addr & 0xffff); - printf(" %02x db $%04x\n", *ptr, *ptr); - break; - default: - dumphex(ptr, addr, dlen); - break; - } - ptr += dlen; - addr += dlen; - } - } -} diff --git a/disasm.h b/disasm.h deleted file mode 100644 index 50d05e3..0000000 --- a/disasm.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * The main disassembler. - */ - -#include "map.h" - -extern void disassemble(uint8_t *data, size_t len, Map *map); diff --git a/handle.h b/handle.h deleted file mode 100644 index e4e467e..0000000 --- a/handle.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * Routines for reading multi-byte numbers from a stream - */ - -#include - -static inline uint32_t fourcc(const char *p) { - return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; -} - -static inline uint16_t r16(uint8_t *p) { - uint16_t r = *p++; - r |= *p << 8; - return r; -} - -static inline uint32_t r24(uint8_t *p) { - uint32_t r = *p++; - r |= *p++ << 8; - r |= *p << 16; - return r; -} - -static inline uint32_t r32(uint8_t *p) { - uint32_t r = *p++; - r |= *p++ << 8; - r |= *p++ << 16; - r |= *p << 24; - return r; -} - -static inline uint32_t r4(uint8_t *p) { - uint32_t r = *p++ << 24; - r |= *p++ << 16; - r |= *p++ << 8; - r |= *p; - return r; -} diff --git a/map.c b/map.c deleted file mode 100644 index d9999f1..0000000 --- a/map.c +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @copyright 2018 Sean Kasun - * Handles the memory map, for the tracing disassembler. - */ - -#include -#include -#include -#include -#include -#include -#include "map.h" -#include "parser.h" -#include "handle.h" - -static Rule *parseRule(ConfigFile *f) { - Rule *rule = malloc(sizeof(Rule)); - rule->next = NULL; - if (!token(f, '$')) { - fail(f, "Address must be a hex value above 0 \n" - "starting with '$', i.e. $c20"); - } - rule->address = hex(f); - if (token(f, '/')) { - rule->address <<= 16; - rule->address |= hex(f); - } - if (rule->address == 0) { - fail(f, "Address must be a hex value above 0 \n" - "starting with '$', i.e. $c20"); - } - if (!token(f, ':')) { - fail(f, "Expected ':'"); - } - rule->flags = IsOpcode; - bool foundFlag = false; - do { - foundFlag = false; - if (token(f, 'e')) { - rule->flags |= IsEmu; - foundFlag = true; - } - if (token(f, 'm')) { - rule->flags |= IsM8; - foundFlag = true; - } - if (token(f, 'x')) { - rule->flags |= IsX8; - foundFlag = true; - } - if (token(f, 'd')) { - rule->flags &= ~IsOpcode; - rule->flags |= IsData; - foundFlag = true; - } - } while (foundFlag); - rule->symbol = NULL; - if (token(f, '<')) { - uint8_t *fnp = f->p; - while (fnp < f->end && *fnp != '>') { - fnp++; - } - if (fnp == f->end) { - fail(f, "Symbol has no closing '>'."); - } - int flen = fnp - f->p; - rule->symbol = malloc(flen); - memcpy(rule->symbol, f->p, flen); - rule->symbol[flen] = 0; - f->p = fnp + 1; - } - return rule; -} - -Map *loadMap(const char *filename, uint32_t org, MapFlags flags) { - Map *map = malloc(sizeof(Map)); - map->rules = NULL; - map->minMemory = org; - map->maxMemory = 0; - map->mem = NULL; - - FILE *f = fopen(filename, "rb"); - if (!f) { - fprintf(stderr, "Failed to open '%s'\n", filename); - exit(-1); - } - fseek(f, 0, SEEK_END); - size_t len = ftell(f); - fseek(f, 0, SEEK_SET); - uint8_t *data = malloc(len); - fread(data, len, 1, f); - fclose(f); - - if (r4(data) != fourcc("gMAP")) { - // not a map file, use org as the only entry point - map->rules = malloc(sizeof(Rule)); - map->rules->address = org; - map->rules->flags = flags; - map->rules->next = NULL; - map->filename = filename; - free(data); - return map; - } - ConfigFile c; - c.start = data; - c.p = c.start; - c.end = c.start + len; - c.filename = filename; - - // set filename - c.p += 4; - eatSpaces(&c); - if (!token(&c, '"')) { - fail(&c, "Expected '\"' around the filename."); - } - uint8_t *fnp = c.p; - while (fnp < c.end && *fnp != '"') { - fnp++; - } - if (fnp == c.end) { - fail(&c, "Filename has no closing '\"'."); - } - int flen = fnp - c.p; - char *fname = malloc(flen); - memcpy(fname, c.p, flen); - fname[flen] = 0; - map->filename = fname; - c.p = fnp + 1; - - eatSpaces(&c); - if (r4(c.p) != fourcc("sORG")) { - fail(&c, "Expected 'sORG' tag"); - } - c.p += 4; - eatSpaces(&c); - if (!token(&c, '$')) { - fail(&c, "ORG must be a hex value above 0\n" - "starting with '$', i.e. $c20"); - } - map->minMemory = hex(&c); - if (map->minMemory == 0) { - fail(&c, "ORG must be a hex value above 0\n" - "starting with '$', i.e. $c20"); - } - eatSpaces(&c); - - // load rules - Rule *lastRule = NULL; - while (c.p < c.end) { - Rule *rule = parseRule(&c); - if (lastRule == NULL) { - map->rules = rule; - } else { - lastRule->next = rule; - } - lastRule = rule; - eatSpaces(&c); - } - free(c.start); - - return map; -} diff --git a/map.h b/map.h deleted file mode 100644 index 2320854..0000000 --- a/map.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * The memory map for the tracing disassembler - */ - -#include - -typedef enum { - IsData = 0x01, - IsOpcode = 0x02, - IsOperand = 0x04, - IsX8 = 0x10, - IsM8 = 0x20, - IsEmu = 0x100, - IsFlags = 0xff013f, - IsX8Changed = 0x200, - IsM8Changed = 0x400, - IsEmuChanged = 0x2000, -} MapFlags; - -typedef struct Rule { - uint32_t address; - uint16_t flags; - char *symbol; - struct Rule *next; -} Rule; - -typedef struct { - Rule *rules; - uint32_t minMemory; - uint32_t maxMemory; - MapFlags *mem; - const char *filename; -} Map; - -extern Map *loadMap(const char *filename, uint32_t org, MapFlags flags); diff --git a/omf.c b/omf.c deleted file mode 100644 index d8216ec..0000000 --- a/omf.c +++ /dev/null @@ -1,584 +0,0 @@ -/** - * @copyright 2018 Sean Kasun - * Handles parsing and relocating an OMF (s16, tool, etc) - */ - -#include "handle.h" -#include "parser.h" -#include -#include -#include - -const char *argp_program_version = "omf 0.5"; -const char *argp_program_bug_address = "sean@seancode.com"; -static char doc[] = "Relocate and extract OMF segments" - "\vThis should be run twice. The first time to generate the map." - "The second time, to use that map to relocate and extract the segments"; -static char args_doc[] = "FILE"; -static struct argp_option options[] = { - {"org", 'o', "ADDRESS", OPTION_ARG_OPTIONAL, - "Start mapping the segments at this address, default $20000"}, - {"map", 'm', "FILE", OPTION_ARG_OPTIONAL, - "Use this map to extract the segments"}, - {"prefix", 'p', "PREFIX", OPTION_ARG_OPTIONAL, - "Prefix segment files with this. Default \"seg\""}, - { 0 } -}; - -struct arguments { - char *filename; - char *map; - char *prefix; - uint32_t org; -}; - -static inline uint32_t parseNum(const char *s) { - uint32_t res = 0; - while (isspace(*s)) { - s++; - } - bool ishex = false; - if (s[0] == '0' && s[1] == 'x') { - s += 2; - ishex = true; - } else if (s[0] == '$') { - s++; - ishex = true; - } - if (ishex) { - while (isxdigit(*s)) { - res <<= 4; - if (*s >= '0' && *s <= '9') { - res |= *s - '0'; - } else if (*s >= 'a' && *s <= 'f') { - res |= *s - 'a' + 10; - } else if (*s >= 'A' && *s <= 'F') { - res |= *s - 'A' + 10; - } - s++; - } - } else { - while (isdigit(*s)) { - res *= 10; - res += *s - '0'; - s++; - } - } - return res; -} - -static error_t parse_opt(int key, char *arg, struct argp_state *state) { - struct arguments *arguments = state->input; - switch (key) { - case 'm': - arguments->map = arg; - break; - case 'p': - arguments->prefix = arg; - break; - case 'o': - if (arg) { - arguments->org = parseNum(arg); - } - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) { - argp_usage(state); - } - arguments->filename = arg; - break; - case ARGP_KEY_END: - if (state->arg_num < 1) { - argp_usage(state); - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -static struct argp argp = { options, parse_opt, args_doc, doc }; - - -typedef struct Segment { - uint32_t bytecnt; - uint32_t resspc; - uint32_t length; - uint8_t lablen; - uint8_t numlen; - uint32_t banksize; - uint16_t kind; - uint32_t org; - uint32_t align; - uint16_t segnum; - uint32_t entry; - char name[256]; - uint8_t *offset; - uint8_t *data; - uint32_t mapped; - struct Segment *next; -} Segment; - -static Segment *loadOMF(uint8_t *data, size_t len); -static void mapSegments(Segment *segments, uint32_t min); -static void relocSegments(Segment *segments, char *prefix); - -int main(int argc, char **argv) { - struct arguments arguments; - arguments.filename = ""; - arguments.map = NULL; - arguments.prefix = "seg"; - arguments.org = 0x20000; - argp_parse(&argp, argc, argv, 0, 0, &arguments); - - // open omf - FILE *f = fopen(arguments.filename, "rb"); - if (!f) { - fprintf(stderr, "Failed to open '%s'\n", arguments.filename); - return -1; - } - fseek(f, 0, SEEK_END); - size_t len = ftell(f); - fseek(f, 0, SEEK_SET); - uint8_t *data = malloc(len); - fread(data, len, 1, f); - fclose(f); - - Segment *segments = loadOMF(data, len); - if (arguments.map != NULL) { - // load the map! - f = fopen(arguments.map, "rb"); - if (!f) { - fprintf(stderr, "Failed to open '%s'\n", arguments.map); - return -1; - } - ConfigFile c; - fseek(f, 0, SEEK_END); - len = ftell(f); - fseek(f, 0, SEEK_SET); - c.start = malloc(len); - fread(c.start, len, 1, f); - fclose(f); - c.p = c.start; - c.end = c.start + len; - - while (c.p < c.end) { - uint32_t segnum = hex(&c); - if (!token(&c, ':')) { - fprintf(stderr, "Error: expected ':' in map.\n"); - exit(-1); - } - uint32_t mapped = hex(&c); - - for (Segment *seg = segments; seg != NULL; seg = seg->next) { - if (seg->segnum == segnum) { - seg->mapped = mapped; - } - } - eatSpaces(&c); - } - free(c.start); - } - - // map any unmapped segments - mapSegments(segments, arguments.org); - - if (arguments.map != NULL) { - relocSegments(segments, arguments.prefix); - } else { - int mlen = strlen(arguments.filename); - char *mapname = malloc(mlen + 5); - memcpy(mapname, arguments.filename, mlen); - memcpy(mapname + mlen, ".map\0", 5); - f = fopen(mapname, "wb"); - if (!f) { - fprintf(stderr, "Failed to create '%s'\n", mapname); - return -1; - } - for (Segment *seg = segments; seg != NULL; seg = seg->next) { - fprintf(f, "%x:%x\n", seg->segnum, seg->mapped); - } - fclose(f); - fprintf(stdout, "Created '%s'. Edit it, and extract with: \n" - " %s --map=%s %s\n", mapname, argv[0], mapname, arguments.filename); - } - - free(data); -} - -static Segment *loadOMF(uint8_t *data, size_t len) { - Segment *last = NULL; - Segment *segments = NULL; - - size_t ofs = 0; - while (ofs < len) { - Segment *seg = malloc(sizeof(Segment)); - seg->next = NULL; - - uint8_t *p = data + ofs; - seg->bytecnt = r32(p); p += 4; - seg->resspc = r32(p); p += 4; - seg->length = r32(p); p += 4; - uint8_t kind = *p++; - seg->lablen = *p++; - seg->numlen = *p++; - uint8_t version = *p++; - seg->banksize = r32(p); p += 4; - seg->kind = r16(p); p += 2; - p += 2; // undefined - seg->org = r32(p); p += 4; - seg->align = r32(p); p += 4; - p += 2; // byte order - seg->segnum = r16(p); p += 2; - seg->entry = r32(p); p += 4; - uint16_t dispname = r16(p); p += 2; - uint16_t dispdata = r16(p); p += 2; - p = data + ofs + dispname + 0xa; - uint8_t strlen = seg->lablen; - if (strlen == 0) { - strlen = *p++; - } - memcpy(seg->name, p, strlen); - seg->name[strlen] = 0; - seg->offset = data + ofs + dispdata; - if (version == 1) { // convert to v2 - seg->bytecnt *= 512; - seg->kind = (kind & 0x1f) | ((kind & 0xe0) << 8); - } - seg->mapped = 0; - seg->data = NULL; - - ofs += seg->bytecnt; - if (last == NULL) { - segments = seg; - } else { - last->next = seg; - } - last = seg; - } - return segments; -} - -static int cmpmemory(const void *p1, const void *p2) { - const uint32_t *a = p1, *b = p2; - return *a - *b; -} - -static void mapSegments(Segment *segments, uint32_t min) { - // we use a memory map that denotes runes of available memory. - // Each segment has a start and end, so we need an array of twice the - // number of segments (+2 for the absolute start and end of memory) - - // count segments - uint32_t numSegs = 0; - for (Segment *seg = segments; seg != NULL; seg = seg->next) { - numSegs++; - } - - uint32_t *memory = malloc(sizeof(uint32_t) * (numSegs * 2 + 2)); - int numNodes = 0; - memory[numNodes++] = min; // minimum - memory[numNodes++] = 0x1000000; // maximum possible memory for the IIgs. - - // step one, map hardcoded or overridden targets - for (Segment *seg = segments; seg != NULL; seg = seg->next) { - if (seg->org != 0 || seg->mapped != 0) { - if (seg->mapped == 0) { - if ((seg->kind & 0x1f) == 0x11) { // absolute bank - seg->mapped = seg->org << 16; - } else { - seg->mapped = seg->org; - } - } - bool collision = false; - // verify we aren't overlapping.. that would be tremendously bad. - for (int node = 0; node < numNodes; node += 2) { - if (seg->mapped < memory[node] && - seg->mapped + seg->length > memory[node]) { // crosses! - collision = true; - } - if (seg->mapped < memory[node + 1] && - seg->mapped + seg->length > memory[node + 1]) { // crosses! - collision = true; - } - } - if (collision) { - fprintf(stderr, "Segment #$%x collides with another segment!\n", - seg->segnum); - exit(-1); - } - memory[numNodes++] = seg->mapped; - memory[numNodes++] = seg->mapped + seg->length; - if (seg->mapped < memory[0]) { // below the minimum! - memory[0] = seg->mapped; // recalibrate the minimum - } - // resort memory map - qsort(memory, numNodes, sizeof(uint32_t), cmpmemory); - } - } - - // finally, map everything else by first fit. - for (Segment *seg = segments; seg != NULL; seg = seg->next) { - if (seg->mapped == 0) { - for (int node = 0; node < numNodes; node += 2) { - uint32_t base = memory[node]; - if (seg->align && (base & (seg->align - 1))) { // snap to alignment - base += seg->align; - base &= ~(seg->align - 1); - } - if (seg->banksize &&(((base & (seg->banksize - 1)) + seg->length) & - ~(seg->banksize - 1))) { // crosses bank - base += seg->banksize; - base &= ~(seg->banksize - 1); - } - // does it fit? - if (base < memory[node + 1] && memory[node + 1] - base >= seg->length) { - seg->mapped = base; - memory[numNodes++] = seg->mapped; - memory[numNodes++] = seg->mapped + seg->length; - qsort(memory, numNodes, sizeof(uint32_t), cmpmemory); - break; - } - } - if (seg->mapped == 0) { - fprintf(stderr, "Failed to map Segment #$%x, not enough free memory\n", - seg->segnum); - exit(-1); - } - } - } - free(memory); -} - -typedef enum { - DONE = 0x00, - RELOC = 0xe2, - INTERSEG = 0xe3, - DS = 0xf1, - LCONST = 0xf2, - cRELOC = 0xf5, - cINTERSEG = 0xf6, - SUPER = 0xf7 -} SegOp; - -static void patch(uint8_t *data, uint8_t numBytes, uint32_t value) { - for (int i = 0; i < numBytes; i++, value >>= 8) { - data[i] = value & 0xff; - } -} - -static void hexout(char *p, uint32_t val, int len) { - p += len; - while (len-- > 0) { - char ch = val & 0xf; - val >>= 4; - if (ch < 10) { - *--p = '0' + ch; - } else { - *--p = 'a' + (ch - 10); - } - } -} - -static void relocSegments(Segment *segments, char *prefix) { - int prefixLen = strlen(prefix); - char *filename = malloc(prefixLen + 4); - char *mapname = malloc(prefixLen + 9); - - for (Segment *seg = segments; seg != NULL; seg = seg->next) { - bool done = false; - uint32_t pc = seg->mapped; - uint8_t *p = seg->offset; - seg->data = calloc(seg->length, 1); - - while (!done) { - uint8_t opcode = *p++; - switch (opcode) { - case DONE: - done = true; - break; - case RELOC: - { - uint8_t numBytes = *p++; - int8_t bitShift = *p++; - uint32_t offset = r32(p); p += 4; - uint32_t subOffset = r32(p) + seg->mapped; p += 4; - - if (bitShift < 0) { - subOffset >>= -bitShift; - } else { - subOffset <<= bitShift; - } - patch(seg->data + offset, numBytes, subOffset); - } - break; - case INTERSEG: - { - uint8_t numBytes = *p++; - int8_t bitShift = *p++; - uint32_t offset = r32(p); p += 4; - p += 2; // filenum - uint16_t segnum = r16(p); p += 2; - uint32_t subOffset = r32(p); p += 4; - for (Segment *sub = segments; sub != NULL; sub = sub->next) { - if (sub->segnum == segnum) { - subOffset += sub->mapped; - break; - } - } - if (bitShift < 0) { - subOffset >>= -bitShift; - } else { - subOffset <<= bitShift; - } - patch(seg->data + offset, numBytes, subOffset); - } - break; - case DS: - pc += r32(p); p += 4; // filled with zeros - break; - case LCONST: - { - uint32_t count = r32(p); p += 4; - memcpy(seg->data + pc - seg->mapped, p, count); p += count; - pc += count; - } - break; - case cRELOC: - { - uint8_t numBytes = *p++; - int8_t bitShift = *p++; - uint16_t offset = r16(p); p += 2; - uint32_t subOffset = r16(p) + seg->mapped; p += 2; - if (bitShift < 0) { - subOffset >>= -bitShift; - } else { - subOffset <<= bitShift; - } - patch(seg->data + offset, numBytes, subOffset); - } - break; - case cINTERSEG: - { - uint8_t numBytes = *p++; - int8_t bitShift = *p++; - uint16_t offset = r16(p); p += 2; - uint8_t segnum = *p++; - uint32_t subOffset = r16(p); p += 2; - for (Segment *sub = segments; sub != NULL; sub = sub->next) { - if (sub->segnum == segnum) { - subOffset += sub->mapped; - break; - } - } - if (bitShift < 0) { - subOffset >>= -bitShift; - } else { - subOffset <<= bitShift; - } - patch(seg->data + offset, numBytes, subOffset); - } - break; - case SUPER: - { - uint32_t superLen = r32(p); p += 4; - uint8_t *superEnd = p + superLen; - uint8_t superType = *p++; - uint32_t superPage = 0; - while (p < superEnd) { - uint8_t numOfs = *p++; - if (numOfs & 0x80) { - superPage += 256 * (numOfs & 0x7f); - continue; - } - for (int o = 0; o <= numOfs; o++) { - uint32_t offset = superPage | *p++; - uint8_t numBytes = 0; - uint32_t subOffset = r16(seg->data + offset); - if (superType == 0 || superType == 1) { // RELOC2 | RELOC3 - subOffset += seg->mapped; - numBytes = 2 + superType; - } else if (superType < 14) { // INTERSEG1--12 - uint8_t segnum = seg->data[offset + 2]; - for (Segment *sub = segments; sub != NULL; sub = sub->next) { - if (sub->segnum == segnum) { - subOffset += sub->mapped; - break; - } - } - numBytes = 3; - } else if (superType < 26) { // INTERSEG13--24 - uint8_t segnum = superType - 13; - for (Segment *sub = segments; sub != NULL; sub = sub->next) { - if (sub->segnum == segnum) { - subOffset += sub->mapped; - break; - } - } - numBytes = 2; - } else { // INTERSEG25--36 - uint8_t segnum = superType - 25; - for (Segment *sub = segments; sub != NULL; sub = sub->next) { - if (sub->segnum == segnum) { - subOffset += sub->mapped; - break; - } - } - subOffset >>= 16; - numBytes = 2; - } - patch(seg->data + offset, numBytes, subOffset); - } - superPage += 256; - } - } - break; - default: - if (opcode < 0xe0) { - memcpy(seg->data + pc - seg->mapped, p, opcode); p += opcode; - pc += opcode; - } else { - fprintf(stderr, "Unknown segment code: %x\n", opcode); - exit(-1); - } - break; - } - } - - memcpy(filename, prefix, prefixLen); - int numLen = 3; - if (seg->segnum < 0x10) { - numLen = 1; - } else if (seg->segnum < 0x100) { - numLen = 2; - } - hexout(filename + prefixLen, seg->segnum, numLen); - filename[prefixLen + numLen] = 0; - memcpy(mapname, filename, prefixLen + numLen); - memcpy(mapname + prefixLen + numLen, ".map\0", 5); - - FILE *f = fopen(filename, "wb"); - if (!f) { - fprintf(stderr, "Failed to create '%s'\n", filename); - exit(-1); - } - fwrite(seg->data, 1, seg->length, f); - fclose(f); - f = fopen(mapname, "wb"); - if (!f) { - fprintf(stderr, "Failed to create '%s'\n", mapname); - exit(-1); - } - fprintf(f, "gMAP \"%s\"\n", filename); - fprintf(f, "sORG $%x\n", seg->mapped); - if ((seg->kind & 0x1f) == 0) { // code - fprintf(f, "$%x:\n", seg->mapped + seg->entry); - } - fclose(f); - fprintf(stdout, "Extracted Segment #$%x %s into %s\n", seg->segnum, - seg->name, filename); - fprintf(stdout, "Created map %s\n", mapname); - } - free(filename); -} diff --git a/parser.c b/parser.c deleted file mode 100644 index 7c5e460..0000000 --- a/parser.c +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @copyright 2018 Sean Kasun - * Routines for parsing a simple config file - */ - -#include "parser.h" -#include -#include -#include - -void fail(ConfigFile *f, char *format, ...) { - // calculate line and column - int line = 1; - int col = 1; - bool done = false; - while (f->p > f->start) { - if (*f->p == '\n') { - done = true; - line++; - } - if (!done) { - col++; - } - f->p--; - } - va_list args; - va_start(args, format); - fprintf(stderr, "%s[%d,%d] Error: ", f->filename, line, col); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - va_end(args); - exit(-1); -} - -void eatSpaces(ConfigFile *f) { - while (f->p < f->end && isspace(*f->p)) { - f->p++; - } -} - -bool token(ConfigFile *f, char ch) { - eatSpaces(f); - if (f->p < f->end && *f->p == ch) { // found it, consume - f->p++; - return true; - } - // not found, don't consume anything - return false; -} - -uint32_t hex(ConfigFile *f) { - eatSpaces(f); - uint32_t res = 0; - while (f->p < f->end && isxdigit(*f->p)) { - res <<= 4; - if (*f->p >= '0' && *f->p <= '9') { - res |= *f->p - '0'; - } else if (*f->p >= 'a' && *f->p <= 'f') { - res |= *f->p - 'a' + 10; - } else if (*f->p >= 'A' && *f->p <= 'F') { - res |= *f->p - 'A' + 10; - } - f->p++; - } - return res; -} diff --git a/parser.h b/parser.h deleted file mode 100644 index 5b8782f..0000000 --- a/parser.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * Routines for parsing simple config files - */ - -#include -#include -#include - -typedef struct { - uint8_t *start; - uint8_t *p; - uint8_t *end; - const char *filename; -} ConfigFile; - -extern void fail(ConfigFile *f, char *format, ...); -extern void eatSpaces(ConfigFile *f); -extern bool token(ConfigFile *f, char ch); -extern uint32_t hex(ConfigFile *f); diff --git a/prodos16.h b/prodos16.h deleted file mode 100644 index a5857e5..0000000 --- a/prodos16.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * The ProDOS 16 tool calls - */ - -typedef struct { - uint16_t call; - const char *name; -} Prodos16; - -static Prodos16 prodos16[] = { - {0x0001, "CREATE"}, - {0x0002, "DESTROY"}, - {0x0004, "CHANGE_PATH"}, - {0x0005, "SET_FILE_INFO"}, - {0x0006, "GET_FILE_INFO"}, - {0x0008, "VOLUME"}, - {0x0009, "SET_PREFIX"}, - {0x000a, "GET_PREFIX"}, - {0x000b, "CLEAR_BACKUP_BIT"}, - {0x0010, "OPEN"}, - {0x0011, "NEWLINE"}, - {0x0012, "READ"}, - {0x0013, "WRITE"}, - {0x0014, "CLOSE"}, - {0x0015, "FLUSH"}, - {0x0016, "SET_MARK"}, - {0x0017, "GET_MARK"}, - {0x0018, "SET_EOF"}, - {0x0019, "GET_EOF"}, - {0x001a, "SET_LEVEL"}, - {0x001b, "GET_LEVEL"}, - {0x001c, "GET_DIR_ENTRY"}, - {0x0020, "GET_DEV_NUM"}, - {0x0021, "GET_LAST_DEV"}, - {0x0022, "READ_BLOCK"}, - {0x0023, "WRITE_BLOCK"}, - {0x0024, "FORMAT"}, - {0x0025, "ERASE_DISK"}, - {0x0027, "GET_NAME"}, - {0x0028, "GET_BOOT_VOL"}, - {0x0029, "QUIT"}, - {0x002a, "GET_VERSION"}, - {0x002c, "D_INFO"}, - {0x0031, "ALLOC_INTERRUPT"}, - {0x0032, "DEALLOCATE_INTERRUPT"}, - {0x0101, "Get_LInfo"}, - {0x0102, "Set_LInfo"}, - {0x0103, "Get_Lang"}, - {0x0104, "Set_Lang"}, - {0x0105, "Error"}, - {0x0106, "Set_Variable"}, - {0x0107, "Version"}, - {0x0108, "Read_Indexed"}, - {0x0109, "Init_Wildcard"}, - {0x010a, "Next_Wildcard"}, - {0x010b, "Read_Variable"}, - {0x010c, "ChangeVector"}, - {0x010d, "Execute"}, - {0x010e, "FastFile"}, - {0x010f, "Direction"}, - {0x0110, "Redirect"}, - {0x0113, "Stop"}, - {0x0114, "ExpandDevices"}, - {0x0115, "UnsetVariable"}, - {0x0116, "Export"}, - {0x0117, "PopVariables"}, - {0x0118, "PushVariables"}, - {0x0119, "SetStopFlag"}, - {0x011a, "ConsoleOut"}, - {0x011b, "SetIODevices"}, - {0x011c, "GetIODevices"}, - {0x011d, "GetCommand"}, - {0x2001, "Create"}, - {0x2002, "Destroy"}, - {0x2003, "OSShutdown"}, - {0x2004, "ChangePath"}, - {0x2005, "SetFileInfo"}, - {0x2006, "GetFileInfo"}, - {0x2007, "JudgeName"}, - {0x2008, "Volume"}, - {0x2009, "SetPrefix"}, - {0x200a, "GetPrefix"}, - {0x200b, "ClearBackup"}, - {0x200c, "SetSysPrefs"}, - {0x200d, "Null"}, - {0x200e, "ExpandPath"}, - {0x200f, "GetSysPrefs"}, - {0x2010, "Open"}, - {0x2011, "NewLine"}, - {0x2012, "Read"}, - {0x2013, "Write"}, - {0x2014, "Close"}, - {0x2015, "Flush"}, - {0x2016, "SetMark"}, - {0x2017, "GetMark"}, - {0x2018, "SetEOF"}, - {0x2019, "GetEOF"}, - {0x201a, "SetLevel"}, - {0x201b, "GetLevel"}, - {0x201c, "GetDirEntry"}, - {0x201d, "BeginSession"}, - {0x201e, "EndSession"}, - {0x201f, "SessionStatus"}, - {0x2020, "GetDevNumber"}, - {0x2024, "Format"}, - {0x2025, "EraseDisk"}, - {0x2026, "ResetCache"}, - {0x2027, "GetName"}, - {0x2028, "GetBoolVol"}, - {0x2029, "Quit"}, - {0x202a, "GetVersion"}, - {0x202b, "GetFSTInfo"}, - {0x202c, "DInfo"}, - {0x202d, "DStatus"}, - {0x202e, "DControl"}, - {0x202f, "DRead"}, - {0x2030, "DWrite"}, - {0x2031, "BindInt"}, - {0x2032, "UnbindInt"}, - {0x2033, "FSTSpecific"}, - {0x2034, "AddNotifyProc"}, - {0x2035, "DelNotifyProc"}, - {0x2036, "DRename"}, - {0x2037, "GetStdRefNum"}, - {0x2038, "GetRefNum"}, - {0x2039, "GetRefInfo"}, - {0x203a, "SetStdRefNum"} -}; - -#define numProdos16 (sizeof(prodos16) / sizeof(prodos16[0])) - -static const char *prodos16Lookup(uint16_t call) { - for (int i = 0; i < numProdos16; i++) { - if (prodos16[i].call >= call) { - if (prodos16[i].call == call) - return prodos16[i].name; - break; - } - } - return NULL; -} diff --git a/prodos8.h b/prodos8.h deleted file mode 100644 index 09d5514..0000000 --- a/prodos8.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * Defines the ProDOS 8 tool calls - */ - -typedef struct { - uint16_t call; - const char *name; -} Prodos8; - -static Prodos8 prodos8[] = { - {0x0040, "ALLOC_INTERRUPT"}, - {0x0041, "DEALLOC_INTERRUPT"}, - {0x0042, "AppleTalk"}, - {0x0043, "SpecialOpenFork"}, - {0x0044, "ByteRangeLock"}, - {0x0065, "QUIT"}, - {0x0080, "READ_BLOCK"}, - {0x0081, "WRITE_BLOCK"}, - {0x0082, "GET_TIME"}, - {0x00c0, "CREATE"}, - {0x00c1, "DESTROY"}, - {0x00c2, "RENAME"}, - {0x00c3, "SetFileInfo"}, - {0x00c4, "GetFileInfo"}, - {0x00c5, "ONLINE"}, - {0x00c6, "SET_PREFIX"}, - {0x00c7, "GET_PREFIX"}, - {0x00c8, "OPEN"}, - {0x00c9, "NEWLINE"}, - {0x00ca, "READ"}, - {0x00cb, "WRITE"}, - {0x00cc, "CLOSE"}, - {0x00cd, "FLUSH"}, - {0x00ce, "SET_MARK"}, - {0x00cf, "GET_MARK"}, - {0x00d0, "SET_EOF"}, - {0x00d1, "GET_EOF"}, - {0x00d2, "SET_BUF"}, - {0x00d3, "GET_BUF"} -}; - -#define numProdos8 (sizeof(prodos8) / sizeof(prodos8[0])) - -static const char *prodos8Lookup(uint16_t call) { - for (int i = 0; i < numProdos8; i++) { - if (prodos8[i].call >= call) { - if (prodos8[i].call == call) - return prodos8[i].name; - break; - } - } - return NULL; -} diff --git a/regs.c b/regs.c deleted file mode 100644 index 469076c..0000000 --- a/regs.c +++ /dev/null @@ -1,137 +0,0 @@ -/** - * @copyright 2018 Sean Kasun - * The main disassembler - */ - -#include -#include -#include "handle.h" -#include "scan.h" -#include "disasm.h" - -const char *argp_program_version = "regs 0.1"; -const char *argp_program_bug_address = "sean@seancode.com"; -static char doc[] = "Disassemble Apple IIgs software"; -static char args_doc[] = "FILE or MAP"; -static struct argp_option options[] = { - {"org", 'o', "ADDRESS", 0, - "Starting address of the binary file"}, - {"m", 'm', 0, OPTION_ARG_OPTIONAL, - "Start with 8-bit accumulator"}, - {"x", 'x', 0, OPTION_ARG_OPTIONAL, - "Start with 8-bit indices"}, - {"e", 'e', 0, OPTION_ARG_OPTIONAL, - "Start in emulation mode"}, - { 0 } -}; - -struct arguments { - char *filename; - uint32_t org; - MapFlags flags; -}; - -static inline uint32_t parseNum(const char *s) { - uint32_t res = 0; - while (isspace(*s)) { - s++; - } - bool ishex = false; - if (s[0] == '0' && s[1] == 'x') { - s += 2; - ishex = true; - } else if (s[0] == '$') { - s++; - ishex = true; - } - if (ishex) { - while (isxdigit(*s)) { - res <<= 4; - if (*s >= '0' && *s <= '9') { - res |= *s - '0'; - } else if (*s >= 'a' && *s <= 'f') { - res |= *s - 'a' + 10; - } else if (*s >= 'A' && *s <= 'F') { - res |= *s - 'A' + 10; - } - s++; - } - } else { - while (isdigit(*s)) { - res *= 10; - res += *s - '0'; - s++; - } - } - return res; -} - -static error_t parse_opt(int key, char *arg, struct argp_state *state) { - struct arguments *arguments = state->input; - switch (key) { - case 'o': - if (arg) { - arguments->org = parseNum(arg); - } - break; - case 'm': - if (arg) { - arguments->flags |= IsM8; - } - break; - case 'x': - if (arg) { - arguments->flags |= IsX8; - } - break; - case 'e': - if (arg) { - arguments->flags |= IsEmu; - } - break; - case ARGP_KEY_ARG: - if (state->arg_num >= 1) { - argp_usage(state); - } - arguments->filename = arg; - break; - case ARGP_KEY_END: - if (state->arg_num < 1) { - argp_usage(state); - } - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -static struct argp argp = { options, parse_opt, args_doc, doc }; - -int main(int argc, char **argv) { - struct arguments arguments; - arguments.filename = ""; - arguments.org = 0x300; - arguments.flags = IsOpcode; - argp_parse(&argp, argc, argv, 0, 0, &arguments); - - // load the map, if it exists - Map *map = loadMap(arguments.filename, arguments.org, arguments.flags); - - // load the file - FILE *f = fopen(map->filename, "rb"); - if (!f) { - fprintf(stderr, "Failed to open '%s'\n", map->filename); - return -1; - } - fseek(f, 0, SEEK_END); - size_t len = ftell(f); - fseek(f, 0, SEEK_SET); - uint8_t *data = malloc(len); - fread(data, len, 1, f); - fclose(f); - - scan(data, len, map); - disassemble(data, len, map); - free(data); -} diff --git a/scan.c b/scan.c deleted file mode 100644 index a039295..0000000 --- a/scan.c +++ /dev/null @@ -1,187 +0,0 @@ -/** - * @copyright 2018 Sean Kasun - * The tracing scanner. - */ -#include "scan.h" -#include "65816.h" -#include "handle.h" - -typedef struct { - uint32_t address; - MapFlags flags; -} Scan; - -typedef struct { - uint32_t max; - uint32_t num; - Scan *values; -} List; - -static void initList(List *list) { - list->max = 1024; - list->num = 0; - list->values = malloc(sizeof(Scan) * list->max); -} - -static void freeList(List *list) { - free(list->values); -} - -static void appendList(List *list, uint32_t address, MapFlags flags) { - list->values[list->num].address = address; - list->values[list->num].flags = flags; - list->num++; - if (list->num == list->max) { - list->max *= 1.6; - list->values = realloc(list->values, sizeof(Scan) * list->max); - } -} - -static uint32_t fillMem(uint32_t address, int len, MapFlags flags, Map *map) { - for (int i = 0; i < len; i++) { - map->mem[address++ - map->minMemory] = flags; - } - return address; -} - -void scan(uint8_t *data, size_t len, Map *map) { - List toScan; - initList(&toScan); - map->maxMemory = map->minMemory + len; - - map->mem = calloc(len, sizeof(MapFlags)); - - for (Rule *rule = map->rules; rule != NULL; rule = rule->next) { - if (rule->flags & IsOpcode) { - appendList(&toScan, rule->address, rule->flags); - } - } - - while (toScan.num > 0) { - toScan.num--; - MapFlags flags = toScan.values[toScan.num].flags; - uint32_t address = toScan.values[toScan.num].address; - - while (address < map->maxMemory) { - uint8_t *ptr = data + address - map->minMemory; - - uint8_t opcode = *ptr++; - if (map->mem[address - map->minMemory] & IsOperand) { - // we jumped into the middle of an opcode.. go backward to clear it - uint32_t offset = (address - map->minMemory) - 1; - while (offset > 0 && (map->mem[offset] & IsOperand)) { - map->mem[offset--] = IsData; - } - map->mem[offset] = IsData; // overwrite the opcode too. - } - - Address mode = opcodes[opcode].address; - OpType type = opcodes[opcode].type; - if (address + addressSizes[mode] > map->maxMemory) { - mode = DB; - } - - uint16_t width = addressSizes[mode]; - if (mode == IMMM && (flags & (IsEmu | IsM8))) { - width--; - } - if (mode == IMMX && (flags & (IsEmu | IsX8))) { - width--; - } - - if (mode == DB) { - map->mem[address++ - map->minMemory] = IsData; - } else { - map->mem[address++ - map->minMemory] = IsOpcode | - (flags & (IsEmu | IsX8 | IsM8)); - address = fillMem(address, width - 1, IsOperand, map); - } - - uint32_t val = 0; - int8_t delta; - int16_t delta16; - - switch (mode) { - case IMM: - val = *ptr++; - if (opcode == 0xe2) { - flags |= val; - } else if (opcode == 0xc2) { - flags &= ~val; - } - break; - case IMMM: - if (flags & (IsEmu | IsM8)) { - val = *ptr++; - } else { - val = r16(ptr); ptr += 2; - } - break; - case IMMX: - if (flags & (IsEmu | IsX8)) { - val = *ptr++; - } else { - val = r16(ptr); ptr += 2; - } - break; - case IMMS: - val = r16(ptr); ptr += 2; - break; - case ABS: - val = r16(ptr); ptr += 2; - val |= address & 0xff0000; - break; - case ABL: - val = r24(ptr); ptr += 3; - break; - case REL: - delta = *ptr++; - val = delta + address; - break; - case RELL: - delta16 = r16(ptr); ptr += 2; - val = delta16 + address; - break; - default: - break; - } - - if (opcode == 0x18 && *ptr == 0xfb) { // clc xce - flags &= ~IsEmu; // 16-bit - } - if (opcode == 0x38 && *ptr == 0xfb) { // sec xce - flags |= IsEmu; // 8-bit - } - - if (opcode == 0x20) { // jsr - if ((val & 0xffff) == 0xc50d || (val & 0xffff) == 0xc70d) { // disk - address = fillMem(address, 3, IsData, map); // db, dw - if (*ptr++ >= 0x40) { - address = fillMem(address, 2, IsData, map); // dw - } - } - if ((val & 0xffff) == 0xbf00) { // prodos8 - address = fillMem(address, 3, IsData, map); // db, dw - } - } - if (opcode == 0x22) { // jsl - if (val == 0xe100a8) { - address = fillMem(address, 6, IsData, map); // dw, dd - } - } - - if (type == BRANCH || (type == CALL && mode != AIX) || - (type == JUMP && mode != IND && mode != AIX)) { - if (val >= map->minMemory && val < map->maxMemory && - !(map->mem[val - map->minMemory] & IsOpcode)) { - appendList(&toScan, val, flags); - } - } - if (type == BREAK || type == RETURN || type == JUMP) { - break; - } - } - } - - freeList(&toScan); -} diff --git a/scan.h b/scan.h deleted file mode 100644 index 8d1d92b..0000000 --- a/scan.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * The tracing scanner. - */ - -#include "map.h" -#include - -extern void scan(uint8_t *data, size_t len, Map *map); diff --git a/smartport.h b/smartport.h deleted file mode 100644 index 49ed455..0000000 --- a/smartport.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * Defines the SmartPort tool calls - */ - -typedef struct { - uint8_t call; - const char *name; -} SmartPort; - -static SmartPort smartport[] = { - {0x00, "Status"}, - {0x01, "Read"}, - {0x02, "Write"}, - {0x03, "Format"}, - {0x04, "Control"}, - {0x05, "Init"}, - {0x06, "Open"}, - {0x07, "Close"}, - {0x08, "Read"}, - {0x09, "Write"}, - {0x40, "Status"}, - {0x41, "Read"}, - {0x42, "Write"}, - {0x43, "Format"}, - {0x44, "Control"}, - {0x45, "Init"}, - {0x46, "Open"}, - {0x47, "Close"}, - {0x48, "Read"}, - {0x49, "Write"} -}; - -#define numSmartPort (sizeof(smartport) / sizeof(smartport[0])) - -static const char *smartportLookup(uint8_t call) { - for (int i = 0; i < numSmartPort; i++) { - if (smartport[i].call >= call) { - if (smartport[i].call == call) - return smartport[i].name; - break; - } - } - return NULL; -} diff --git a/src/2mg.cc b/src/2mg.cc new file mode 100644 index 0000000..6ac8c99 --- /dev/null +++ b/src/2mg.cc @@ -0,0 +1,317 @@ +/** @copyright 2020 Sean Kasun */ + +#include +#include +#include +#include +#include +#include +#include +#include "handle.h" +#include "prodos_types.h" + +const char *argp_program_version = "2mg 0.2"; +const char *argp_program_bug_address = "sean@seancode.com"; +static char doc[] = "Extract ProDOS disk images"; +static char args_doc[] = "DISKIMAGE"; +static struct argp_option options[] = { + {"list", 'l', 0, 0, "List files"}, + { 0 } +}; + +struct arguments { + const char *diskimage; + bool list; +}; + +static error_t parse_opt(int key, char *arg, struct argp_state *state) { + struct arguments *arguments = static_cast(state->input); + switch (key) { + case 'l': + arguments->list = true; + break; + case ARGP_KEY_ARG: + if (state->arg_num >= 1) { + argp_usage(state); + } + arguments->diskimage = arg; + break; + case ARGP_KEY_END: + if (state->arg_num < 1) { + argp_usage(state); + } + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, doc }; + +static std::string readFilename(Handle h, int len) { + static const char *digits = "0123456789abcdef"; + std::string r; + for (int i = 0; i < len; i++) { + uint8_t ch = h->r8(); + if (isalnum(ch) || ch == '_' || ch == '.' || ch == ' ') { + r += static_cast(ch); + } else { + r += 'x'; + uint8_t hi = ch >> 4; + uint8_t lo = ch & 0xf; + r += digits[hi]; + r += digits[lo]; + } + } + return r; +} + +static void indent(int depth) { + for (int i = 0; i < depth; i++) { + std::cout << " "; + } +} + +static void doEntry(uint32_t entry, Handle h, int depth); + +static void doDirectory(uint16_t key, Handle h, int depth) { + uint32_t block = key * 512 + 4; + h->seek(block); + auto type = h->r8(); + if ((type & 0xf0) != 0xf0 && (type & 0xf0) != 0xe0) { + std::cerr << "Invalid ProDOS disk" << std::endl; + return; + } + std::string dirname = readFilename(h, type & 0xf); + if (depth < 0) { + mkdir(dirname.c_str(), 0777); + chdir(dirname.c_str()); + } else { + indent(depth); + std::cout << dirname << " " << std::endl; + } + h->seek(block + 0x1f); + auto entryLength = h->r8(); + auto entriesPerBlock = h->r8(); + auto fileCount = h->r16(); + uint8_t curEntry = 1; + uint16_t curFile = 0; + + while (curFile < fileCount) { + auto entry = block + curEntry * entryLength; + h->seek(entry); + auto type = h->r8(); + if (type) { + doEntry(entry, h, depth < 0 ? -1 : depth + 1); + curFile++; + } + curEntry++; + if (curEntry == entriesPerBlock) { + curEntry = 0; + h->seek(block - 2); + block = h->r16() * 512 + 4; + } + } + if (depth < 0) { + chdir(".."); + } +} + +static void printDateTime(uint16_t date, uint16_t time) { + std::cout << std::setfill('0') + << std::setw(2) << ((date >> 9) & 0x7f) << "-" + << std::setw(2) << ((date >> 5) & 0xf) << "-" + << std::setw(2) << (date & 0x1f) << " " + << std::setw(2) << ((time >> 8) & 0x1f) << ":" + << std::setw(2) << (time & 0x3f); +} + +static void dumpSeedling(Handle h, uint32_t len, std::ostream &f) { + h->dump(len, f); +} + +static void dumpSapling(Handle h, uint32_t len, std::ostream &f) { + auto index = h->tell(); + while (len > 0) { + h->seek(index); + uint16_t blockid = h->r8(); + h->skip(255); + blockid |= h->r8() << 8; + uint32_t blen = std::min(512, len); + if (blockid && (blockid + 1) * 512 <= h->length) { + h->seek(blockid * 512); + dumpSeedling(h, blen, f); + } else { + f.seekp(blen, std::ios::cur); + } + len -= blen; + index++; + } +} + +static void dumpTree(Handle h, uint32_t len, std::ostream &f) { + auto index = h->tell(); + while (len > 0) { + h->seek(index); + uint16_t blockid = h->r8(); + h->skip(255); + blockid |= h->r8() << 8; + uint32_t blen = std::min(256 * 512, len); + if (blockid && (blockid + 1) * 512 <= h->length) { + h->seek(blockid * 512); + dumpSapling(h, blen, f); + } else { + f.seekp(blen, std::ios::cur); + } + len -= blen; + index++; + } +} + +static void doFile(uint16_t key, uint32_t len, std::string name, + Handle h, int type) { + h->seek(key * 512); + std::ofstream f(name, std::ios::out | std::ios::binary | std::ios::trunc); + if (!f.is_open()) { + std::cerr << "Failed to create " << name << std::endl; + return; + } + switch (type) { + case 1: + dumpSeedling(h, len, f); + break; + case 2: + dumpSapling(h, len, f); + break; + case 3: + dumpTree(h, len, f); + break; + } + f.close(); +} + +static void doGSOS(uint16_t key, std::string filename, uint8_t type, + Handle h, int depth) { + h->seek(key * 512); + auto filetype = h->r8(); + auto subkey = h->r16(); + h->skip(2); + auto eof = h->r24(); + if (depth < 0) { + doFile(subkey, eof, filename, h, filetype); + } + std::string resname = filename; + resname += ".res"; + h->seek(key * 512 + 0x100); + filetype = h->r8(); + subkey = h->r16(); + h->skip(2); + auto reof = h->r24(); + if (depth < 0) { + doFile(subkey, reof, resname, h, filetype); + } else { + indent(depth); + std::cout << filename << " " << eof << " bytes resource: " + << reof << " bytes" << std::endl; + } +} + +static void doEntry(uint32_t entry, Handle h, int depth) { + h->seek(entry); + auto type = h->r8(); + std::string filename = readFilename(h, type & 0xf); + + h->seek(entry + 0x10); + auto filetype = h->r8(); + auto key = h->r16(); + h->skip(2); + auto eof = h->r24(); + + switch (type & 0xf0) { + case 0x10: // seedling + case 0x20: // sapling + case 0x30: // tree + if (depth < 0) { + doFile(key, eof, filename, h, type >> 4); + } else { + auto createDate = h->r16(); + auto createTime = h->r16(); + h->skip(3); + auto aux = h->r16(); + auto modDate = h->r16(); + auto modTime = h->r16(); + indent(depth); + std::cout << filename << std::endl; + indent(depth); + std::cout << " Size: " << eof; + std::cout << " Created: "; + printDateTime(createDate, createTime); + std::cout << " Modified: "; + printDateTime(modDate, modTime); + std::cout << std::endl; + indent(depth); + std::cout << " Type: $" << std::hex << std::setw(2) + << std::setfill('0') << static_cast(filetype) + << " Aux: $" << std::setw(4) << aux << std::dec << " "; + uint32_t typeaux = filetype | (aux << 8); + for (int i = 0; i < numTypes; i++) { + if ((typeaux & fileTypes[i].mask) == fileTypes[i].id) { + std::cout << fileTypes[i].ext << "/" << fileTypes[i].desc; + break; + } + } + std::cout << std::endl; + } + break; + case 0x50: + doGSOS(key, filename, filetype, h, depth); + break; + case 0xd0: + doDirectory(key, h, depth < 0 ? -1 : depth + 1); + break; + default: + std::cerr << "Unknown file storage: " << static_cast(type) + << std::endl; + return; + } +} + +class MyPunct : public std::numpunct { + protected: + virtual char do_thousands_sep() const { return ','; } + virtual std::string do_grouping() const { return "\03"; } +}; + +int main(int argc, char **argv) { + struct arguments arguments; + arguments.list = false; + + argp_parse(&argp, argc, argv, 0, 0, &arguments); + + std::cout.imbue(std::locale(std::locale::classic(), new MyPunct)); + + auto h = TheHandle::createFromFile(arguments.diskimage); + if (!h->isOpen()) { + std::cerr << "Failed to open " << arguments.diskimage << std::endl; + return -1; + } + if (h->length < 64) { + std::cerr << arguments.diskimage << " is not a valid prodos disk image" + << std::endl; + return -1; + } + if (h->read(4) == "2IMG") { + h->seek(0xc); + if (h->r32() != 1) { + std::cerr << "Not a ProDOS disk image" << std::endl; + return -1; + } + h->seek(0x14); + auto disklen = h->r32() * 512; + auto diskofs = h->r32(); + h->seek(diskofs); + h = TheHandle::createFromArray(h->readBytes(disklen)); + } + doDirectory(2, h, arguments.list ? 0 : -1); +} diff --git a/src/Makefile b/src/Makefile index fa2c0f4..f6fafa0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,4 +1,5 @@ OBJS = main.o omf.o handle.o map.o disasm.o api.o scanner.o +OBJS2mg = 2mg.o handle.o CXX = clang++ CXXFLAGS = -g -Wall -std=c++11 @@ -7,7 +8,10 @@ ifeq ($(UNAME), Darwin) LDFLAGS = -largp endif -all: ../regs +all: ../regs ../2mg + +../2mg: $(OBJS2mg) + $(CXX) $(CXXFLAGS) $(LIBS) -o $@ $(LDFLAGS) $^ ../regs: $(OBJS) $(CXX) $(CXXFLAGS) $(LIBS) -o $@ $(LDFLAGS) $^ @@ -16,4 +20,4 @@ all: ../regs $(CXX) -c $(CXXFLAGS) -o $@ $< clean: - rm -f ../regs $(OBJS) + rm -f ../regs ../2mg $(OBJS) $(OBJS2mg) diff --git a/src/api.cc b/src/api.cc index 1b0677e..0414e74 100644 --- a/src/api.cc +++ b/src/api.cc @@ -1,6 +1,7 @@ /** @copyright 2020 Sean Kasun */ #include +#include #include "api.h" Fingerprints::Fingerprints() { @@ -199,3 +200,151 @@ void API::setFunction(Handle h, std::shared_ptr s) { f->signature.push_back(static_cast(h->r32())); } } + +void API::search(std::string keyword, uint32_t org) { + for (auto &s : symbols) { + auto it = std::search(s.first.begin(), s.first.end(), + keyword.begin(), keyword.end(), + [](char a, char b) { + return std::toupper(a) == std::toupper(b); + }); + if (it != s.first.end()) { + dumpSymbol(s.second, org); + } + } +} + +void API::dumpRef(std::shared_ptr ref) { + for (int i = 0; i < ref->pointer; i++) { + printf("^"); + } + printf("%s", ref->symbol->name.c_str()); + if (ref->array > 0) { + printf("[%d]", ref->array); + } else if (ref->array == 0) { + printf("[]"); + } +} + +void API::dumpSymbol(std::shared_ptr sym, uint32_t org) { + printf("%s: ", sym->name.c_str()); + switch (sym->kind) { + case symbol::Kind::isIntrinsic: + switch (std::static_pointer_cast(sym)->type) { + case symbol::Intrinsic::U8: + printf("uint8"); + break; + case symbol::Intrinsic::U16: + printf("uint16"); + break; + case symbol::Intrinsic::U32: + printf("uint32"); + break; + case symbol::Intrinsic::S8: + printf("int8"); + break; + case symbol::Intrinsic::S16: + printf("int16"); + break; + case symbol::Intrinsic::S32: + printf("int32"); + break; + } + printf("\n"); + break; + case symbol::Kind::isEnum: + { + auto e = std::static_pointer_cast(sym); + printf("enum : %s {\n", e->type->name.c_str()); + for (auto entry : e->entries) { + printf(" %s = $%x,\n", entry.first.c_str(), entry.second); + } + printf("}\n"); + } + break; + case symbol::Kind::isAlias: + case symbol::Kind::isRef: + dumpRef(std::static_pointer_cast(sym)); + printf("\n"); + break; + case symbol::Kind::isStruct: + { + auto s = std::static_pointer_cast(sym); + printf("struct { // $%x bytes\n", sym->size); + for (auto &f : s->fields) { + printf(" %s: ", f.key.c_str()); + switch (f.value->kind) { + case symbol::Kind::isRef: + dumpRef(std::static_pointer_cast(f.value)); + break; + default: + printf("%s", f.value->name.c_str()); + break; + } + printf(" // $"); + if (org > 0xffff) { + printf("%02x/%04x\n", org >> 16, org & 0xffff); + } else { + printf("%04x\n", org); + } + org += f.value->size; + } + printf("}\n"); + } + break; + case symbol::Kind::isUnion: + { + auto s = std::static_pointer_cast(sym); + printf("union { // $%x bytes\n", sym->size); + for (auto &f : s->fields) { + printf(" %s: ", f.key.c_str()); + switch (f.value->kind) { + case symbol::Kind::isRef: + dumpRef(std::static_pointer_cast(f.value)); + break; + default: + printf("%s", f.value->name.c_str()); + break; + } + printf(" // $"); + if (org > 0xffff) { + printf("%02x/%04x\n", org >> 16, org & 0xffff); + } else { + printf("%04x\n", org); + } + } + printf("}\n"); + } + break; + case symbol::Kind::isFunction: + { + auto f = std::static_pointer_cast(sym); + printf("(\n"); + for (auto &a : f->arguments) { + printf(" %s: ", a.key.c_str()); + dumpRef(a.ref); + printf(",\n"); + } + if (f->returnType->symbol != nullptr) { + printf("): "); + dumpRef(f->returnType); + } else { + printf(")"); + } + if (f->signature[0] >= 0) { // tool + printf(" = TOOL $%02x:%02x\n", f->signature[0], + f->signature[1]); + } else if (f->signature[0] == -1) { // p16/gsos + printf(" = GSOS $%04x\n", f->signature[2]); + } else if (f->signature[0] == -2) { // p8 + printf(" = P8 $%02x\n", f->signature[2]); + } else if (f->signature[0] == -3) { // smartport + printf(" = Smartport $%02x\n", f->signature[2]); + } else if (f->signature[0] == -4) { // symbol + printf(" = $%02x/%04x\n", f->signature[1] >> 16, + f->signature[1] & 0xffff); + } + } + break; + } +} diff --git a/src/api.h b/src/api.h index 8a2a671..9e4f7ff 100644 --- a/src/api.h +++ b/src/api.h @@ -73,6 +73,9 @@ class API { public: API(unsigned char *dat, unsigned int len); std::map> symbols; + void search(std::string keyword, uint32_t org); + void dumpSymbol(std::shared_ptr symbol, uint32_t org); + void dumpRef(std::shared_ptr ref); private: std::shared_ptr lookup(uint32_t id); diff --git a/src/disasm.cc b/src/disasm.cc index 1c593ee..264fe55 100644 --- a/src/disasm.cc +++ b/src/disasm.cc @@ -64,10 +64,10 @@ std::shared_ptr Disassembler::decodeInst(Handle f, Entry *entry) { inst->type = opcodes[opcode].type; auto mode = opcodes[opcode].addressing; inst->length = sizes[mode]; - if (mode == IMMM && (entry->flags & (IsEmu | IsM8))) { + if (mode == IMMM && (entry->flags & IsM8)) { inst->length--; } - if (mode == IMMX && (entry->flags & (IsEmu | IsX8))) { + if (mode == IMMX && (entry->flags & IsX8)) { inst->length--; } entry->org += inst->length; @@ -86,15 +86,9 @@ std::shared_ptr Disassembler::decodeInst(Handle f, Entry *entry) { } else if (opcode == 0xc2) { entry->flags &= ~inst->oper; } - if ((entry->flags ^ oldFlags) & IsX8) { - entry->flags |= IsX8Changed; - } - if ((entry->flags ^ oldFlags) & IsM8) { - entry->flags |= IsM8Changed; - } break; case IMMM: - if (entry->flags & (IsEmu | IsM8)) { + if (entry->flags & IsM8) { inst->oper = f->r8(); inst->operType = Opr::Imm8; } else { @@ -103,7 +97,7 @@ std::shared_ptr Disassembler::decodeInst(Handle f, Entry *entry) { } break; case IMMX: - if (entry->flags & (IsEmu | IsX8)) { + if (entry->flags & IsX8) { inst->oper = f->r8(); inst->operType = Opr::Imm8; } else { @@ -194,7 +188,7 @@ std::shared_ptr Disassembler::decodeInst(Handle f, Entry *entry) { break; case INX: inst->oper = f->r8(); - inst->operType = Opr::IndX; + inst->operType = Opr::IndXD; break; case INY: inst->oper = f->r8(); @@ -235,19 +229,25 @@ std::shared_ptr Disassembler::decodeInst(Handle f, Entry *entry) { } if (opcode == 0x18) { if (f->r8() == 0xfb) { // clc xce - entry->flags &= 0xffffff ^ IsEmu; + entry->flags &= ~IsEmu; } f->skip(-1); } if (opcode == 0x38) { if (f->r8() == 0xfb) { // sec xce - entry->flags |= IsEmu; + entry->flags |= IsEmu | IsM8 | IsX8; } f->skip(-1); } if ((entry->flags ^ oldFlags) & IsEmu) { entry->flags |= IsEmuChanged; } + if ((entry->flags ^ oldFlags) & IsX8) { + entry->flags |= IsX8Changed; + } + if ((entry->flags ^ oldFlags) & IsM8) { + entry->flags |= IsM8Changed; + } inst->flags = entry->flags; return inst; } @@ -318,14 +318,17 @@ std::string Disassembler::printInst(std::shared_ptr inst) { args = "(B:" + hex(inst->oper, 4) + ", x)"; comment += lookup(inst->oper); break; + case Opr::IndXD: + args = "(D:" + hex(inst->oper, 2) + ")"; + break; case Opr::IndY: - args = "(" + hex(inst->oper, 6) + "), y"; + args = "(D:" + hex(inst->oper, 2) + "), y"; break; case Opr::IndL: - args = "[" + hex(inst->oper, 6) + "]"; + args = "[D:" + hex(inst->oper, 2) + "]"; break; case Opr::IndLY: - args = "[" + hex(inst->oper, 6) + "], y"; + args = "[D:" + hex(inst->oper, 2) + "], y"; break; case Opr::IndS: args = "(" + hex(inst->oper, 2) + ", s), y"; diff --git a/src/disasm.h b/src/disasm.h index cc15092..5b67f49 100644 --- a/src/disasm.h +++ b/src/disasm.h @@ -28,7 +28,7 @@ enum InsType : uint16_t { enum class Opr { None = 0, Imm8, Imm16, Abs, AbsB, AbsD, AbsX, AbsXB, AbsXD, - AbsY, AbsYB, AbsYD, AbsS, Ind, IndB, IndD, IndX, IndXB, + AbsY, AbsYB, AbsYD, AbsS, Ind, IndB, IndD, IndX, IndXB, IndXD, IndY, IndL, IndLY, IndS, Bank, }; diff --git a/src/handle.cc b/src/handle.cc index b02aea8..d6fd0a0 100644 --- a/src/handle.cc +++ b/src/handle.cc @@ -105,3 +105,7 @@ void TheHandle::seek(int64_t pos) { void TheHandle::skip(int64_t ofs) { pos += ofs; } + +void TheHandle::dump(int64_t length, std::ostream &f) { + f.write(reinterpret_cast(pos), length); +} diff --git a/src/handle.h b/src/handle.h index 2607112..3b67555 100644 --- a/src/handle.h +++ b/src/handle.h @@ -24,6 +24,7 @@ class TheHandle { void skip(int64_t length); std::string read(int32_t length); std::vector readBytes(int32_t length); + void dump(int64_t length, std::ostream &f); int64_t length; diff --git a/src/main.cc b/src/main.cc index 569e173..09d88e9 100644 --- a/src/main.cc +++ b/src/main.cc @@ -16,6 +16,7 @@ static struct argp_option options[] = { {"m", 'm', 0, OPTION_ARG_OPTIONAL, "Start with 8-bit accumulator"}, {"x", 'x', 0, OPTION_ARG_OPTIONAL, "Start with 8-bit indices"}, {"e", 'e', 0, OPTION_ARG_OPTIONAL, "Start in emulation mode"}, + {"l", 'l', "KEYWORD", 0, "Search API for KEYWORD"}, { 0 }, }; @@ -23,9 +24,11 @@ struct arguments { const char *filename; uint32_t org; uint32_t flags; + const char *keyword; }; static inline uint32_t parseNum(const char *s) { + uint32_t bank = 0; uint32_t res = 0; while (isspace(*s)) { s++; @@ -39,7 +42,13 @@ static inline uint32_t parseNum(const char *s) { ishex = true; } if (ishex) { - while (isxdigit(*s)) { + while (isxdigit(*s) || *s == '/') { + if (*s == '/') { + s++; + bank = res; + res = 0; + continue; + } res <<= 4; if (isdigit(*s)) { res |= *s - '0'; @@ -57,7 +66,7 @@ static inline uint32_t parseNum(const char *s) { s++; } } - return res; + return (bank << 16) | res; } static error_t parse_opt(int key, char *arg, struct argp_state *state) { @@ -83,6 +92,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { arguments->flags |= IsEmu; } break; + case 'l': + arguments->keyword = arg; + break; case ARGP_KEY_ARG: if (state->arg_num >= 1) { argp_usage(state); @@ -90,7 +102,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { arguments->filename = arg; break; case ARGP_KEY_END: - if (state->arg_num < 1) { + if (state->arg_num < 1 && !arguments->keyword) { argp_usage(state); } break; @@ -103,12 +115,20 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) { static struct argp argp = { options, parse_opt, args_doc, doc }; int main(int argc, char **argv) { + API api(iigs_dat, iigs_dat_len); + struct arguments arguments; arguments.filename = ""; + arguments.keyword = nullptr; arguments.org = 0; arguments.flags = 0; argp_parse(&argp, argc, argv, 0, 0, &arguments); + if (arguments.keyword) { + api.search(arguments.keyword, arguments.org); + return 0; + } + // load map if it exists Map map(arguments.filename, arguments.org); OMF omf; @@ -130,7 +150,6 @@ int main(int argc, char **argv) { // from the api map.save(); - API api(iigs_dat, iigs_dat_len); auto prints = std::make_shared(); for (auto s : api.symbols) { diff --git a/src/map.cc b/src/map.cc index 5fbffe4..5435f02 100644 --- a/src/map.cc +++ b/src/map.cc @@ -2,6 +2,7 @@ #include "map.h" #include #include +#include struct Field { uint32_t org; @@ -14,11 +15,13 @@ struct Field { Map::Map(const char *filename, uint32_t org) : org(org) { mapname = filename; mapname += ".regs"; + usedMap = false; File file(mapname); if (!file.is_open()) { return; } + usedMap = true; while (!file.eof()) { uint32_t ofs = file.hex(); if (file.check('!')) { @@ -35,6 +38,8 @@ Map::Map(const char *filename, uint32_t org) : org(org) { switch (ch) { case 'e': entry.flags |= IsEmu; + entry.flags |= IsM8; + entry.flags |= IsX8; break; case 'm': entry.flags |= IsM8; @@ -64,12 +69,13 @@ void Map::save() { fields[org].isEntry = true; if (entryPoint.flags & IsEmu) { fields[org].flags += 'e'; - } - if (entryPoint.flags & IsM8) { - fields[org].flags += 'm'; - } - if (entryPoint.flags & IsX8) { - fields[org].flags += 'x'; + } else { // only if not emu, otherwise its redundant + if (entryPoint.flags & IsM8) { + fields[org].flags += 'm'; + } + if (entryPoint.flags & IsX8) { + fields[org].flags += 'x'; + } } } for (auto sym : symbols) { @@ -102,7 +108,7 @@ void Map::save() { } bool Map::needsEntry() { - return entryPoints.size() == 0; + return !usedMap; } std::vector Map::getEntries() { diff --git a/src/map.h b/src/map.h index f5798c2..bd0a0f7 100644 --- a/src/map.h +++ b/src/map.h @@ -42,6 +42,7 @@ class Map { private: std::string mapname; + bool usedMap; std::vector entryPoints; std::map symbols; std::string toAddress(uint32_t val); diff --git a/src/omf.cc b/src/omf.cc index 6a9edd0..44a3783 100644 --- a/src/omf.cc +++ b/src/omf.cc @@ -22,6 +22,9 @@ static bool compareSegments(const Segment &a, const Segment &b) { bool OMF::load(const char *filename, uint32_t org) { handle = TheHandle::createFromFile(filename); + if (!handle->isOpen()) { + return false; + } if (!isOMF()) { Segment seg; @@ -31,6 +34,7 @@ bool OMF::load(const char *filename, uint32_t org) { seg.mapped = org; seg.data = handle; seg.length = seg.bytecnt; + seg.segnum = 1; segments.push_back(seg); } else { if (!loadSegments()) { diff --git a/prodos_types.h b/src/prodos_types.h similarity index 100% rename from prodos_types.h rename to src/prodos_types.h diff --git a/src/scanner.cc b/src/scanner.cc index 7dceb6a..247835d 100644 --- a/src/scanner.cc +++ b/src/scanner.cc @@ -94,6 +94,9 @@ static bool compareBlocks(std::shared_ptr a, std::shared_ptr b) { } bool Scanner::basicBlocks() { + if (labels.empty()) { // no blocks + return true; + } // always start at a label auto address = labels.lower_bound(0)->first; auto block = getBlock(address); diff --git a/src/scanner.h b/src/scanner.h index 62ec124..ff41277 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -2,6 +2,7 @@ #pragma once #include "map.h" +#include struct Block { explicit Block(uint32_t address) : address(address) {} diff --git a/tools.h b/tools.h deleted file mode 100644 index 7395d76..0000000 --- a/tools.h +++ /dev/null @@ -1,1290 +0,0 @@ -#pragma once - -/** - * @copyright 2018 Sean Kasun - * Defines the IIgs Tool calls - */ - -typedef struct { - uint16_t id; - const char *name; -} Tool; - -static Tool tools[] = { - {0x0101, "TLBootInit"}, - {0x0102, "MMBootInit"}, - {0x0103, "MTBootInit"}, - {0x0104, "QDBootInit"}, - {0x0105, "DeskBootInit"}, - {0x0106, "EMBootInit"}, - {0x0107, "SchBootInit"}, - {0x0108, "SoundBootInit"}, - {0x0109, "ADBBootInit"}, - {0x010a, "SANEBootInit"}, - {0x010b, "IMBootInit"}, - {0x010c, "TextBootInit"}, - {0x010e, "WindBootInit"}, - {0x010f, "MenuBootInit"}, - {0x0110, "CtlBootInit"}, - {0x0111, "LoaderBootInit"}, - {0x0112, "QDAuxBootInit"}, - {0x0113, "PMBootInit"}, - {0x0114, "LEBootInit"}, - {0x0115, "DialogBootInit"}, - {0x0116, "ScrapBootInit"}, - {0x0117, "SFBootInit"}, - {0x0118, "DUBootInit"}, - {0x0119, "NSBootInit"}, - {0x011a, "SeqBootInit"}, - {0x011b, "FMBootInit"}, - {0x011c, "ListBootInit"}, - {0x011d, "ACEBootInit"}, - {0x011e, "ResourceBootInit"}, - {0x0120, "MIDIBootInit"}, - {0x0121, "VDBootInit"}, - {0x0122, "TEBootInit"}, - {0x0123, "MSBootInit"}, - {0x0125, "AnimBootInit"}, - {0x0201, "TLStartUp"}, - {0x0202, "MMStartUp"}, - {0x0203, "MTStartUp"}, - {0x0204, "QDStartUp"}, - {0x0205, "DeskStartUp"}, - {0x0206, "EMStartUp"}, - {0x0207, "SchStartUp"}, - {0x0208, "SoundStartUp"}, - {0x0209, "ADBStartUp"}, - {0x020a, "SANEStartUp"}, - {0x020b, "IMStartUp"}, - {0x020c, "TextStartUp"}, - {0x020e, "WindStartUp"}, - {0x020f, "MenuStartUp"}, - {0x0210, "CtlStartUp"}, - {0x0211, "LoaderStartUp"}, - {0x0212, "QDAuxStartUp"}, - {0x0213, "PMStartUp"}, - {0x0214, "LEStartUp"}, - {0x0215, "DialogStartUp"}, - {0x0216, "ScrapStartUp"}, - {0x0217, "SFStartUp"}, - {0x0218, "DUStartUp"}, - {0x0219, "NSStartUp"}, - {0x021a, "SeqStartUp"}, - {0x021b, "FMStartUp"}, - {0x021c, "ListStartUp"}, - {0x021d, "ACEStartUp"}, - {0x021e, "ResourceStartUp"}, - {0x0220, "MIDIStartUp"}, - {0x0221, "VDStartUp"}, - {0x0222, "TEStartUp"}, - {0x0223, "MSStartUp"}, - {0x0225, "AnimStartUp"}, - {0x0301, "TLShutDown"}, - {0x0302, "MMShutDown"}, - {0x0303, "MTShutDown"}, - {0x0304, "QDShutDown"}, - {0x0305, "DeskShutDown"}, - {0x0306, "EMShutDown"}, - {0x0307, "SchShutDown"}, - {0x0308, "SoundShutDown"}, - {0x0309, "ADBShutDown"}, - {0x030a, "SANEShutDown"}, - {0x030b, "IMShutDown"}, - {0x030c, "TextShutDown"}, - {0x030e, "WindShutDown"}, - {0x030f, "MenuShutDown"}, - {0x0310, "CtlShutDown"}, - {0x0311, "LoaderShutDown"}, - {0x0312, "QDAuxShutDown"}, - {0x0313, "PMShutDown"}, - {0x0314, "LEShutDown"}, - {0x0315, "DialogShutDown"}, - {0x0316, "ScrapShutDown"}, - {0x0317, "SFShutDown"}, - {0x0318, "DUShutDown"}, - {0x0319, "NSShutDown"}, - {0x031a, "SeqShutDown"}, - {0x031b, "FMShutDown"}, - {0x031c, "ListShutDown"}, - {0x031d, "ACEShutDown"}, - {0x031e, "ResourceShutDown"}, - {0x0320, "MIDIShutDown"}, - {0x0321, "VDShutDown"}, - {0x0322, "TEShutDown"}, - {0x0323, "MSShutDown"}, - {0x0325, "AnimShutDown"}, - {0x0401, "TLVersion"}, - {0x0402, "MMVersion"}, - {0x0403, "MTVersion"}, - {0x0404, "QDVersion"}, - {0x0405, "DeskVersion"}, - {0x0406, "EMVersion"}, - {0x0407, "SchVersion"}, - {0x0408, "SoundVersion"}, - {0x0409, "ADBVersion"}, - {0x040a, "SANEVersion"}, - {0x040b, "IMVersion"}, - {0x040c, "TextVersion"}, - {0x040e, "WindVersion"}, - {0x040f, "MenuVersion"}, - {0x0410, "CtlVersion"}, - {0x0411, "LoaderVersion"}, - {0x0412, "QDAuxVersion"}, - {0x0413, "PMVersion"}, - {0x0414, "LEVersion"}, - {0x0415, "DialogVersion"}, - {0x0416, "ScrapVersion"}, - {0x0417, "SFVersion"}, - {0x0418, "DUVersion"}, - {0x0419, "NSVersion"}, - {0x041a, "SeqVersion"}, - {0x041b, "FMVersion"}, - {0x041c, "ListVersion"}, - {0x041d, "ACEVersion"}, - {0x041e, "ResourceVersion"}, - {0x0420, "MIDIVersion"}, - {0x0421, "VDVersion"}, - {0x0422, "TEVersion"}, - {0x0423, "MSVersion"}, - {0x0425, "AnimVersion"}, - {0x0501, "TLReset"}, - {0x0502, "MMReset"}, - {0x0503, "MTReset"}, - {0x0504, "QDReset"}, - {0x0505, "DeskReset"}, - {0x0506, "EMReset"}, - {0x0507, "SchReset"}, - {0x0508, "SoundReset"}, - {0x0509, "ADBReset"}, - {0x050a, "SANEReset"}, - {0x050b, "IMReset"}, - {0x050c, "TextReset"}, - {0x050e, "WindReset"}, - {0x050f, "MenuReset"}, - {0x0510, "CtlReset"}, - {0x0511, "LoaderReset"}, - {0x0512, "QDAuxReset"}, - {0x0513, "PMReset"}, - {0x0514, "LEReset"}, - {0x0515, "DialogReset"}, - {0x0516, "ScrapReset"}, - {0x0517, "SFReset"}, - {0x0518, "DUReset"}, - {0x0519, "NSReset"}, - {0x051a, "SeqReset"}, - {0x051b, "FMReset"}, - {0x051c, "ListReset"}, - {0x051d, "ACEReset"}, - {0x051e, "ResourceReset"}, - {0x0520, "MIDIReset"}, - {0x0521, "VDReset"}, - {0x0522, "TEReset"}, - {0x0523, "MSReset"}, - {0x0525, "AnimReset"}, - {0x0601, "TLStatus"}, - {0x0602, "MMStatus"}, - {0x0603, "MTStatus"}, - {0x0604, "QDStatus"}, - {0x0605, "DeskStatus"}, - {0x0606, "EMStatus"}, - {0x0607, "SchStatus"}, - {0x0608, "SoundStatus"}, - {0x0609, "ADBStatus"}, - {0x060a, "SANEStatus"}, - {0x060b, "IMStatus"}, - {0x060c, "TextStatus"}, - {0x060e, "WindStatus"}, - {0x060f, "MenuStatus"}, - {0x0610, "CtlStatus"}, - {0x0611, "LoaderStatus"}, - {0x0612, "QDAuxStatus"}, - {0x0613, "PMStatus"}, - {0x0614, "LEStatus"}, - {0x0615, "DialogStatus"}, - {0x0616, "ScrapStatus"}, - {0x0617, "SFStatus"}, - {0x0618, "DUStatus"}, - {0x0619, "NSStatus"}, - {0x061a, "SeqStatus"}, - {0x061b, "FMStatus"}, - {0x061c, "ListStatus"}, - {0x061d, "ACEStatus"}, - {0x061e, "ResourceStatus"}, - {0x0620, "MIDIStatus"}, - {0x0621, "VDStatus"}, - {0x0622, "TEStatus"}, - {0x0623, "MSStatus"}, - {0x0625, "AnimStatus"}, - {0x071d, "ACEInfo"}, - {0x0804, "AddPt"}, - {0x0825, "AnimIdleDebug"}, - {0x0901, "GetTSPtr"}, - {0x0902, "NewHandle"}, - {0x0903, "WriteBRam"}, - {0x0904, "GetAddress"}, - {0x0905, "SaveScrn"}, - {0x0906, "DoWindows"}, - {0x0907, "SchAddTask"}, - {0x0908, "WriteRamBlock"}, - {0x0909, "SendInfo"}, - {0x090a, "SANEFP816"}, - {0x090b, "Multiply"}, - {0x090c, "SetInGlobals"}, - {0x090e, "NewWindow"}, - {0x090f, "MenuKey"}, - {0x0910, "NewControl"}, - {0x0911, "InitialLoad"}, - {0x0912, "CopyPixels"}, - {0x0913, "PrDefault"}, - {0x0914, "LENew"}, - {0x0915, "ErrorSound"}, - {0x0916, "UnloadScrap"}, - {0x0917, "SFGetFile"}, - {0x0919, "AllocGen"}, - {0x091a, "SetIncr"}, - {0x091b, "CountFamilies"}, - {0x091c, "CreateList"}, - {0x091d, "ACECompress"}, - {0x091e, "CreateResourceFile"}, - {0x0920, "MIDIControl"}, - {0x0921, "VDInStatus"}, - {0x0922, "TENew"}, - {0x0923, "SetBasicChan"}, - {0x0925, "StartScene"}, - {0x0a01, "SetTSPtr"}, - {0x0a02, "ReAllocHandle"}, - {0x0a03, "ReadBRam"}, - {0x0a04, "GrafOn"}, - {0x0a05, "RestScrn"}, - {0x0a06, "GetNextEvent"}, - {0x0a07, "SchFlush"}, - {0x0a08, "ReadRamBlock"}, - {0x0a09, "ReadKeyMicroData"}, - {0x0a0a, "SANEDecStr816"}, - {0x0a0b, "SDivide"}, - {0x0a0c, "SetOutGlobals"}, - {0x0a0e, "CheckUpdate"}, - {0x0a0f, "GetMenuBar"}, - {0x0a10, "DisposeControl"}, - {0x0a11, "Restart"}, - {0x0a12, "WaitCursor"}, - {0x0a13, "PrValidate"}, - {0x0a14, "LEDispose"}, - {0x0a15, "NewModalDialog"}, - {0x0a16, "LoadScrap"}, - {0x0a17, "SFPutFile"}, - {0x0a19, "DeAlocGen"}, - {0x0a1a, "ClearIncr"}, - {0x0a1b, "FindFamily"}, - {0x0a1c, "SortList"}, - {0x0a1d, "ACEExpand"}, - {0x0a1e, "OpenResourceFile"}, - {0x0a20, "MIDIDevice"}, - {0x0a21, "VDInSetStd"}, - {0x0a22, "TEKill"}, - {0x0a23, "SetMIDIMode"}, - {0x0a25, "StopScene"}, - {0x0b01, "GetFuncPtr"}, - {0x0b02, "RestoreHandle"}, - {0x0b03, "WriteBParam"}, - {0x0b04, "GrafOff"}, - {0x0b05, "SaveAll"}, - {0x0b06, "EventAvail"}, - {0x0b08, "GetTableAddress"}, - {0x0b09, "ReadKeyMicroMemory"}, - {0x0b0a, "SANEElems816"}, - {0x0b0b, "UDivide"}, - {0x0b0c, "SetErrGlobals"}, - {0x0b0e, "CloseWindow"}, - {0x0b0f, "MenuRefresh"}, - {0x0b10, "KillControls"}, - {0x0b11, "LoadSegNum"}, - {0x0b12, "DrawIcon"}, - {0x0b13, "PrStlDialog"}, - {0x0b14, "LESetText"}, - {0x0b15, "NewModelessDialog"}, - {0x0b16, "ZeroScrap"}, - {0x0b17, "SFPGetFile"}, - {0x0b19, "NoteOn"}, - {0x0b1a, "GetTimer"}, - {0x0b1b, "GetFamInfo"}, - {0x0b1c, "NextMember"}, - {0x0b1d, "ACECompBegin"}, - {0x0b1e, "CloseResourceFile"}, - {0x0b20, "MIDIClock"}, - {0x0b21, "VDInGetStd"}, - {0x0b22, "TESetText"}, - {0x0b23, "PlayNote"}, - {0x0b25, "StartFrameTimer"}, - {0x0c01, "GetWAP"}, - {0x0c02, "AddToOOMQueue"}, - {0x0c03, "ReadBParam"}, - {0x0c04, "GetStandardSCB"}, - {0x0c05, "RestAll"}, - {0x0c06, "GetMouse"}, - {0x0c08, "GetSoundVolume"}, - {0x0c09, "Resync"}, - {0x0c0b, "LongMul"}, - {0x0c0c, "GetInGlobals"}, - {0x0c0e, "Destkop"}, - {0x0c0f, "FlashMenuBar"}, - {0x0c10, "SetCtlTitle"}, - {0x0c11, "UnloadSegNum"}, - {0x0c12, "SpecialRect"}, - {0x0c13, "PrJobDialog"}, - {0x0c14, "LEIdle"}, - {0x0c15, "CloseDialog"}, - {0x0c16, "PutScrap"}, - {0x0c17, "SFPPutFile"}, - {0x0c19, "NoteOff"}, - {0x0c1a, "GetLoc"}, - {0x0c1b, "GetFamNum"}, - {0x0c1c, "DrawMember"}, - {0x0c1d, "ACEExpBegin"}, - {0x0c1e, "AddResource"}, - {0x0c20, "MIDIInfo"}, - {0x0c21, "VDInConvAdj"}, - {0x0c22, "TEGetText"}, - {0x0c23, "StopNote"}, - {0x0c25, "StopFrameTimer"}, - {0x0d01, "SetWAP"}, - {0x0d02, "RemoveFromOOMQueue"}, - {0x0d03, "ReadTimeHex"}, - {0x0d04, "InitColorTable"}, - {0x0d06, "Button"}, - {0x0d08, "SetSoundVolume"}, - {0x0d09, "AsyncADBReceive"}, - {0x0d0b, "LongDivide"}, - {0x0d0c, "GetOutGlobals"}, - {0x0d0e, "SetWTitle"}, - {0x0d0f, "InsertMenu"}, - {0x0d10, "GetCtlTitle"}, - {0x0d11, "LoadSegNum"}, - {0x0d12, "SeedFill"}, - {0x0d13, "PrPixelMap"}, - {0x0d14, "LEClick"}, - {0x0d15, "NewDItem"}, - {0x0d16, "GetScrap"}, - {0x0d17, "SFAllCaps"}, - {0x0d19, "AllNotesOff"}, - {0x0d1a, "SeqAllNotesOff"}, - {0x0d1b, "AddFamily"}, - {0x0d1c, "SelectMember"}, - {0x0d1d, "GetACEExpState"}, - {0x0d1e, "UpdateResourceFile"}, - {0x0d20, "MIDIReadPacket"}, - {0x0d21, "VDKeyControl"}, - {0x0d22, "TEGetTextInfo"}, - {0x0d23, "KillAllNotes"}, - {0x0d25, "SetBackgndPort"}, - {0x0e01, "LoadTools"}, - {0x0e03, "WriteTimeHex"}, - {0x0e04, "SetColorTable"}, - {0x0e05, "InstallNDA"}, - {0x0e06, "StillDown"}, - {0x0e08, "FFStartSound"}, - {0x0e09, "SyncADBReceive"}, - {0x0e0b, "FixRatio"}, - {0x0e0c, "GetErrGlobals"}, - {0x0e0e, "GetWTitle"}, - {0x0e0f, "DeleteMenu"}, - {0x0e10, "HideControl"}, - {0x0e11, "UnloadSeg"}, - {0x0e12, "CalcMask"}, - {0x0e13, "PrOpenDoc"}, - {0x0e14, "LESetSelect"}, - {0x0e15, "RemoveDItem"}, - {0x0e16, "GetScrapHandle"}, - {0x0e17, "SFGetFile2"}, - {0x0e19, "NSSetUpdateRate"}, - {0x0e1a, "SetTrkInfo"}, - {0x0e1b, "InstallFont"}, - {0x0e1c, "GetListDefProc"}, - {0x0e1d, "SetACEExpState"}, - {0x0e1e, "LoadResource"}, - {0x0e20, "MIDIWritePacket"}, - {0x0e21, "VDKeyStatus"}, - {0x0e22, "TEIdle"}, - {0x0e23, "SetRecTrack"}, - {0x0e25, "RefreshBack"}, - {0x0f01, "LoadOneTool"}, - {0x0f03, "ReadAsciiTime"}, - {0x0f04, "GetColorTable"}, - {0x0f05, "InstallCDA"}, - {0x0f06, "WaitMouseUp"}, - {0x0f08, "FFStopSound"}, - {0x0f09, "AbsOn"}, - {0x0f0b, "FixMul"}, - {0x0f0c, "SetInputDevice"}, - {0x0f0e, "SetFrameColor"}, - {0x0f0f, "InsertMItem"}, - {0x0f10, "ShowControl"}, - {0x0f11, "GetLoadSegInfo"}, - {0x0f12, "GetSysIcon"}, - {0x0f13, "PrCloseDoc"}, - {0x0f14, "LEActivate"}, - {0x0f15, "ModalDialog"}, - {0x0f16, "GetScrapSize"}, - {0x0f17, "SFPutFile2"}, - {0x0f19, "NSSetUserUpdateRtn"}, - {0x0f1a, "StartSeq"}, - {0x0f1b, "SetPurgeStart"}, - {0x0f1c, "ResetMember"}, - {0x0f1e, "RemoveResource"}, - {0x0f20, "MIDIRecordSeq"}, - {0x0f21, "VDKeySetKCol"}, - {0x0f22, "TEActivate"}, - {0x0f23, "SetPlayTrack"}, - {0x0f25, "StartChar"}, - {0x1001, "UnloadOneTool"}, - {0x1002, "DisposeHandle"}, - {0x1003, "SetVector"}, - {0x1004, "SetColorEntry"}, - {0x1006, "TickCount"}, - {0x1008, "FFSoundStatus"}, - {0x1009, "AbsOff"}, - {0x100b, "FracMul"}, - {0x100c, "SetOutputDevice"}, - {0x100e, "GetFrameColor"}, - {0x100f, "DeleteMItem"}, - {0x1010, "DrawControls"}, - {0x1011, "GetUserID"}, - {0x1012, "PixelMap2Rgn"}, - {0x1013, "PrOpenPage"}, - {0x1014, "LEDeactivate"}, - {0x1015, "IsDialogEvent"}, - {0x1016, "GetScrapPath"}, - {0x1017, "SFPGetFile2"}, - {0x101a, "StepSeq"}, - {0x101b, "CountFonts"}, - {0x101c, "NewList"}, - {0x101e, "MarkResourceChange"}, - {0x1020, "MIDIStopRecord"}, - {0x1021, "VDKeyGetKRCol"}, - {0x1022, "TEDeactivate"}, - {0x1023, "TrackToChan"}, - {0x1025, "MoveChar"}, - {0x1101, "TLMountVolume"}, - {0x1102, "DisposeAll"}, - {0x1103, "GetVector"}, - {0x1104, "GetColorEntry"}, - {0x1105, "ChooseCDA"}, - {0x1106, "GetDBLTime"}, - {0x1108, "FFGeneratorStatus"}, - {0x1109, "ReadAbs"}, - {0x110b, "FixDiv"}, - {0x110c, "SetErrorDevice"}, - {0x110e, "SelectWindow"}, - {0x110f, "GetSysBar"}, - {0x1110, "HiliteControls"}, - {0x1111, "LGetPathname"}, - {0x1113, "PrClosePage"}, - {0x1114, "LEKey"}, - {0x1115, "DialogSelect"}, - {0x1116, "SetScrapPath"}, - {0x1117, "SFPPutFile2"}, - {0x111a, "StopSeq"}, - {0x111b, "FindFontStats"}, - {0x111c, "DrawMember2"}, - {0x111e, "SetCurResourceFile"}, - {0x1120, "MIDIPlaySeq"}, - {0x1121, "VDKeyGetKGCol"}, - {0x1122, "TEClick"}, - {0x1123, "Locate"}, - {0x1125, "GetCharRecPtr"}, - {0x1201, "TLTextMountVolume"}, - {0x1202, "PurgeHandle"}, - {0x1203, "SetHeartBeat"}, - {0x1204, "SetSCB"}, - {0x1206, "GetCaretTime"}, - {0x1208, "SetSoundMIRQV"}, - {0x1209, "SetAbsScale"}, - {0x120b, "FracDiv"}, - {0x120c, "GetInputDevice"}, - {0x120e, "HideWindow"}, - {0x120f, "SetSysBar"}, - {0x1210, "CtlNewRes"}, - {0x1211, "UserShutdown"}, - {0x1213, "PrPicFile"}, - {0x1214, "LECut"}, - {0x1215, "DlgCut"}, - {0x1216, "GetScrapCount"}, - {0x1217, "SFShowInvisible"}, - {0x121a, "SetInstTable"}, - {0x121b, "LoadFont"}, - {0x121c, "NextMember2"}, - {0x121e, "GetCurResourceFile"}, - {0x1220, "MIDIStopPlay"}, - {0x1221, "VDKeyGetKBCol"}, - {0x1222, "TEUpdate"}, - {0x1223, "SetVelComp"}, - {0x1225, "KillChar"}, - {0x1301, "SaveTextState"}, - {0x1302, "PurgeAll"}, - {0x1303, "DelHeartBeat"}, - {0x1304, "GetSCB"}, - {0x1305, "SetDAStrPtr"}, - {0x1306, "SetSwitch"}, - {0x1308, "SetUserSoundIRQV"}, - {0x1309, "GetAbsScale"}, - {0x130b, "FixRound"}, - {0x130c, "GetOutputDevice"}, - {0x130e, "ShowWindow"}, - {0x130f, "FixMenuBar"}, - {0x1310, "FindControl"}, - {0x1311, "RenamePathname"}, - {0x1312, "IBeamCursor"}, - {0x1313, "PrControl"}, - {0x1314, "LECopy"}, - {0x1315, "DlgCopy"}, - {0x1316, "GetScrapState"}, - {0x1317, "SFReScan"}, - {0x131a, "StartInts"}, - {0x131b, "LoadSysFont"}, - {0x131c, "ResetMember2"}, - {0x131e, "SetCurResourceApp"}, - {0x1320, "MIDIConvert"}, - {0x1321, "VDKeySetKDiss"}, - {0x1322, "TEPaintText"}, - {0x1323, "SetMIDIPort"}, - {0x1325, "LoadActor"}, - {0x1401, "RestoreTextState"}, - {0x1403, "ClrHeartBeat"}, - {0x1404, "SetAllSCBs"}, - {0x1405, "GetDAStrPtr"}, - {0x1406, "PostEvent"}, - {0x1408, "FFSoundDoneStatus"}, - {0x1409, "SRQPoll"}, - {0x140b, "FracSqrt"}, - {0x140c, "GetErrorDevice"}, - {0x140e, "SendBehind"}, - {0x140f, "CountMItems"}, - {0x1410, "TestControl"}, - {0x1412, "WhooshRect"}, - {0x1413, "PrError"}, - {0x1414, "LEPaste"}, - {0x1415, "DlgPaste"}, - {0x1417, "SFMultiGet2"}, - {0x141a, "StopInts"}, - {0x141b, "AddFontVar"}, - {0x141c, "SelectMember2"}, - {0x141e, "GetCurResourceApp"}, - {0x1421, "VDKeyGetKDiss"}, - {0x1422, "TEKey"}, - {0x1423, "SetInstrument"}, - {0x1425, "SetCharScript"}, - {0x1501, "MessageCenter"}, - {0x1503, "SysFailMgr"}, - {0x1504, "ClearScreen"}, - {0x1505, "OpenNDA"}, - {0x1506, "FlushEvents"}, - {0x1508, "FFSetUpSound"}, - {0x1509, "SRQRemove"}, - {0x150b, "FracCos"}, - {0x150c, "InitTextDev"}, - {0x150e, "FrontWindow"}, - {0x150f, "NewMenuBar"}, - {0x1510, "TrackControl"}, - {0x1513, "PrSetError"}, - {0x1514, "LEDelete"}, - {0x1515, "DlgDelete"}, - {0x1517, "SFPMultiGet2"}, - {0x151a, "StartSeqRel"}, - {0x151b, "FixFontMenu"}, - {0x151c, "SortList2"}, - {0x151e, "HomeResourceFile"}, - {0x1521, "VDKeySetNKD"}, - {0x1522, "TEUnsupported"}, - {0x1523, "SeqPlayer"}, - {0x1525, "RunAnimScripts"}, - {0x1601, "SetDefaultTPT"}, - {0x1603, "GetAddr"}, - {0x1604, "SetMasterSCB"}, - {0x1605, "CloseNDA"}, - {0x1606, "GetOSEvent"}, - {0x1608, "FFStartPlaying"}, - {0x1609, "ClearSRQTable"}, - {0x160b, "FracSin"}, - {0x160c, "CtlTextDev"}, - {0x160e, "SetInfoDraw"}, - {0x160f, "GetMHandle"}, - {0x1610, "MoveControl"}, - {0x1613, "PrChoosePrinter"}, - {0x1614, "LEInsert"}, - {0x1615, "DrawDialog"}, - {0x161b, "ChooseFont"}, - {0x161c, "NewList2"}, - {0x161e, "WriteResource"}, - {0x1621, "VDKeyGetNKD"}, - {0x1622, "TECut"}, - {0x1623, "SetTempo"}, - {0x1625, "FillAddrTable"}, - {0x1701, "MessageByName"}, - {0x1703, "ReadMouse"}, - {0x1704, "GetMasterSCB"}, - {0x1705, "SystemClick"}, - {0x1706, "OSEventAvail"}, - {0x1708, "SetDOCReg"}, - {0x170b, "FixATan2"}, - {0x170c, "StatusTextDev"}, - {0x170e, "FindWindow"}, - {0x170f, "SetBarColors"}, - {0x1710, "DragControl"}, - {0x1713, "GetDeviceName"}, - {0x1714, "LEUpdate"}, - {0x1715, "Alert"}, - {0x171b, "ItemID2FamNum"}, - {0x171c, "ListKey"}, - {0x171e, "ReleaseResource"}, - {0x1721, "VDOutSetStd"}, - {0x1722, "TECopy"}, - {0x1723, "SetCallBack"}, - {0x1725, "CompileRect"}, - {0x1801, "StartUpTools"}, - {0x1802, "GetHandleSize"}, - {0x1803, "InitMouse"}, - {0x1804, "OpenPort"}, - {0x1805, "SystemEdit"}, - {0x1806, "SetEventMask"}, - {0x1808, "ReadDOCReg"}, - {0x180b, "HiWord"}, - {0x180c, "WriteChar"}, - {0x180e, "TrackGoAway"}, - {0x180f, "GetBarColors"}, - {0x1810, "SetCtlIcons"}, - {0x1813, "PrGetPrinterSpecs"}, - {0x1814, "LETextBox"}, - {0x1815, "StopAlert"}, - {0x181b, "FMSetSysFont"}, - {0x181c, "CompareStrings"}, - {0x181e, "DetachResource"}, - {0x1821, "VDOutGetStd"}, - {0x1822, "TEPaste"}, - {0x1823, "SysExOut"}, - {0x1825, "StartTockTask"}, - {0x1901, "ShutDownTools"}, - {0x1902, "SetHandleSize"}, - {0x1903, "SetMouse"}, - {0x1904, "InitPort"}, - {0x1905, "SystemTask"}, - {0x1906, "FakeMouse"}, - {0x190b, "LoWord"}, - {0x190c, "ErrWriteChar"}, - {0x190e, "MoveWindow"}, - {0x190f, "SetMTitleStart"}, - {0x1910, "SetCtlValue"}, - {0x1913, "PrDevPrChanged"}, - {0x1914, "LEFromScrap"}, - {0x1915, "NoteAlert"}, - {0x191b, "FMGetSysFID"}, - {0x191e, "UniqueResourceID"}, - {0x1921, "VDOutControl"}, - {0x1922, "TEClear"}, - {0x1923, "SetBeat"}, - {0x1925, "FireTockTask"}, - {0x1a01, "GetMsgHandle"}, - {0x1a02, "FindHandle"}, - {0x1a03, "HomeMouse"}, - {0x1a04, "ClosePort"}, - {0x1a05, "SystemEvent"}, - {0x1a06, "SetAutokeyLimit"}, - {0x1a0b, "Long2Fix"}, - {0x1a0c, "WriteLine"}, - {0x1a0e, "DragWindow"}, - {0x1a0f, "GetMTitleStart"}, - {0x1a10, "GetCtlValue"}, - {0x1a13, "PrDevstartup"}, - {0x1a14, "LEToScrap"}, - {0x1a15, "CautionAlert"}, - {0x1a1b, "FMGetCurFID"}, - {0x1a1e, "SetResourceID"}, - {0x1a21, "VDOutStatus"}, - {0x1a22, "TEInsert"}, - {0x1a23, "MIDIMessage"}, - {0x1a25, "SetForegndPort"}, - {0x1b01, "AcceptRequests"}, - {0x1b02, "FreeMem"}, - {0x1b03, "ClearMouse"}, - {0x1b04, "SetPort"}, - {0x1b05, "GetNumNDAs"}, - {0x1b06, "GetKeyTranslation"}, - {0x1b0b, "Fix2Long"}, - {0x1b0c, "ErrWriteLine"}, - {0x1b0e, "GrowWindow"}, - {0x1b0f, "GetMenuMgrPort"}, - {0x1b10, "SetCtlParams"}, - {0x1b13, "PrDevShutdown"}, - {0x1b14, "LEScrapHandle"}, - {0x1b15, "ParamText"}, - {0x1b1b, "FamNum2ItemID"}, - {0x1b1e, "GetResourceAttr"}, - {0x1b21, "VDGetFeatures"}, - {0x1b22, "TEReplace"}, - {0x1b23, "LocateEnd"}, - {0x1b25, "SetAnimWindow"}, - {0x1c01, "SendRequests"}, - {0x1c02, "MaxBlock"}, - {0x1c03, "ClampMouse"}, - {0x1c04, "GetPort"}, - {0x1c05, "CloseNDAbyWinPtr"}, - {0x1c06, "SetKeyTranslation"}, - {0x1c0b, "Fix2Frac"}, - {0x1c0c, "WriteString"}, - {0x1c0e, "SizeWindow"}, - {0x1c0f, "CalcMenuSize"}, - {0x1c10, "GetCtlParams"}, - {0x1c13, "PrDevOpen"}, - {0x1c14, "LEGetScrapLen"}, - {0x1c15, "SetDAFont"}, - {0x1c1b, "InstallWithStats"}, - {0x1c1e, "SetResourceAttr"}, - {0x1c21, "VDInControl"}, - {0x1c22, "TEGetSelection"}, - {0x1c23, "Merge"}, - {0x1d02, "TotalMem"}, - {0x1d03, "GetMouseClamp"}, - {0x1d04, "SetPortLoc"}, - {0x1d05, "CloseAllNDAs"}, - {0x1d0b, "Frac2Fix"}, - {0x1d0c, "ErrWriteString"}, - {0x1d0e, "TaskMaster"}, - {0x1d0f, "SetMTitleWidth"}, - {0x1d10, "DragRect"}, - {0x1d13, "PrDevRead"}, - {0x1d14, "LESetScrapLen"}, - {0x1d1e, "GetResourceSize"}, - {0x1d21, "VDGGControl"}, - {0x1d22, "TESsetSelection"}, - {0x1d23, "DeleteTrack"}, - {0x1e02, "CheckHandle"}, - {0x1e03, "PosMouse"}, - {0x1e04, "GetPortLoc"}, - {0x1e05, "FixAppleMenu"}, - {0x1e0b, "Fix2X"}, - {0x1e0c, "TextWriteBlock"}, - {0x1e0e, "BeginUpdate"}, - {0x1e0f, "GetMTitleWidth"}, - {0x1e10, "GrowSize"}, - {0x1e13, "PrDevWrite"}, - {0x1e14, "LESetHilite"}, - {0x1e15, "GetControlDItem"}, - {0x1e1e, "MatchResourceHandle"}, - {0x1e21, "VDGGStatus"}, - {0x1e22, "TEGetSelectionStyle"}, - {0x1e23, "SetMetro"}, - {0x1f02, "CompactMem"}, - {0x1f03, "ServeMouse"}, - {0x1f04, "SetPortRect"}, - {0x1f05, "AddToRunQ"}, - {0x1f0b, "Frac2X"}, - {0x1f0c, "ErrWriteBlock"}, - {0x1f0e, "EndUpdate"}, - {0x1f0f, "SetMenuFlag"}, - {0x1f10, "GetCtlDPage"}, - {0x1f13, "PrDevClose"}, - {0x1f14, "LESetCaret"}, - {0x1f15, "GetIText"}, - {0x1f1e, "GetOpenFileRefNum"}, - {0x1f22, "TEStyleChange"}, - {0x1f23, "GetMSData"}, - {0x2002, "HLock"}, - {0x2003, "GetNewID"}, - {0x2004, "GetPortRect"}, - {0x2005, "RemoveFromRunQ"}, - {0x200b, "X2FIx"}, - {0x200c, "WriteCString"}, - {0x200e, "GetWMgrPort"}, - {0x200f, "GetMenuFlag"}, - {0x2010, "SetCtlAction"}, - {0x2011, "InitialLoad2"}, - {0x2013, "PrDevStatus"}, - {0x2014, "LETextBox2"}, - {0x2015, "SetIText"}, - {0x201e, "CountTypes"}, - {0x2022, "TEOffsetToPoint"}, - {0x2023, "ConvertToTime"}, - {0x2102, "HLockAll"}, - {0x2103, "DeleteID"}, - {0x2104, "SetPortSize"}, - {0x2105, "RemoveCDA"}, - {0x210b, "X2Frac"}, - {0x210c, "ErrWriteCString"}, - {0x210e, "PinRect"}, - {0x210f, "SetMenuTitle"}, - {0x2110, "GetCtlAction"}, - {0x2111, "GetUserID2"}, - {0x2113, "PrDevAsyncRead"}, - {0x2114, "LESetJust"}, - {0x2115, "SelectIText"}, - {0x211e, "GetIndType"}, - {0x2122, "TEPointToOffset"}, - {0x2123, "ConvertToMeasure"}, - {0x2202, "HUnlock"}, - {0x2203, "StatusID"}, - {0x2204, "MovePortTo"}, - {0x2205, "RemoveNDA"}, - {0x220b, "Int2Hex"}, - {0x220c, "ReadChar"}, - {0x220e, "HiliteWindow"}, - {0x220f, "GetMenuTitle"}, - {0x2210, "SetCtlRefCon"}, - {0x2211, "LGetPathname2"}, - {0x2213, "PrDevWriteBackground"}, - {0x2214, "LEGetTextHand"}, - {0x2215, "HideDItem"}, - {0x221e, "CountResources"}, - {0x2222, "TEGetDefProc"}, - {0x2223, "MSSuspend"}, - {0x2302, "HUnlockAll"}, - {0x2303, "IntSource"}, - {0x2304, "SetOrigin"}, - {0x2305, "GetIndDAInfo"}, - {0x230b, "Long2Hex"}, - {0x230c, "TextReadBlock"}, - {0x230e, "ShowHide"}, - {0x230f, "MenuGlobal"}, - {0x2310, "GetCtlRefCon"}, - {0x2313, "PrDriverVer"}, - {0x2314, "LEGetTextLen"}, - {0x2315, "ShowDItem"}, - {0x231e, "GetIndResource"}, - {0x2322, "TEGetRuler"}, - {0x2323, "MSResume"}, - {0x2402, "SetPurge"}, - {0x2403, "FWEntry"}, - {0x2404, "SetClip"}, - {0x2405, "CallDeskAcc"}, - {0x240b, "Hex2Int"}, - {0x240c, "ReadLine"}, - {0x240e, "BringToFront"}, - {0x240f, "SetMItem"}, - {0x2410, "EraseControl"}, - {0x2413, "PrPortVer"}, - {0x2414, "GetLEDefProc"}, - {0x2415, "FindDItem"}, - {0x241e, "SetResourceLoad"}, - {0x2422, "TESetRuler"}, - {0x2423, "SetTuningTable"}, - {0x2502, "SetPurgeAll"}, - {0x2503, "GetTick"}, - {0x2504, "GetClip"}, - {0x2505, "GetDeskGlobal"}, - {0x250b, "Hex2Long"}, - {0x250e, "WindNewRes"}, - {0x250f, "GetMItem"}, - {0x2510, "DrawOneCtl"}, - {0x2513, "PrGetZoneName"}, - {0x2515, "UpdateDialog"}, - {0x251e, "SetResourceFileDepth"}, - {0x2522, "TEScroll"}, - {0x2523, "GetTuningTable"}, - {0x2603, "PackBytes"}, - {0x2604, "ClipRect"}, - {0x260b, "Int2Dec"}, - {0x260e, "TrackZoom"}, - {0x260f, "SetMItemFlag"}, - {0x2610, "FindTargetCtl"}, - {0x2615, "GetDItemType"}, - {0x261e, "GetMapHandle"}, - {0x2622, "TEGetInternalProc"}, - {0x2623, "SetTrackOut"}, - {0x2703, "UnPackBytes"}, - {0x2704, "HidePen"}, - {0x270b, "Long2Dec"}, - {0x270e, "ZoomWindow"}, - {0x270f, "GetMItemFlag"}, - {0x2710, "MakeNextCtlTarget"}, - {0x2715, "SetDItemType"}, - {0x271e, "LoadAbsResource"}, - {0x2722, "TEGetLastError"}, - {0x2723, "StartMIDIDriver"}, - {0x2802, "PtrToHand"}, - {0x2803, "Munger"}, - {0x2804, "ShowPen"}, - {0x280b, "Dec2Int"}, - {0x280e, "SetWRefCon"}, - {0x280f, "SetMItemBlink"}, - {0x2810, "MakeThisCtlTarget"}, - {0x2813, "PrGetPrinterDrvName"}, - {0x2815, "GetDItemBox"}, - {0x281e, "ResourceConverter"}, - {0x2822, "TECompactRecord"}, - {0x2823, "StopMIDIDriver"}, - {0x2902, "HandToPtr"}, - {0x2903, "GetIRQEnable"}, - {0x2904, "GetPen"}, - {0x290b, "Dec2Long"}, - {0x290e, "GetWRefCon"}, - {0x290f, "MenuNewRes"}, - {0x2910, "SendEventToCtl"}, - {0x2913, "PrGetPortDvrName"}, - {0x2915, "SetDItemBox"}, - {0x2a02, "HandToHand"}, - {0x2a03, "SetAbsClamp"}, - {0x2a04, "SetPenState"}, - {0x2a0b, "HexIt"}, - {0x2a0e, "GetNextWindow"}, - {0x2a0f, "DrawMenuBar"}, - {0x2a10, "GetCtlID"}, - {0x2a13, "PrGetUserName"}, - {0x2a15, "GetFirstDItem"}, - {0x2a1e, "RMFindNamedResource"}, - {0x2b02, "BlockMove"}, - {0x2b03, "GetAbsClamp"}, - {0x2b04, "GetPenState"}, - {0x2b0e, "GetWKind"}, - {0x2b0f, "MenuSelect"}, - {0x2b10, "SetCtlID"}, - {0x2b13, "PrGetNetworkName"}, - {0x2b15, "GetNextDItem"}, - {0x2b1e, "RMGetResourceName"}, - {0x2c03, "SysBeep"}, - {0x2c04, "SetPenSize"}, - {0x2c0e, "GetWFrame"}, - {0x2c0f, "HiliteMenu"}, - {0x2c10, "CallCtlDefProc"}, - {0x2c15, "ModalDialog2"}, - {0x2c1e, "RMLoadNamedResource"}, - {0x2d04, "GetPenSize"}, - {0x2d0e, "SetWFrame"}, - {0x2d0f, "NewMenu"}, - {0x2d10, "NotifyCtls"}, - {0x2d1e, "RMSetResourceName"}, - {0x2e03, "AddToQueue"}, - {0x2e04, "SetPenMode"}, - {0x2e0e, "GetStructRgn"}, - {0x2e0f, "DisposeMenu"}, - {0x2e10, "GetCtlMoreFlags"}, - {0x2e15, "GetDItemValue"}, - {0x2f02, "RealFreeMem"}, - {0x2f03, "DeleteFromQueue"}, - {0x2f04, "GetPenMode"}, - {0x2f0e, "GetContentRgn"}, - {0x2f0f, "InitPalette"}, - {0x2f10, "SetCtlMoreFlags"}, - {0x2f15, "SetDItemValue"}, - {0x3002, "SetHandleID"}, - {0x3004, "SetPenPat"}, - {0x300e, "GetUpdateRgn"}, - {0x300f, "EnableMItem"}, - {0x3010, "GetCtlHandleFromID"}, - {0x3013, "PrDevIsItSafe"}, - {0x3103, "GetInterruptState"}, - {0x3104, "GetPenPat"}, - {0x310e, "GetDefProc"}, - {0x310f, "DisableMItem"}, - {0x3110, "NewControl2"}, - {0x3113, "GetZoneList"}, - {0x3203, "GetIntStateRecSize"}, - {0x3204, "SetPenMask"}, - {0x320e, "SetDefProc"}, - {0x320f, "CheckMItem"}, - {0x3210, "CMLoadResource"}, - {0x3213, "GetMyZone"}, - {0x3215, "GetNewModalDialog"}, - {0x3303, "ReadMouse2"}, - {0x3304, "GetPenMask"}, - {0x330e, "GetWControls"}, - {0x330f, "SetMItemMark"}, - {0x3310, "CMReleaseResource"}, - {0x3313, "GetPrinterList"}, - {0x3315, "GetNewDItem"}, - {0x3403, "GetCodeResConverter"}, - {0x3404, "SetBackPat"}, - {0x340e, "SetOriginMask"}, - {0x340f, "GetMItemMark"}, - {0x3410, "SetCtlParamPtr"}, - {0x3413, "PMUnloadDriver"}, - {0x3415, "GetAlertStage"}, - {0x3503, "GetROMResource"}, - {0x3504, "GetBackPat"}, - {0x350e, "GetInfoRefCon"}, - {0x350f, "SetMItemStyle"}, - {0x3510, "GetCtlParamPtr"}, - {0x3513, "PMLoadDriver"}, - {0x3515, "ResetAlertStage"}, - {0x3603, "ReleaseROMResource"}, - {0x3604, "PenNormal"}, - {0x360e, "SetInfoRefCon"}, - {0x360f, "GetMItemStyle"}, - {0x3613, "PrGetDocName"}, - {0x3615, "DefaultFilter"}, - {0x3703, "ConvSeconds"}, - {0x3704, "SetSolidPenPat"}, - {0x370e, "GetZoomRect"}, - {0x370f, "SetMenuID"}, - {0x3710, "InvalCtls"}, - {0x3713, "PrSetDocName"}, - {0x3715, "GetDefButton"}, - {0x3803, "SysBeep2"}, - {0x3804, "SetSolidBackPat"}, - {0x380e, "SetZoomRect"}, - {0x380f, "SetMItemID"}, - {0x3810, "CtlReserved"}, - {0x3813, "PrGetPgOrientation"}, - {0x3815, "SetDefButton"}, - {0x3903, "VersionString"}, - {0x3904, "SolidPattern"}, - {0x390e, "RefreshDesktop"}, - {0x390f, "SetMenuBar"}, - {0x3910, "FindRadioButton"}, - {0x3915, "DisableDItem"}, - {0x3a03, "WaitUntil"}, - {0x3a04, "MoveTo"}, - {0x3a0e, "InvalRect"}, - {0x3a0f, "SetMItemName"}, - {0x3a10, "SetLETextByID"}, - {0x3a15, "EnableDItem"}, - {0x3b03, "StringToText"}, - {0x3b04, "Move"}, - {0x3b0e, "InvalRgn"}, - {0x3b0f, "GetPopUpDefProc"}, - {0x3b10, "GetLETextByID"}, - {0x3c03, "ShowBootInfo"}, - {0x3c04, "LineTo"}, - {0x3c0e, "ValidRect"}, - {0x3c0f, "PopUpMenuSelect"}, - {0x3d03, "ScanDevices"}, - {0x3d04, "Line"}, - {0x3d0e, "ValidRgn"}, - {0x3d0f, "DrawPopUp"}, - {0x3e04, "SetPicSave"}, - {0x3e0e, "GetContentOrigin"}, - {0x3e0f, "NewMenu2"}, - {0x3f04, "GetPicSave"}, - {0x3f0e, "SetContentOrigin"}, - {0x3f0f, "InsertMItem2"}, - {0x4004, "SetRgnSave"}, - {0x400e, "GetDataSize"}, - {0x400f, "SetMenuTitle2"}, - {0x4104, "GetRgnSave"}, - {0x410e, "SetDataSize"}, - {0x410f, "SetMItem2"}, - {0x4204, "SetPolySave"}, - {0x420e, "GetMaxGrow"}, - {0x420f, "SetMItemName2"}, - {0x4304, "GetPolySave"}, - {0x430e, "SetMaxGrow"}, - {0x430f, "NewMenuBar2"}, - {0x4404, "SetGrafProcs"}, - {0x440e, "GetScroll"}, - {0x4504, "GetGrafProcs"}, - {0x450e, "SetScroll"}, - {0x450f, "HideMenuBar"}, - {0x4604, "SetUserField"}, - {0x460e, "GetPage"}, - {0x460f, "ShowMenuBar"}, - {0x4704, "GetUserField"}, - {0x470e, "SetPage"}, - {0x470f, "SetMItemIcon"}, - {0x4804, "SetSysField"}, - {0x480e, "GetContentDraw"}, - {0x480f, "GetMItemIcon"}, - {0x4904, "GetSysField"}, - {0x490e, "SetContentDraw"}, - {0x490f, "SetMItemStruct"}, - {0x4a04, "SetRect"}, - {0x4a0e, "GetInfoDraw"}, - {0x4a0f, "GetMItemStruct"}, - {0x4b04, "OffsetRect"}, - {0x4b0e, "SetSysWindow"}, - {0x4b0f, "RemoveMItemStruct"}, - {0x4c04, "InsetRect"}, - {0x4c0e, "GetSysWFlag"}, - {0x4c0f, "GetMItemFlag2"}, - {0x4d04, "SectRect"}, - {0x4d0e, "StartDrawing"}, - {0x4d0f, "SetMItemFlag2"}, - {0x4e04, "UnionRect"}, - {0x4e0e, "SetWindowIcons"}, - {0x4e0f, "GetMItemWidth"}, - {0x4f04, "PtInRect"}, - {0x4f0e, "GetRectInfo"}, - {0x4f0f, "GetMItemBlink"}, - {0x5004, "Pt2Rect"}, - {0x500e, "StartInfoDrawing"}, - {0x500f, "InsertPathMItems"}, - {0x5104, "EqualRect"}, - {0x510e, "EndInfoDrawing"}, - {0x5204, "NotEmptyRect"}, - {0x520e, "GetFirstWindow"}, - {0x5304, "FrameRect"}, - {0x530e, "WindDragRect"}, - {0x5404, "PaintRect"}, - {0x540e, "Private01"}, - {0x5504, "EraseRect"}, - {0x550e, "DrawInfoBar"}, - {0x5604, "InvertRect"}, - {0x560e, "WindowGlobal"}, - {0x5704, "FillRect"}, - {0x570e, "SetContentOrigin2"}, - {0x5804, "FrameOval"}, - {0x580e, "GetWindowMgrGlobals"}, - {0x5904, "PaintOval"}, - {0x590e, "AlertWindow"}, - {0x5a04, "EraseOval"}, - {0x5a0e, "StartFrameDrawing"}, - {0x5b04, "InvertOval"}, - {0x5b0e, "EndFrameDrawing"}, - {0x5c04, "FillOVal"}, - {0x5c0e, "ResizeWindow"}, - {0x5d04, "FrameRRect"}, - {0x5d0e, "TaskMasterContent"}, - {0x5e04, "PaintRRect"}, - {0x5e0e, "TaskMasterKey"}, - {0x5f04, "EraseRRect"}, - {0x5f0e, "TaskMasterDA"}, - {0x6004, "InvertRRect"}, - {0x600e, "CompileText"}, - {0x6104, "FillRRect"}, - {0x610e, "NewWindow2"}, - {0x6204, "FrameArc"}, - {0x620e, "ErrorWindow"}, - {0x6304, "PaintArc"}, - {0x630e, "GetAuxWindInfo"}, - {0x6404, "EraseArc"}, - {0x640e, "DoModalWindow"}, - {0x6504, "InvertArc"}, - {0x650e, "MWGetCtlPart"}, - {0x6604, "FillArc"}, - {0x660e, "MWSetMenuProc"}, - {0x6704, "NewRgn"}, - {0x670e, "MWStdDrawProc"}, - {0x6804, "DisposeRgn"}, - {0x680e, "MWSetUpEditMenu"}, - {0x6904, "CopyRgn"}, - {0x690e, "FindCursorCtl"}, - {0x6a04, "SetEmptyRgn"}, - {0x6a0e, "ResizeInfoBar"}, - {0x6b04, "SetRectRgn"}, - {0x6b0e, "HandleDiskInsert"}, - {0x6c04, "RectRgn"}, - {0x6d04, "OpenRgn"}, - {0x6e04, "CloseRgn"}, - {0x6f04, "OffsetRgn"}, - {0x7004, "InsetRgn"}, - {0x7104, "SectRgn"}, - {0x7204, "UnionRgn"}, - {0x7304, "DiffRgn"}, - {0x7404, "XorRgn"}, - {0x7504, "PtInRgn"}, - {0x7604, "RectInRgn"}, - {0x7704, "EqualRgn"}, - {0x7804, "EmptyRgn"}, - {0x7904, "FrameRgn"}, - {0x7a04, "PaintRgn"}, - {0x7b04, "EraseRgn"}, - {0x7c04, "InvertRgn"}, - {0x7d04, "FillRgn"}, - {0x7e04, "ScrollRect"}, - {0x7f04, "PaintPixels"}, - {0x8004, "AddPt"}, - {0x8104, "SubPt"}, - {0x8204, "SetPt"}, - {0x8304, "EqualPt"}, - {0x8404, "LocalToGlobal"}, - {0x8504, "GlobalToLocal"}, - {0x8604, "Random"}, - {0x8704, "SetRandSeed"}, - {0x8804, "GetPixel"}, - {0x8904, "ScalePt"}, - {0x8a04, "MapPt"}, - {0x8b04, "MapRect"}, - {0x8c04, "MapRgn"}, - {0x8d04, "SetStdProcs"}, - {0x8e04, "SetCursor"}, - {0x8f04, "GetCursorAdr"}, - {0x9004, "HideCursor"}, - {0x9104, "ShowCursor"}, - {0x9204, "ObscureCursor"}, - {0x9304, "SetMouseLoc"}, - {0x9404, "SetFont"}, - {0x9504, "GetFont"}, - {0x9604, "GetFontInfo"}, - {0x9704, "GetFontGlobals"}, - {0x9804, "SetFontFlags"}, - {0x9904, "GetFontFlags"}, - {0x9a04, "SetTextFace"}, - {0x9b04, "GetTextFace"}, - {0x9c04, "SetTextMode"}, - {0x9d04, "GetTextMode"}, - {0x9e04, "SetSpaceExtra"}, - {0x9f04, "GetSpaceExtra"}, - {0xa004, "SetForeColor"}, - {0xa104, "GetForeColor"}, - {0xa204, "SetBackColor"}, - {0xa304, "GetBackColor"}, - {0xa404, "DrawChar"}, - {0xa504, "DrawString"}, - {0xa604, "DrawCString"}, - {0xa704, "DrawText"}, - {0xa804, "CharWidth"}, - {0xa904, "StringWidth"}, - {0xaa04, "CStringWidth"}, - {0xab04, "TextWidth"}, - {0xac04, "CharBounds"}, - {0xad04, "StringBounds"}, - {0xae04, "CStringBounds"}, - {0xaf04, "TextBounds"}, - {0xb004, "SetArcRot"}, - {0xb104, "GetArcRot"}, - {0xb204, "SetSysFont"}, - {0xb304, "GetSysFont"}, - {0xb404, "SetVisRgn"}, - {0xb504, "GetVisRgn"}, - {0xb604, "SetIntUse"}, - {0xb704, "OpenPicture"}, - {0xb712, "OpenPicture"}, - {0xb804, "PicComment"}, - {0xb812, "PicComment"}, - {0xb904, "ClosePicture"}, - {0xba04, "DrawPicture"}, - {0xba12, "DrawPicture"}, - {0xbb04, "KillPicture"}, - {0xbb12, "KillPicture"}, - {0xbc04, "FramePoly"}, - {0xbd04, "PaintPoly"}, - {0xbe04, "ErasePoly"}, - {0xbf04, "InvertPoly"}, - {0xc004, "FillPoly"}, - {0xc104, "OpenPoly"}, - {0xc204, "ClosePoly"}, - {0xc304, "KillPoly"}, - {0xc404, "OffsetPoly"}, - {0xc504, "MapPoly"}, - {0xc604, "SetClipHandle"}, - {0xc704, "GetClipHandle"}, - {0xc804, "SetVisHandle"}, - {0xc904, "GetVisHandle"}, - {0xca04, "InitCursor"}, - {0xcb04, "SetBufDims"}, - {0xcc04, "ForceBufDims"}, - {0xcd04, "SaveBufDims"}, - {0xce04, "RestoreBufDims"}, - {0xcf04, "GetFGSize"}, - {0xd004, "SetFontID"}, - {0xd104, "GetFontID"}, - {0xd204, "SetTextSize"}, - {0xd304, "GetTextSize"}, - {0xd404, "SetCharExtra"}, - {0xd504, "GetCharExtra"}, - {0xd604, "PPToPort"}, - {0xd704, "InflateTextBuffer"}, - {0xd804, "GetRomFont"}, - {0xd904, "GetFontLore"}, - {0xda04, "Get640Color"}, - {0xdb04, "Set640Color"} -}; - -#define numTools (sizeof(tools) / sizeof(tools[0])) - -static const char *toolLookup(uint16_t tool) { - for (int i = 0; i < numTools; i++) { - if (tools[i].id >= tool) { - if (tools[i].id == tool) - return tools[i].name; - break; - } - } - return NULL; -}