1
0
mirror of https://github.com/fachat/xa65.git synced 2024-06-08 07:29:39 +00:00
xa65/xa/misc/file65.c
2023-12-07 23:34:19 +01:00

424 lines
11 KiB
C

/* file65 -- A part of xa65 - 65xx/65816 cross-assembler and utility suite
* Print information about o65 files
*
* Copyright (C) 1989-1997 Andre Fachat (afachat@gmx.de)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include "version.h"
#define BUF (9*4+8)
#define programname "file65"
#define progversion "v0.2.1"
#define author "Written by Andre Fachat"
#define copyright "Copyright (C) 1997-2002 Andre Fachat."
/* o65 file format mode bits */
#define FM_OBJ 0x1000
#define FM_SIZE 0x2000
#define FM_RELOC 0x4000
#define FM_CPU 0x8000
#define FM_CPU2 0x00f0
#define FM_CPU2_6502 0x0000
#define FM_CPU2_65C02 0x0010
#define FM_CPU2_65SC02 0x0020
#define FM_CPU2_65CE02 0x0030
#define FM_CPU2_NMOS 0x0040
#define FM_CPU2_65816E 0x0050
const char *cpunames[16] = { "6502", "65C02", "65SC02", "65CE02", "NMOS6502",
"65816",
NULL, NULL, "6809", NULL, // 1000 -
"Z80", NULL, NULL, // 1010 -
"8086", // 1101 -
"80286", // 1110 -
NULL };
int read_options(FILE *fp);
int print_labels(FILE *fp, int offset);
unsigned char hdr[BUF];
unsigned char cmp[] = { 1, 0, 'o', '6', '5' };
int xapar = 0;
int rompar = 0;
int romoff = 0;
int labels = 0;
int verbose = 0;
void usage(FILE *fp) {
fprintf(fp, "Usage: %s [options] [file]\n"
"Print file information about o65 files\n"
"\n",
programname);
fprintf(fp,
" -P print the segment end addresses according to `xa' command line\n"
" parameters `-b?'\n"
" -a offset print `xa' ``romable'' parameter for another file behind this one\n"
" in the same ROM. Add offset to start address.\n"
" -A offset same as `-a', but only print the start address of the next\n"
" file in the ROM\n"
" -v print undefined and global labels\n"
" -vv print undefined and global labels, and relocation tables\n"
" --version output version information and exit\n"
" --help display this help and exit\n");
}
int main(int argc, char *argv[]) {
int i, j, n, mode, hlen;
FILE *fp;
char *aligntxt[4] = { "[align 1]", "[align 2]", "[align 4]", "[align 256]" };
if (argc <= 1) {
usage(stderr);
exit(1);
}
i = 1;
if (strstr(argv[i], "--help") || strstr(argv[i], "-?")
|| strstr(argv[i], "-h")) {
usage(stdout);
exit(0);
}
if (strstr(argv[i], "--version")) {
version(programname, progversion, author, copyright);
exit(0);
}
while (i < argc) {
if (argv[i][0] == '-') {
/* process options */
switch (argv[i][1]) {
case 'v':
j = 1;
while (argv[i][j] == 'v') {
verbose++;
j++;
}
break;
case 'a':
case 'A':
rompar = 1;
if (argv[i][1] == 'A')
rompar++;
if (argv[i][2])
romoff = atoi(argv[i] + 2);
else if (i + 1 < argc)
romoff = atoi(argv[++i]);
else
fprintf(stderr, "%s: missing offset\n", programname);
break;
case 'P':
xapar = 1;
break;
default:
fprintf(stderr, "%s: %s unknown option, use '-h' for help\n",
programname, argv[i]);
break;
}
} else {
fp = fopen(argv[i], "rb");
if (fp) {
n = fread(hdr, 1, 8, fp);
if ((n >= 8) && (!memcmp(hdr, cmp, 5))) {
mode = hdr[7] * 256 + hdr[6];
if (!xapar && !rompar) {
printf("%s: o65 version %d %s file\n", argv[i], hdr[5],
hdr[7] & 0x10 ? "object" : "executable");
printf(" mode: %04x =", mode);
printf(
"[%s][%sbit][%s relocation][CPU %s][CPU2 %s]%s\n",
(mode & 0x1000) ? "object" : "executable",
(mode & 0x2000) ? "32" : "16",
(mode & 0x4000) ? "page" : "byte",
(mode & 0x8000) ? "65816" : "6502",
cpunames[(mode & FM_CPU2) >> 4],
aligntxt[mode & 3]);
}
if (mode & 0x2000) {
fprintf(stderr,
"file65: %s: 32 bit size not supported\n",
argv[i]);
} else {
n = fread(hdr + 8, 1, 18, fp);
if (n < 18) {
fprintf(stderr, "file65: %s: truncated file\n",
argv[i]);
} else {
if (!xapar && !rompar) {
printf(
" text segment @ $%04x - $%04x [$%04x bytes]\n",
hdr[9] * 256 + hdr[8],
hdr[9] * 256 + hdr[8] + hdr[11] * 256
+ hdr[10],
hdr[11] * 256 + hdr[10]);
printf(
" data segment @ $%04x - $%04x [$%04x bytes]\n",
hdr[13] * 256 + hdr[12],
hdr[13] * 256 + hdr[12] + hdr[15] * 256
+ hdr[14],
hdr[15] * 256 + hdr[14]);
printf(
" bss segment @ $%04x - $%04x [$%04x bytes]\n",
hdr[17] * 256 + hdr[16],
hdr[17] * 256 + hdr[16] + hdr[19] * 256
+ hdr[18],
hdr[19] * 256 + hdr[18]);
printf(
" zero segment @ $%04x - $%04x [$%04x bytes]\n",
hdr[21] * 256 + hdr[20],
hdr[21] * 256 + hdr[20] + hdr[23] * 256
+ hdr[22],
hdr[23] * 256 + hdr[22]);
printf(" stack size $%04x bytes %s\n",
hdr[25] * 256 + hdr[24],
(hdr[25] * 256 + hdr[24]) == 0 ?
"(i.e. unknown)" : "");
if (verbose) {
read_options(fp);
print_labels(fp,
hdr[11] * 256 + hdr[10]
+ hdr[15] * 256 + hdr[14]);
}
} else {
struct stat fbuf;
hlen = 8 + 18 + read_options(fp);
stat(argv[i], &fbuf);
if (xapar) {
if (!rompar)
printf("-bt %d ",
(hdr[9] * 256 + hdr[8])
+ (hdr[11] * 256
+ hdr[10]));
printf("-bd %d -bb %d -bz %d ",
(hdr[13] * 256 + hdr[12])
+ (hdr[15] * 256 + hdr[14]),
(hdr[17] * 256 + hdr[16])
+ (hdr[19] * 256 + hdr[18]),
(hdr[21] * 256 + hdr[20])
+ (hdr[23] * 256 + hdr[22]));
}
if (rompar == 1) {
printf("-A %lu ",
(unsigned long) ((hdr[9] * 256
+ hdr[8]) - hlen + romoff
+ (fbuf.st_size)));
} else if (rompar == 2) {
printf("%lu ",
(unsigned long) ((hdr[9] * 256
+ hdr[8]) - hlen + romoff
+ (fbuf.st_size)));
}
printf("\n");
}
}
}
} else {
fprintf(stderr, "file65: %s: not an o65 file!\n", argv[i]);
if (hdr[0] == 1 && hdr[1] == 8 && hdr[3] == 8) {
printf(
"%s: C64 BASIC executable (start address $0801)?\n",
argv[i]);
} else if (hdr[0] == 1 && hdr[1] == 4 && hdr[3] == 4) {
printf(
"%s: CBM PET BASIC executable (start address $0401)?\n",
argv[i]);
}
}
} else {
fprintf(stderr, "file65: %s: %s\n", argv[i], strerror(errno));
}
}
i++;
}
return 0;
}
static struct {
int opt;
int strfl;
char *string;
} otab[] = { { 0, 1, "Filename" }, { 1, 0, "O/S Type" }, { 2, 1, "Assembler" },
{ 3, 1, "Author" }, { 4, 1, "Creation Date" }, { -1, -1, NULL } };
static char *stab[] = { "undefined", "absolute", "text", "data", "bss", "zero",
"-", "-" };
void print_option(unsigned char *buf, int len) {
int i, strfl = 0;
for (i = 0; otab[i].opt >= 0; i++)
if (*buf == otab[i].opt)
break;
if (otab[i].opt >= 0) {
printf("fopt: %-17s: ", otab[i].string);
strfl = otab[i].strfl;
} else {
printf("fopt: Unknown Type $%02x : ", (*buf & 0xff));
}
if (strfl) {
buf[len] = 0;
printf("%s\n", buf + 1);
} else {
for (i = 1; i < len - 1; i++) {
printf("%02x ", buf[i] & 0xff);
}
printf("\n");
}
}
int read_options(FILE *fp) {
int c, d, l = 0;
unsigned char tb[256];
c = fgetc(fp);
l++;
while (c && c != EOF) {
c &= 255;
d = fread(tb, 1, c - 1, fp);
if (labels)
print_option(tb, c);
l += c;
c = fgetc(fp);
}
return l;
}
int print_labels(FILE *fp, int offset) {
int i, nud, c, seg, off;
const char *segments[] = { "undef", "abs", "text", "data", "bss", "zero" };
const char *reltype[] =
{ "-", "LOW", "HIGH", "-", "WORD", "SEG", "SEGADDR" };
/*
printf("print_labels:offset=%d\n",offset);
*/
fseek(fp, offset, SEEK_CUR);
// -----------------------------------------------------------
// print undefined labels
nud = (fgetc(fp) & 0xff);
nud += ((fgetc(fp) << 8) & 0xff00);
printf("Undefined Labels: %d\n", nud);
if (nud) {
do {
c = fgetc(fp);
while (c && c != EOF) {
fputc(c, stdout);
c = fgetc(fp);
}
printf("\t");
} while (--nud);
printf("\n");
}
// ---------------------------------------------------------
// skip relocation tables
// two tables, one for text one for data
for (i = 0; i < 2; i++) {
unsigned char lowbyte;
unsigned short index;
unsigned short offset = 0;
if (verbose > 1) {
printf("Relocation table for %s:\n", i ? "text" : "data");
}
c = fgetc(fp);
while (c && c != EOF) {
c &= 0xff;
while (c == 255 && c != EOF) {
offset += 254;
c = fgetc(fp);
if (c == EOF)
break;
c &= 0xff;
}
if (c == EOF)
break;
offset += c;
c = fgetc(fp);
if ((c & 0xe0) == 0x40) {
lowbyte = fgetc(fp);
}
if ((c & 0x07) == 0) {
index = fgetc(fp) & 0xff;
index += (fgetc(fp) & 0xff) << 8;
}
if (verbose > 1) {
printf("\t%d:%s(%s (%d)", offset, reltype[(c >> 5) & 0xf],
segments[c & 0x07], (c & 0x07));
if ((c & 0xe0) == 0x40) {
printf(", %02x", lowbyte);
}
if ((c & 0x07) == 0) {
printf(", %04x", index);
}
printf(")");
}
c = fgetc(fp);
}
if (verbose > 1) {
printf("\n");
}
}
// ---------------------------------------------------------
// print global labels
nud = (fgetc(fp) & 0xff);
nud += ((fgetc(fp) << 8) & 0xff00);
printf("Global Labels: %d\n", nud);
if (nud) {
do {
c = fgetc(fp);
while (c && c != EOF) {
fputc(c, stdout);
c = fgetc(fp);
}
if (c == EOF)
break;
seg = fgetc(fp) & 0xff;
off = (fgetc(fp) & 0xff);
off += ((fgetc(fp) << 8) & 0xff00);
printf(" (segID=%d (%s), offset=%04x)\n", seg, stab[seg & 7], off);
} while (--nud);
}
return 0;
}