Initial commit.
This commit is contained in:
commit
cb38eeee09
4 changed files with 1805 additions and 0 deletions
295
Readme.txt
Normal file
295
Readme.txt
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
------------------------------------
|
||||||
|
DisPel v1.0
|
||||||
|
65816/SMC Disassembler
|
||||||
|
by James Churchill of Naruto (C)2001
|
||||||
|
------------------------------------
|
||||||
|
10th of July, 2001.
|
||||||
|
|
||||||
|
pelrun@gmail.com
|
||||||
|
|
||||||
|
The Problem
|
||||||
|
-----------
|
||||||
|
|
||||||
|
[snip]
|
||||||
|
|
||||||
|
Hi, pelrun from 2011 here. I wrote this document so long ago that I don't
|
||||||
|
think it's fair to leave my griping about the tools of the time still in
|
||||||
|
this readme. It just boils down to one statement - what was available at
|
||||||
|
the time had a bunch of quirks that made them difficult to use for SNES
|
||||||
|
disassembly. So I wrote my own.
|
||||||
|
|
||||||
|
The Solution
|
||||||
|
------------
|
||||||
|
|
||||||
|
And so I present DisPel, my own 65816 disassembler. It's not nearly as
|
||||||
|
"full-featured" as Tracer (for instance, it's not really for use with NES
|
||||||
|
roms), but it does everything I need it to. And since it only took a day to
|
||||||
|
write (most of which was working on the commandline parser, not the
|
||||||
|
disassembler itself!) I wonder why I didn't do it ages ago.
|
||||||
|
|
||||||
|
The v0.9 is only because I haven't done exhaustive testing to ensure the
|
||||||
|
disassembly is correct. But every piece of code I have disassembled with it
|
||||||
|
matched exactly with code I've disassembled in the past (which was corrected
|
||||||
|
by hand, of course!)
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
Able to disassemble a section of a rom, or a whole bank. Or all of it :)
|
||||||
|
Correct REP/SEP instruction handling.
|
||||||
|
Correct bank-boundary handling.
|
||||||
|
Automatic (but overridable) HiROM and LoROM support.
|
||||||
|
Shadow ROM support.
|
||||||
|
User-specifiable listing origin (see below.)
|
||||||
|
True SNES addressing - no need to worry about LoROM-offset conversion or
|
||||||
|
headers.
|
||||||
|
Control-C *will* stop it!
|
||||||
|
|
||||||
|
|
||||||
|
True SNES addressing
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Now you no longer need to worry about converting to-from LoROM addresses to
|
||||||
|
disassemble the code you want - just enter the SNES bank/addresses you want,
|
||||||
|
and DisPel does the conversion automatically. The SMC header is also
|
||||||
|
automatically ignored, unless disabled with the "-n" option.
|
||||||
|
|
||||||
|
And now that you can select any section of the rom to disassemble, you don't
|
||||||
|
have to worry about a 20MB+ file containing "disassembled" graphics data,
|
||||||
|
which is just a waste of space.
|
||||||
|
|
||||||
|
Addresses/banks are specified using plain hex - no $ or 0x prefixes added.
|
||||||
|
|
||||||
|
HiROM addressing is switched on using the "-h" option. Use inSNESt or a
|
||||||
|
similar utility to determine whether it's needed.
|
||||||
|
|
||||||
|
v0.91 update: I've replaced the half-assed HiROM support with a newer half-assed
|
||||||
|
version. Specifying HiROM addresses (bank $40-$6F and $C0-$EF) will switch HiROM on
|
||||||
|
automatically, and HiROM listings use the correct banks now (instead of bank $00+).
|
||||||
|
|
||||||
|
v0.95 update: HiROM is now detected automatically. If DisPel gets it wrong, you can use
|
||||||
|
the -h/-l options to force HiROM/LoROM modeS respectively.
|
||||||
|
|
||||||
|
|
||||||
|
You can disassemble a single bank using the "-b" option.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
dispel -b 1D rom.smc
|
||||||
|
Will disassemble from $1D8000 to $1DFFFF.
|
||||||
|
|
||||||
|
dispel -b 1D -h rom.smc
|
||||||
|
Will disassemble from $1D0000 to $1DFFFF.
|
||||||
|
|
||||||
|
Address ranges can be specified using the "-r" option. If the end address is
|
||||||
|
omitted, the disassembly will proceed from the supplied address to the end
|
||||||
|
of the file.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
dispel -r 58600-79700 rom.smc
|
||||||
|
Will disassemble the range $58600 to $79700... only the valid LoROM banks,
|
||||||
|
of course :)
|
||||||
|
|
||||||
|
dispel -r 58600 rom.smc
|
||||||
|
Will disassemble from $58600 onwards.
|
||||||
|
|
||||||
|
|
||||||
|
Correct REP/SEP state parsing
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
As a byproduct of the need to maintain backward compatibility with the 6502,
|
||||||
|
certain opcodes take either a 1-byte or a 2-byte operand. Which is used is
|
||||||
|
dictated by processor state flags, and so is software selectable. This is a
|
||||||
|
problem with disassembly because normally the disassembler does not know
|
||||||
|
which version of the instruction to use. This leads to the instruction
|
||||||
|
alignment being lost and gibberish instructions being output.
|
||||||
|
|
||||||
|
Tracer tried to address the problem by maintaining similar state flags, and
|
||||||
|
updating them when an instruction that would normally alter them is
|
||||||
|
disassembled. Unfortunately, it didn't do it correctly for all the
|
||||||
|
instructions that are affected. DisPel however should produce a correct
|
||||||
|
listing for a subroutine when the state is set properly at the start.
|
||||||
|
|
||||||
|
Since most 65816 code I've seen explicitly sets the size flags at the
|
||||||
|
beginning of each subroutine, you needn't worry about it a lot of the time.
|
||||||
|
However, if you are disassembling a small block where the flags are set
|
||||||
|
differently at the beginning, you can affect the initial state of the flags
|
||||||
|
using the "-a" and "-x" options.
|
||||||
|
|
||||||
|
"-a" forces 8-bit accumulator mode.
|
||||||
|
"-x" forces 8-bit X/Y register mode.
|
||||||
|
|
||||||
|
DisPel defaults to 16-bit accumulator and X/Y register mode.
|
||||||
|
|
||||||
|
Of course, this only applies at the beginning of the listing. Subsequent REP
|
||||||
|
and SEP commands will alter the states appropriately.
|
||||||
|
|
||||||
|
|
||||||
|
Bank-boundary handling
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
SNES code doesn't run across bank boundaries. It might in a HiROM (though
|
||||||
|
I doubt it), but never ever in a LoROM image. So the disassembler shouldn't
|
||||||
|
place instructions that straddle the bank boundary. If that happens, then the
|
||||||
|
instruction alignment is definitely compromised, as one or more bytes are
|
||||||
|
"eaten" at the start of the next bank that shouldn't be. This results in an
|
||||||
|
incorrect listing at the start of the bank, which continues until the
|
||||||
|
disassembler (through pure luck) re-establishes the alignment.
|
||||||
|
|
||||||
|
This was of great concern in Tracer, which had woeful support for
|
||||||
|
disassembling sections of a ROM. If it didn't get it right (and you were
|
||||||
|
bound to have some banks that started like this) then replacing them with
|
||||||
|
a correct listing was a real pain.
|
||||||
|
|
||||||
|
So in DisPel I enforced the bank boundaries. If an disassembled instruction
|
||||||
|
would go over a boundary then it is ignored. The surplus bytes up to the
|
||||||
|
boundary are displayed without an accompanying instruction, and disassembly
|
||||||
|
continues at the boundary onwards.
|
||||||
|
|
||||||
|
Just in case I'm completely wrong about the boundary-crossing on HiROMs, you
|
||||||
|
can disable the enforcement using the -d option. But I suggest leaving it on
|
||||||
|
to start with.
|
||||||
|
|
||||||
|
Shadow ROM support
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Shadow ROM is a feature of the SNES hardware - the entire rom image is
|
||||||
|
mirrored at bank 80 onwards. When game code executes in those banks the
|
||||||
|
hardware runs at a higher clock-rate than when it's running at the lower
|
||||||
|
copy. (I think it's called FastROM and SlowROM in other documentation - for
|
||||||
|
obvious reasons. Note I'm not talking about Fast/SlowROM *protection* here.)
|
||||||
|
|
||||||
|
65816 code is largely relocatable. However, long absolute addressing modes
|
||||||
|
mean that most code *must* be run where it was assembled to run - it's
|
||||||
|
origin. Therefore FastROM code won't work properly if attempted to be
|
||||||
|
executed in the SlowROM banks and vice versa. Unfortunately, when you
|
||||||
|
disassemble FastROM game code with the other tools, you get a listing based
|
||||||
|
in the SlowROM banks, which gives you a very inconsistent view of the code -
|
||||||
|
relative addresses point at expected places, but long jumps and the like all
|
||||||
|
go somewhere completely unexpected!
|
||||||
|
|
||||||
|
If you know what's going on, there's no problem. But it's still a hassle to
|
||||||
|
have to worry about it. Therefore DisPel has the ability to produce a listing
|
||||||
|
using the FastROM addresses. You'll know if you need it if you see JMP's
|
||||||
|
going to addresses above $800000.
|
||||||
|
|
||||||
|
For HiROMs, the SlowROM code begins at bank $40, and the FastROM copy at $C0.
|
||||||
|
|
||||||
|
To turn it on, either specify the address range/bank in the $80xxxx+ area
|
||||||
|
directly, or use the -s option to convert the specified addresses to FastROM
|
||||||
|
ones.
|
||||||
|
|
||||||
|
v0.95 update: FastROM is detected automatically. You can force enable/disable it
|
||||||
|
by using the -s and -i options.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
dispel -bank 02 -s rom.smc
|
||||||
|
Will disassemble bank 02 as bank 82.
|
||||||
|
|
||||||
|
dispel -bank 82 rom.smc
|
||||||
|
Identical to above.
|
||||||
|
|
||||||
|
dispel -r 20000-2FFFF -s rom.smc
|
||||||
|
Will disassemble the region $20000-$2FFFF as $820000-$82FFFF.
|
||||||
|
|
||||||
|
dispel -r 820000-82FFFF rom.smc
|
||||||
|
Same as above.
|
||||||
|
|
||||||
|
|
||||||
|
User-specified origin support
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
This is what I first meant to use to implement Shadow ROM support. I did it
|
||||||
|
better above, but I decided to leave this in in case someone might find it
|
||||||
|
useful.
|
||||||
|
|
||||||
|
Not all SNES code is run where it is in the rom. Some of it is copied
|
||||||
|
elsewhere then executed at the new location (the sound code does this, but
|
||||||
|
that's a different discussion.) Such won't work at it's original location -
|
||||||
|
the code is assembled to run somewhere else, and all absolute addresses will
|
||||||
|
point to incorrect places. If you ever encounter something like this, the
|
||||||
|
"-g" option will force DisPel to assume the code is assembled somewhere other
|
||||||
|
than it's actual location. Relatively addressed operands will be altered to
|
||||||
|
fit.
|
||||||
|
|
||||||
|
You shouldn't need this. If you do, you'll probably understand what this
|
||||||
|
does anyway. Otherwise, don't worry about it.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
dispel -b 0 -g 30000 rom.smc
|
||||||
|
|
||||||
|
Disassemble LoROM bank 0 ($8000-$FFFF) as if it was really at $30000-$37FFF.
|
||||||
|
|
||||||
|
|
||||||
|
Miscellaneous
|
||||||
|
-------------
|
||||||
|
|
||||||
|
You can output only the instructions (no address/hexdump fields) using the -t option.
|
||||||
|
|
||||||
|
You can also place blank lines after RTS,RTI,RTL instructions using the -p option.
|
||||||
|
This will help show where the subroutines start/end in the code.
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
dispel [-n] [-h] [-s] [-a] [-x] [-d]
|
||||||
|
[-b <bank>|-r <startaddr>-<endaddr>] [-g <origin>]
|
||||||
|
[-o <outfile>] <infile>
|
||||||
|
|
||||||
|
Options: (numbers are hex-only, no prefixes)
|
||||||
|
-n Don't skip SMC header
|
||||||
|
-h HiROM memory mapping. Default is LoROM.
|
||||||
|
-s Use shadow ROM addresses (see above.)
|
||||||
|
-a Start in 8-bit accumulator mode. Default is 16-bit.
|
||||||
|
-x Start in 8-bit X/Y mode. Default is 16-bit.
|
||||||
|
-d Turn off bank-boundary enforcement (see above.)
|
||||||
|
-b <bank> Disassemble bank <bank> only. Overrides -r.
|
||||||
|
-r <start>-<end> Disassemble block from <start> to <end>.
|
||||||
|
Omit -<end> to disassemble to end of file.
|
||||||
|
-g <origin> Set origin of disassembled code (see above.)
|
||||||
|
-o <outfile> Set file to redirect output to. Default is to the screen.
|
||||||
|
<infile> File to disassemble.
|
||||||
|
|
||||||
|
|
||||||
|
Release history
|
||||||
|
---------------
|
||||||
|
|
||||||
|
v1.0001 - 5/4/2011
|
||||||
|
No code changes; put the source into roughly the code style I use nowadays
|
||||||
|
before making the source public, and changed from a VC5 project to GCC/Make.
|
||||||
|
|
||||||
|
v1.00 - 10/7/2002
|
||||||
|
Fixed a stupid bug that made DisPel crash if the input file didn't exist.
|
||||||
|
Lowercased the mnemonics.
|
||||||
|
Changed the parameter format for BRK and COP
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
v0.99 - 10/7/2001
|
||||||
|
Fixed a couple of errors that prevented the 8-bit register options from working,
|
||||||
|
as well as the shadow/hirom support.
|
||||||
|
Also fixed a small error in this document; -b overrides -r, not -a.
|
||||||
|
|
||||||
|
v0.96 - 3/2/2001
|
||||||
|
Turns out I was erroneously outputting ",X" for the
|
||||||
|
"Direct Page Indirect Indexed, Y" and
|
||||||
|
"Direct Page Indirect Long Indexed, Y" opcodes.
|
||||||
|
That's 16 opcodes with incorrect output - Ouch.
|
||||||
|
Thanks to Skeud for pointing it out.
|
||||||
|
|
||||||
|
v0.95 - 19/11/2000
|
||||||
|
Autodetects HiROM/LoROM and FastROM/SlowROM modes.
|
||||||
|
Added option to put blank lines after RTS,RTL,RTI instructions
|
||||||
|
Added option to output raw assembly (minus address/hex data fields)
|
||||||
|
|
||||||
|
v0.91 - 25/09/2000
|
||||||
|
Fixed HiROM addressing to start from bank $40 instead of $00.
|
||||||
|
Serves me right for not looking up the HiROM docs...
|
||||||
|
|
||||||
|
v0.9 - 24/09/2000
|
||||||
|
Initial Release
|
7
dispel.h
Normal file
7
dispel.h
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/* dispel.h
|
||||||
|
* Header file for DisPel
|
||||||
|
* James Churchill
|
||||||
|
* Created 240900
|
||||||
|
*/
|
||||||
|
|
||||||
|
int disasm(unsigned char *mem, unsigned long pos, unsigned char *flag, char *inst, unsigned char tsrc);
|
492
main.c
Normal file
492
main.c
Normal file
|
@ -0,0 +1,492 @@
|
||||||
|
/* main.c
|
||||||
|
* DisPel 65816 Disassembler
|
||||||
|
* James Churchill
|
||||||
|
* Created 20000924
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <io.h>
|
||||||
|
|
||||||
|
#include "dispel.h"
|
||||||
|
|
||||||
|
void usage(void)
|
||||||
|
{
|
||||||
|
printf("\nDisPel v0.99 by James Churchill of Naruto (C)2001\n"
|
||||||
|
"65816/SMC Disassembler\n"
|
||||||
|
"Usage: dispel [-n] [-t] [-h] [-l] [-s] [-i] [-a] [-x] [-e] [-p]\n"
|
||||||
|
" [-b <bank>|-r <startaddr>-<endaddr>] [-g <origin>]\n"
|
||||||
|
" [-d <width>] [-o <outfile>] <infile>\n\n"
|
||||||
|
"Options: (numbers are hex-only, no prefixes)\n"
|
||||||
|
" -n Don't skip SMC header\n"
|
||||||
|
" -t Don't output addresses/hex dump.\n"
|
||||||
|
" -h/-l Force HiROM/LoROM memory mapping.\n"
|
||||||
|
" -s/-i Force enable/disable shadow ROM addresses (see readme.)\n"
|
||||||
|
" -a Start in 8-bit accumulator mode. Default is 16-bit.\n"
|
||||||
|
" -x Start in 8-bit X/Y mode. Default is 16-bit.\n"
|
||||||
|
" -e Turn off bank-boundary enforcement. (see readme.)\n"
|
||||||
|
" -p Split subroutines by placing blank lines after RTS,RTL,RTI\n"
|
||||||
|
" -b <bank> Disassemble bank <bank> only. Overrides -r.\n"
|
||||||
|
" -r <start>-<end> Disassemble block from <start> to <end>.\n"
|
||||||
|
" Omit -<end> to disassemble to end of file.\n"
|
||||||
|
" -g <origin> Set origin of disassembled code (see readme.)\n"
|
||||||
|
" -d <width> No disassembly - produce a hexdump with <width> bytes/line.\n"
|
||||||
|
" -o <outfile> Set file to redirect output to. Default is stdout.\n"
|
||||||
|
" <infile> File to disassemble.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Snes9x Hi/LoROM autodetect code */
|
||||||
|
|
||||||
|
int AllASCII(unsigned char *b, int size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
if (b[i] < 32 || b[i] > 126)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScoreHiROM(unsigned char *data)
|
||||||
|
{
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
if ((data[0xFFDC] + data[0xFFDD]*256 + data[0xFFDE] + data[0xFFDF]*256) == 0xFFFF)
|
||||||
|
{
|
||||||
|
score += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data[0xFFDA] == 0x33)
|
||||||
|
{
|
||||||
|
score += 2;
|
||||||
|
}
|
||||||
|
if ((data[0xFFD5] & 0xf) < 4)
|
||||||
|
{
|
||||||
|
score += 2;
|
||||||
|
}
|
||||||
|
if (!(data[0xFFFD] & 0x80))
|
||||||
|
{
|
||||||
|
score -= 4;
|
||||||
|
}
|
||||||
|
if ((1 << (data[0xFFD7] - 7)) > 48)
|
||||||
|
{
|
||||||
|
score -= 1;
|
||||||
|
}
|
||||||
|
if (!AllASCII(&data[0xFFB0], 6))
|
||||||
|
{
|
||||||
|
score -= 1;
|
||||||
|
}
|
||||||
|
if (!AllASCII(&data[0xFFC0], 20))
|
||||||
|
{
|
||||||
|
score -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (score);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScoreLoROM(unsigned char *data)
|
||||||
|
{
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
if ((data[0x7FDC] + data[0x7FDD]*256 + data[0x7FDE] + data[0x7FDF]*256) == 0xFFFF)
|
||||||
|
{
|
||||||
|
score += 2;
|
||||||
|
}
|
||||||
|
if (data[0x7FDA] == 0x33)
|
||||||
|
{
|
||||||
|
score += 2;
|
||||||
|
}
|
||||||
|
if ((data[0x7FD5] & 0xf) < 4)
|
||||||
|
{
|
||||||
|
score += 2;
|
||||||
|
}
|
||||||
|
if (!(data[0x7FFD] & 0x80))
|
||||||
|
{
|
||||||
|
score -= 4;
|
||||||
|
}
|
||||||
|
if ((1 << (data[0x7FD7] - 7)) > 48)
|
||||||
|
{
|
||||||
|
score -= 1;
|
||||||
|
}
|
||||||
|
if (!AllASCII(&data[0x7FB0], 6))
|
||||||
|
{
|
||||||
|
score -= 1;
|
||||||
|
}
|
||||||
|
if (!AllASCII(&data[0x7FC0], 20))
|
||||||
|
{
|
||||||
|
score -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (score);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hexdump(unsigned char *data,unsigned long pos,unsigned long rpos,
|
||||||
|
unsigned long len,char *inst, unsigned char dwidth)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
sprintf(inst, "%02lX/%04lX:\t", (pos >> 16) & 0xFF, pos & 0xFFFF);
|
||||||
|
for(i=0; i<dwidth && i+rpos<len; i++)
|
||||||
|
{
|
||||||
|
sprintf(inst + i*2 + 9, "%02X", data[rpos+i]);
|
||||||
|
}
|
||||||
|
return dwidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
FILE *fin,*fout;
|
||||||
|
char infile[BUFSIZ],outfile[BUFSIZ],inst[521];
|
||||||
|
unsigned char dmem[4],flag=0,*data;
|
||||||
|
unsigned long len,pos=0,origin=0x1000000,start=0,end=0,rpos;
|
||||||
|
unsigned char opt,skip=1,hirom=2,shadow=2,bound=1,tsrc=0;
|
||||||
|
unsigned int offset,bank=0x100,i,tmp,dwidth=0;
|
||||||
|
int hiscore,loscore;
|
||||||
|
|
||||||
|
outfile[0]=0;
|
||||||
|
|
||||||
|
// Parse the commandline
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=1; i<(argc-1); i++)
|
||||||
|
{
|
||||||
|
if (sscanf(argv[i], "-%c", &opt) == 0)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(opt)
|
||||||
|
{
|
||||||
|
case 'n':
|
||||||
|
skip = 0;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
tsrc |= 1;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
hirom = 1;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
hirom = 0;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
shadow = 1;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
shadow = 0;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
flag |= 0x20;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
flag |= 0x10;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
bound = 0;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
tsrc |= 2;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
i++;
|
||||||
|
if ((sscanf(argv[i], "%2X", &dwidth) == 0) || dwidth==0)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
printf("\n-d requires a hex value between 01 and FF after it.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
i++;
|
||||||
|
if (sscanf(argv[i], "%2X", &bank) == 0)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
printf("\n-b requires a 1-byte hex value after it.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
i++;
|
||||||
|
if (sscanf(argv[i], "%6lX-%6lX", &start, &end) == 0)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
printf("\n-a requires at least one hex value after it.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
i++;
|
||||||
|
if (sscanf(argv[i], "%6lX", &origin) == 0)
|
||||||
|
{
|
||||||
|
usage();
|
||||||
|
printf("\n-r requires one hex value after it.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
i++;
|
||||||
|
strcpy(outfile, argv[i]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
printf("\nUnknown option: -%c\n", opt);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the input filename and open it
|
||||||
|
strcpy(infile, argv[i]);
|
||||||
|
fin = fopen(infile, "rb");
|
||||||
|
if (!fin)
|
||||||
|
{
|
||||||
|
printf("Cannot open %s for reading.\n", infile);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the output
|
||||||
|
if (outfile[0] == 0)
|
||||||
|
{
|
||||||
|
strcpy(outfile,"STDOUT");
|
||||||
|
fout = stdout;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fout = fopen(outfile, "w");
|
||||||
|
if (!fout)
|
||||||
|
{
|
||||||
|
printf("Cannot open %s for writing.\n",outfile);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the file into memory
|
||||||
|
len = filelength(fileno(fin));
|
||||||
|
|
||||||
|
// Make sure the image is big enough
|
||||||
|
|
||||||
|
if (len < 0x10000 || (skip == 1 && len < 0x10200))
|
||||||
|
{
|
||||||
|
printf("This file looks too small to be a rom image.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate mem for file. Extra 3 bytes to prevent segfault during memcpy
|
||||||
|
if ((data = malloc(len+3)) == NULL)
|
||||||
|
{
|
||||||
|
printf("Cant alloc %ld bytes.\n", len+3);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (skip)
|
||||||
|
{
|
||||||
|
len -= 0x200;
|
||||||
|
fread(data, 0x200, 1, fin);
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(data, len, 1, fin);
|
||||||
|
fclose(fin);
|
||||||
|
|
||||||
|
// Autodetect the HiROM/LoROM state
|
||||||
|
|
||||||
|
if (hirom==2)
|
||||||
|
{
|
||||||
|
hiscore = ScoreHiROM(data);
|
||||||
|
loscore = ScoreLoROM(data);
|
||||||
|
if (hiscore>loscore)
|
||||||
|
{
|
||||||
|
// fprintf(stderr,"Autodetected HiROM image.\n");
|
||||||
|
hirom = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// fprintf(stderr,"Autodetected LoROM image.\n");
|
||||||
|
hirom = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmangle the address options
|
||||||
|
|
||||||
|
pos = start;
|
||||||
|
|
||||||
|
// If shadow addresses given, convert to unshadowed and set shadow on.
|
||||||
|
if ((bank == 0x100 && start & 0x800000) | (bank & 0x80))
|
||||||
|
{
|
||||||
|
shadow = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If HiROM addresses given, convert to normal and set hirom on.
|
||||||
|
if ((bank == 0x100 && start & 0x400000) | (bank & 0x40))
|
||||||
|
{
|
||||||
|
hirom=1;
|
||||||
|
}
|
||||||
|
bank &= 0x13F;
|
||||||
|
start &= 0x3FFFFF;
|
||||||
|
end &= 0x3FFFFF;
|
||||||
|
|
||||||
|
// Autodetect shadow
|
||||||
|
|
||||||
|
if(shadow == 2)
|
||||||
|
{
|
||||||
|
// fprintf(stderr,"%02X\n",data[hirom?0xFFD5:0x7FD5]);
|
||||||
|
if(data[hirom?0xFFD5:0x7FD5] & 0x30)
|
||||||
|
{
|
||||||
|
shadow=1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shadow=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the bank byte is set, apply it to the address range
|
||||||
|
if (bank < 0x100)
|
||||||
|
{
|
||||||
|
if(hirom)
|
||||||
|
{
|
||||||
|
pos = bank << 16;
|
||||||
|
start = pos;
|
||||||
|
end = start | 0xFFFF;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos = (bank << 16) + 0x8000;
|
||||||
|
start = bank * 0x8000;
|
||||||
|
end = start + 0x7FFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!hirom)
|
||||||
|
{
|
||||||
|
// Convert the addresses to offsets
|
||||||
|
if ((start & 0xFFFF) < 0x8000)
|
||||||
|
{
|
||||||
|
start += 0x8000;
|
||||||
|
}
|
||||||
|
pos = start;
|
||||||
|
start = ((start >> 16) & 0xFF) * 0x8000 + (start & 0x7FFF);
|
||||||
|
end = ((end >> 16) & 0xFF) * 0x8000 + (end & 0x7FFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If end isn't after start, set end to end-of-file.
|
||||||
|
if(end <= start)
|
||||||
|
{
|
||||||
|
end = len-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If new origin set, apply it.
|
||||||
|
if (origin<0x1000000)
|
||||||
|
{
|
||||||
|
pos=origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If shadow set, apply it
|
||||||
|
if (shadow)
|
||||||
|
{
|
||||||
|
pos |= 0x800000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If hirom, apply the mapping
|
||||||
|
if (hirom)
|
||||||
|
{
|
||||||
|
pos |= 0x400000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
fprintf(stderr,"Start: $%06X End: $%06X Pos: $%06X\n", start, end, pos);
|
||||||
|
fprintf(stderr,"Input: %s\nOutput: %s\n", infile, outfile);
|
||||||
|
if(shadow)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"Autodetected FastROM.\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr,"Autodetected SlowROM.\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Begin disassembly
|
||||||
|
|
||||||
|
rpos = start;
|
||||||
|
|
||||||
|
while (rpos < len && rpos <= end)
|
||||||
|
{
|
||||||
|
// copy some data to the staging area
|
||||||
|
memcpy(dmem, data+rpos, 4);
|
||||||
|
|
||||||
|
// disassemble one instruction, or produce one line of hexdump
|
||||||
|
if (dwidth == 0)
|
||||||
|
{
|
||||||
|
offset = disasm(dmem, pos, &flag, inst, tsrc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset = hexdump(data, pos, rpos, len, inst, dwidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a file/block overrun
|
||||||
|
if ((rpos + offset) > len || (rpos + offset) > (end+1))
|
||||||
|
{
|
||||||
|
// print out remaining bytes and finish
|
||||||
|
fprintf(fout,"%02lX/%04lX:\t", (pos >> 16) & 0xFF, pos & 0xFFFF);
|
||||||
|
for (i=rpos; i<len && i<=end; i++)
|
||||||
|
{
|
||||||
|
fprintf(fout,"%02X", data[rpos]);
|
||||||
|
}
|
||||||
|
fprintf(fout,"\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a bank overrun
|
||||||
|
if (bound && ((pos & 0xFFFF) + offset) > 0x10000)
|
||||||
|
{
|
||||||
|
// print out remaining bytes
|
||||||
|
fprintf(fout, "%02lX/%04lX:\t",(pos >> 16) & 0xFF, pos & 0xFFFF);
|
||||||
|
tmp = 0x10000 - (pos & 0xFFFF);
|
||||||
|
for (i=0; i<tmp; i++)
|
||||||
|
{
|
||||||
|
fprintf(fout, "%02X", data[rpos+i]);
|
||||||
|
}
|
||||||
|
fprintf(fout, "\n");
|
||||||
|
// Move to next bank
|
||||||
|
if(!hirom)
|
||||||
|
{
|
||||||
|
pos = (pos & 0xFF0000) + 0x18000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos += tmp;
|
||||||
|
}
|
||||||
|
rpos += tmp;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fout, "%s\n", inst);
|
||||||
|
|
||||||
|
// Move to next instruction
|
||||||
|
if (!hirom && ((pos & 0xFFFF) + offset) > 0xFFFF)
|
||||||
|
{
|
||||||
|
pos = (pos & 0xFF0000) + 0x18000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos += offset;
|
||||||
|
}
|
||||||
|
rpos+=offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue