initial import

This commit is contained in:
Sean 2018-08-19 17:24:54 -07:00
commit 2154a1a7e3
58 changed files with 10167 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.o
*.swp
*.map
seg*
2mg
omf
regs

312
2mg.c Normal file
View File

@ -0,0 +1,312 @@
/**
* @copyright 2018 Sean Kasun
* Extracts 2mg and other prodos disk images into a directory structure
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <argp.h>
#include <stdbool.h>
#include "handle.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 *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);
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 <dir>\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 doEntry(uint8_t *entry, uint8_t *disk, uint32_t disklen,
int depth) {
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 {
indent(depth);
printf("%s %d bytes\n", filename, eof);
}
break;
case 0x50:
doGSOS(key, filename, 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 *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);
}

374
65816.h Normal file
View File

@ -0,0 +1,374 @@
#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", IMP, 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
};

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
Copyright (c) 2018, Sean Kasun
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.

19
Makefile Normal file
View File

@ -0,0 +1,19 @@
CC=clang
CFLAGS=-Wall
all: 2mg omf regs
2mg: 2mg.o
$(CC) $(CFLAGS) -o $@ $^
omf: omf.o parser.o
$(CC) $(CFLAGS) -o $@ $^
regs: regs.o map.o scan.o parser.o disasm.o
$(CC) $(CFLAGS) -o $@ $^
%.o: %.c
$(CC) -c $(CFLAGS) -o $@ $<
clean:
rm -f *.o 2mg omg regs

175
README.md Executable file
View File

@ -0,0 +1,175 @@
# 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`.
## 2mg
`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.
## omf
`omf` is a rather complicated tool which is designed to extract relocatable segments from OMF files. Apple IIgs executables (.sys16 files) and system tools (ex. SYSTEM/TOOLS/TOOL025) are in OMF format.
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:
`segment:memory location`
The `segment`is the segment number from the OMF file, and `memory location` is where in memory to relocate that segment.
`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.
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).
`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.
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.
At this point, you can use the resulting segment files and map files as input to the `regs` tool.
## regs
`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.
#### 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:
```
gMAP "seg1"
sORG $30000
$30000:
$30053:mx
$31066: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.
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 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.
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.
You can also use bank-separators if you wish. `$3/1066` is the same as `$31066`.
## Pascal folder
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.
# 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
I'll be using the S16 from Dream Zone as an example.
Generate a basic map of your S16:
`$ omf dream.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:
`$ omf --map=dream.s16.map dream.s16 --prefix=dream`
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.
### 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.
```
$ omf TOOL025
$ omf --map=TOOL025.map 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:
```
gMAP "seg1"
sORG $20000
```
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.
### 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.
First thing I do, is actually hand make a `rom.map` file for the ROM.
```
gMAP "APPLE2GS.ROM"
sORG $fe0000
$fe/0000:
```
and disassemble it.
`$ regs rom.map > rom.s`
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.
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.
`$8 * 4 + $fe012f = $fe014f`
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.
`$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.

412
addresses.h Normal file
View File

@ -0,0 +1,412 @@
#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"},
{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) {
for (int i = 0; i < numAddresses; i++) {
if (addresses[i].address >= addr) {
if (addresses[i].address == addr)
return addresses[i].comment;
break;
}
}
if (addr & ~0xffff)
return addressLookup(addr & 0xffff); // try pageless
return NULL;
}

358
disasm.c Normal file
View File

@ -0,0 +1,358 @@
/**
* @copyright 2018 Sean Kasun
* The main disassembler.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#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++;