mirror of
https://github.com/fadden/ciderpress.git
synced 2024-12-29 04:29:18 +00:00
453 lines
12 KiB
C++
453 lines
12 KiB
C++
/*
|
|
* CiderPress
|
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
|
* See the file LICENSE for distribution terms.
|
|
*/
|
|
/*
|
|
* 6502/65C02/65802/65816 disassembly.
|
|
*
|
|
* Lots of credit to the classic "Programming the 65816" book by David Eyes
|
|
* and Ron Lichty.
|
|
*/
|
|
#ifndef __LR_DISASM__
|
|
#define __LR_DISASM__
|
|
|
|
#include "ReformatBase.h"
|
|
|
|
/*
|
|
* Disassembly base class. Common code and data for all formats.
|
|
*/
|
|
class ReformatDisasm65xxx : public ReformatText {
|
|
public:
|
|
ReformatDisasm65xxx(void) {
|
|
fOneByteBrkCop = false;
|
|
ValidateOpMap();
|
|
ValidateOpCodeDetails();
|
|
}
|
|
virtual ~ReformatDisasm65xxx(void) {}
|
|
|
|
/*
|
|
* OpCode enumeration, must fit in 0-255.
|
|
*/
|
|
typedef enum OpCode {
|
|
kOpCodeUnknown = 0,
|
|
|
|
kOpADC, kOpAND, kOpASL, kOpBCC,
|
|
kOpBCS, kOpBEQ, kOpBIT, kOpBMI,
|
|
kOpBNE, kOpBPL, kOpBRA, kOpBRK,
|
|
kOpBRL, kOpBVC, kOpBVS, kOpCLC,
|
|
kOpCLD, kOpCLI, kOpCLV, kOpCMP,
|
|
kOpCOP, kOpCPX, kOpCPY, kOpDEC,
|
|
kOpDEX, kOpDEY, kOpEOR, kOpINC,
|
|
kOpINX, kOpINY, kOpJML, kOpJMP,
|
|
kOpJSL, kOpJSR, kOpLDA, kOpLDX,
|
|
kOpLDY, kOpLSR, kOpMVN, kOpMVP,
|
|
kOpNOP, kOpORA, kOpPEA, kOpPEI,
|
|
kOpPER, kOpPHA, kOpPHB, kOpPHD,
|
|
kOpPHK, kOpPHP, kOpPHX, kOpPHY,
|
|
kOpPLA, kOpPLB, kOpPLD, kOpPLP,
|
|
kOpPLX, kOpPLY, kOpREP, kOpROL,
|
|
kOpROR, kOpRTI, kOpRTL, kOpRTS,
|
|
kOpSBC, kOpSEC, kOpSED, kOpSEI,
|
|
kOpSEP, kOpSTA, kOpSTP, kOpSTX,
|
|
kOpSTY, kOpSTZ, kOpTAX, kOpTAY,
|
|
kOpTCD, kOpTCS, kOpTDC, kOpTRB,
|
|
kOpTSB, kOpTSC, kOpTSX, kOpTXA,
|
|
kOpTXS, kOpTXY, kOpTYA, kOpTYX,
|
|
kOpWAI, kOpWDM, kOpXBA, kOpXCE,
|
|
|
|
kOpCodeMAX
|
|
} OpCode;
|
|
|
|
/*
|
|
* Address mode enumeration, must fit in 0-255.
|
|
*/
|
|
typedef enum AddrMode {
|
|
kAddrModeUnknown = 0,
|
|
|
|
kAddrAbs,
|
|
kAddrAbsIndexX,
|
|
kAddrAbsIndexY,
|
|
kAddrAbsIndexXInd,
|
|
kAddrAbsInd,
|
|
kAddrAbsIndLong,
|
|
kAddrAbsLong,
|
|
kAddrAbsIndexXLong,
|
|
kAddrAcc,
|
|
kAddrBlockMove,
|
|
kAddrDP,
|
|
kAddrDPIndexX,
|
|
kAddrDPIndexY,
|
|
kAddrDPIndexXInd,
|
|
kAddrDPInd,
|
|
kAddrDPIndLong,
|
|
kAddrDPIndIndexY,
|
|
kAddrDPIndIndexYLong,
|
|
kAddrImm,
|
|
kAddrImplied,
|
|
kAddrPCRel,
|
|
kAddrPCRelLong,
|
|
kAddrStackAbs,
|
|
kAddrStackDPInd,
|
|
kAddrStackInt,
|
|
kAddrStackPCRel,
|
|
kAddrStackPull,
|
|
kAddrStackPush,
|
|
kAddrStackRTI,
|
|
kAddrStackRTL,
|
|
kAddrStackRTS,
|
|
kAddrStackRel,
|
|
kAddrStackRelIndexY,
|
|
kAddrWDM,
|
|
|
|
kAddrModeMAX
|
|
} AddrMode;
|
|
|
|
/*
|
|
* Specify the CPU to emulate when disassembling.
|
|
*/
|
|
typedef enum CPU {
|
|
kCPU6502,
|
|
kCPU65C02,
|
|
kCPU65802,
|
|
kCPU65816,
|
|
|
|
kCPUCount
|
|
} CPU;
|
|
|
|
protected:
|
|
/*
|
|
* Table mapping instruction byte values to an opcode and address mode.
|
|
*
|
|
* There are 256 entries, each of which is a combination of OpCode and
|
|
* AddrMode.
|
|
*/
|
|
typedef struct OpMap {
|
|
int opAndAddr[kCPUCount];
|
|
} OpMap;
|
|
|
|
/*
|
|
* We have a table of these, with one entry in the table for each
|
|
* entry in the OpCode enum. We use a 1:1 mapping, so it's vital that
|
|
* the entries line up.
|
|
*/
|
|
typedef struct OpCodeDetails {
|
|
OpCode opCode; // sanity check
|
|
char mnemonic[4]; // 3-letter mnemonic
|
|
} OpCodeDetails;
|
|
|
|
bool fOneByteBrkCop;
|
|
|
|
/*
|
|
* Output one or more lines of code in a manner appropriate for a
|
|
* monitor listing on an 8-bit machine. Returns the #of bytes consumed.
|
|
*/
|
|
int OutputMonitor8(const unsigned char* srcBuf, long srcLen,
|
|
long backState, unsigned short addr);
|
|
int OutputMonitor16(const unsigned char* srcBuf, long srcLen,
|
|
long backState, unsigned long addr, bool shortRegs);
|
|
|
|
private:
|
|
bool ValidateOpMap(void);
|
|
static const OpMap kOpMap[];
|
|
|
|
bool ValidateOpCodeDetails(void);
|
|
static const OpCodeDetails kOpCodeDetails[];
|
|
|
|
inline unsigned short RelOffset(unsigned short addr, unsigned char off) {
|
|
char shift = (char) off;
|
|
return addr +2 + shift;
|
|
}
|
|
inline unsigned short RelLongOffset(unsigned short addr, unsigned short off) {
|
|
short shift = (short) off;
|
|
return addr +3 + shift;
|
|
}
|
|
|
|
enum {
|
|
kMaxByteConsumption = 4, // max #of bytes for one output
|
|
};
|
|
|
|
int GetOpWidth(OpCode opCode, AddrMode addrMode, CPU cpu,
|
|
bool emul, bool shortM, bool shortX);
|
|
bool IsP8Call(const unsigned char* srcBuf, long srcLen);
|
|
bool IsToolboxCall(const unsigned char* srcBuf, long srcLen,
|
|
long backState);
|
|
bool IsInlineGSOS(const unsigned char* srcBuf, long srcLen);
|
|
bool IsStackGSOS(const unsigned char* srcBuf, long srcLen,
|
|
long backState);
|
|
|
|
void PrintMonitor8Line(OpCode opCode, AddrMode addrMode,
|
|
unsigned short addr, const unsigned char* srcBuf, long srcLen,
|
|
const char* comment);
|
|
void PrintMonitor16Line(OpCode opCode, AddrMode addrMode,
|
|
unsigned long addr, const unsigned char* srcBuf, long srcLen,
|
|
const char* comment);
|
|
|
|
/* 24-bit address helpers */
|
|
inline unsigned char Bank(unsigned long addr) {
|
|
return (unsigned char) ((addr >> 16) & 0xff);
|
|
}
|
|
inline unsigned short Offset(unsigned long addr) {
|
|
return (unsigned short) (addr & 0xffff);
|
|
}
|
|
};
|
|
|
|
/*
|
|
* 8-bit code disassembly.
|
|
*
|
|
* Used for DOS 'B' files and ProDOS BIN/SYS.
|
|
*/
|
|
class ReformatDisasm8 : public ReformatDisasm65xxx {
|
|
public:
|
|
ReformatDisasm8(void) {}
|
|
virtual ~ReformatDisasm8(void) {}
|
|
|
|
virtual void Examine(ReformatHolder* pHolder);
|
|
virtual int Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput);
|
|
};
|
|
|
|
/*
|
|
* This holds an OMF segment header, and abstracts away version
|
|
* differences.
|
|
*/
|
|
class OMFSegmentHeader {
|
|
public:
|
|
OMFSegmentHeader(void) : fReady(false) {}
|
|
virtual ~OMFSegmentHeader(void) {}
|
|
|
|
bool Unpack(const unsigned char* srcBuf, long srcLen, int fileType);
|
|
void Dump(void) const;
|
|
|
|
typedef enum SegmentType {
|
|
kTypeCode = 0x00,
|
|
kTypeData = 0x01,
|
|
kTypeJumpTable = 0x02,
|
|
kTypePathName = 0x04,
|
|
kTypeLibraryDict = 0x08,
|
|
kTypeInit = 0x10,
|
|
kTypeAbsoluteBank = 0x11,
|
|
kTypeDPStack = 0x12,
|
|
} SegmentType;
|
|
typedef enum SegmentFlag {
|
|
kFlagBankRelative,
|
|
kFlagSkip,
|
|
kFlagReload,
|
|
kFlagAbsoluteBank,
|
|
kFlagNoSpecialMem,
|
|
kFlagPositionIndep,
|
|
kFlagPrivate,
|
|
kFlagDynamic,
|
|
} SegmentFlag;
|
|
|
|
int GetVersion(void) const { return fVersion; }
|
|
unsigned long GetSegmentLen(void) const { return fByteCnt; }
|
|
SegmentType GetSegmentType(void) const;
|
|
bool GetSegmentFlag(SegmentFlag flag) const;
|
|
int GetSegNum(void) const { return fSegNum; }
|
|
int GetLabLen(void) const { return fLabLen; }
|
|
int GetNumLen(void) const { return fNumLen; }
|
|
int GetDispData(void) const { return fDispData; }
|
|
const char* GetLoadName(void) const { return (const char*) fLoadName; }
|
|
const char* GetSegName(void) const { return (const char*) fSegName; }
|
|
|
|
// some of these must be public in VC++6.0
|
|
enum {
|
|
kMaxVersion = 2, // v0/1/2 == OMF 1.0 / 2.0 / 2.1
|
|
kLoadNameLen = 10,
|
|
kV0HdrMinSize = 0x25,
|
|
kV1HdrMinSize = 0x2c + kLoadNameLen,
|
|
kV2HdrMinSize = 0x30 + kLoadNameLen,
|
|
kHdrMinSize = kV0HdrMinSize, // smallest of the three
|
|
kExpectedNumLen = 4,
|
|
kExpectedBankSize = 65536,
|
|
|
|
kSegNameLen = 255,
|
|
};
|
|
|
|
private:
|
|
bool fReady;
|
|
|
|
inline unsigned short Get16LE(const unsigned char* buf) {
|
|
return *buf | *(buf+1) << 8;
|
|
}
|
|
inline unsigned long Get32LE(const unsigned char* buf) {
|
|
return *buf | *(buf+1) << 8 | *(buf+2) << 16 | *(buf+3) << 24;
|
|
}
|
|
|
|
unsigned long fBlockCnt; // v0/v1
|
|
unsigned long fByteCnt; // v2
|
|
unsigned long fResSpc;
|
|
unsigned long fLength;
|
|
unsigned char fType; // v0/v1
|
|
unsigned char fLabLen;
|
|
unsigned char fNumLen; // (always 4)
|
|
unsigned char fVersion; // (0, 1, or 2)
|
|
unsigned long fBankSize; // (always 65536)
|
|
unsigned short fKind; // v2
|
|
unsigned long fOrg;
|
|
unsigned long fAlign;
|
|
unsigned char fNumSex;
|
|
unsigned char fLCBank; // v1
|
|
unsigned short fSegNum; // v1/v2
|
|
unsigned long fEntry; // v1/v2
|
|
unsigned short fDispName; // v1/v2
|
|
unsigned short fDispData; // v1/v2
|
|
unsigned long fTempOrg; // v2
|
|
unsigned char fLoadName[kLoadNameLen+1]; // 10 chars, space-padded
|
|
unsigned char fSegName[kSegNameLen+1]; // 1-255 chars
|
|
};
|
|
|
|
#if 0
|
|
/*
|
|
* Constants and functions for manipulating an OMF segment.
|
|
*/
|
|
class OMFSegment {
|
|
public:
|
|
OMFSegment(void) : fSegBuf(nil), fSegLen(-1), fCurPtr(nil),
|
|
fNumLen(-1), fLabLen(-1)
|
|
{}
|
|
virtual ~OMFSegment(void) {}
|
|
|
|
void Setup(const OMFSegmentHeader* pSegHdr, const unsigned char* srcBuf,
|
|
long srcLen);
|
|
const unsigned char* ProcessNextChunk(void);
|
|
|
|
/*
|
|
* Segment op codes.
|
|
*/
|
|
typedef enum SegmentOp {
|
|
kSegOpEND = 0x00, // all
|
|
kSegOpCONSTStart = 0x01, // object
|
|
kSegOpCONSTEnd = 0xdf, // object
|
|
kSegOpALIGN = 0xe0, // object
|
|
kSegOpORG = 0xe1, // org
|
|
kSegOpRELOC = 0xe2, // load
|
|
kSegOpINTERSEG = 0xe3, // load
|
|
kSegOpUSING = 0xe4, // object
|
|
kSegOpSTRONG = 0xe5, // object
|
|
kSegOpGLOBAL = 0xe6, // object
|
|
kSegOpGEQU = 0xe7, // object
|
|
kSegOpMEM = 0xe8, // object
|
|
// 0xe9, 0xea unused
|
|
kSegOpEXPR = 0xeb, // object
|
|
kSegOpZEXPR = 0xec, // object
|
|
kSegOpBEXPR = 0xed, // object
|
|
kSegOpRELEXPR = 0xee, // object
|
|
kSegOpLOCAL = 0xef, // object
|
|
kSegOpEQU = 0xf0, // object
|
|
kSegOpDS = 0xf1, // all
|
|
kSegOpLCONST = 0xf2, // all
|
|
kSegOpLEXPR = 0xf3, // object
|
|
kSegOpENTRY = 0xf4, // RTL
|
|
kSegOpcRELOC = 0xf5, // load
|
|
kSegOpcINTERSEG = 0xf6, // load
|
|
kSegOpSUPER = 0xf7, // load
|
|
|
|
kSegOpGeneral = 0xfb, // reserved
|
|
kSegOpExperimental1 = 0xfc, // reserved
|
|
kSegOpExperimental2 = 0xfd, // reserved
|
|
kSegOpExperimental3 = 0xfe, // reserved
|
|
kSegOpExperimental4 = 0xff, // reserved
|
|
} SegmentOp;
|
|
|
|
/*
|
|
* Expression operands.
|
|
*/
|
|
typedef enum ExprOp {
|
|
kExprOpEnd = 0x00,
|
|
kExprOpAddition = 0x01,
|
|
kExprOpSubtraction = 0x02,
|
|
kExprOpMultiplication = 0x03,
|
|
kExprOpDivision = 0x04,
|
|
kExprOpIntegerRemainder = 0x05,
|
|
kExprOpUnaryNegation = 0x06,
|
|
kExprOpBitShift = 0x07,
|
|
kExprOpAND = 0x08,
|
|
kExprOpOR = 0x09,
|
|
kExprOpEOR = 0x0a,
|
|
kExprOpNOT = 0x0b,
|
|
kExprOpLessThenEqualTo = 0x0c,
|
|
kExprOpGreaterThanEqualTo = 0x0d,
|
|
kExprOpNotEqual = 0x0e,
|
|
kExprOpLessThan = 0x0f,
|
|
kExprOpGreaterThan = 0x10,
|
|
kExprOpEqualTo = 0x11,
|
|
kExprOpBitAND = 0x12,
|
|
kExprOpBitOR = 0x13,
|
|
kExprOpBitEOR = 0x14,
|
|
kExprOpBitNOT = 0x15,
|
|
|
|
kExprOpPushLocation = 0x80,
|
|
kExprOpPushConstant = 0x81,
|
|
kExprOpPushLabelWeak = 0x82,
|
|
kExprOpPushLabelValue = 0x83,
|
|
kExprOpPushLabelLength = 0x84,
|
|
kExprOpPushLabelType = 0x85,
|
|
kExprOpPushLabelCount = 0x86,
|
|
kExprOpPushRelOffset = 0x87,
|
|
} ExprOp;
|
|
|
|
private:
|
|
// inline unsigned short Get16LE(const unsigned char* buf) {
|
|
// return *buf | *(buf+1) << 8;
|
|
// }
|
|
inline unsigned long Get32LE(const unsigned char* buf) {
|
|
return *buf | *(buf+1) << 8 | *(buf+2) << 16 | *(buf+3) << 24;
|
|
}
|
|
|
|
/*
|
|
* Given a pointer to the start of a label, return the label's len.
|
|
* The length returned includes the length byte (if present).
|
|
*/
|
|
int LabelLength(const unsigned char* ptr) {
|
|
if (fLabLen != 0)
|
|
return fLabLen;
|
|
else
|
|
return (*ptr) +1;
|
|
}
|
|
|
|
/* determine the length of an expression */
|
|
int ExpressionLength(const unsigned char* ptr);
|
|
|
|
const unsigned char* fSegBuf;
|
|
long fSegLen;
|
|
|
|
const unsigned char* fCurPtr;
|
|
int fNumLen;
|
|
int fLabLen;
|
|
};
|
|
#endif
|
|
|
|
|
|
/*
|
|
* 16-bit code disassembly.
|
|
*
|
|
* Used for GS/OS S16, EXE, PIF, and others.
|
|
*
|
|
* OMF information comes from Appendix F of the Apple IIgs GS/OS Reference
|
|
* manual and Appendix B of the Orca/M assembler manual.
|
|
*/
|
|
class ReformatDisasm16 : public ReformatDisasm65xxx {
|
|
public:
|
|
ReformatDisasm16(void) {}
|
|
virtual ~ReformatDisasm16(void) {}
|
|
|
|
virtual void Examine(ReformatHolder* pHolder);
|
|
virtual int Process(const ReformatHolder* pHolder,
|
|
ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
|
|
ReformatOutput* pOutput);
|
|
|
|
private:
|
|
void OutputSection(const unsigned char* srcBuf, long srcLen,
|
|
unsigned long addr, bool shortRegs);
|
|
bool OutputOMF(const unsigned char* srcBuf, long srcLen,
|
|
long fileType, bool shortRegs);
|
|
void PrintHeader(const OMFSegmentHeader* pSegHdr,
|
|
int segmentNumber, bool longFmt);
|
|
void PrintSegment(const OMFSegmentHeader* pSegHdr,
|
|
const unsigned char* srcBuf, long srcLen, bool shortRegs);
|
|
};
|
|
|
|
#endif /*__LR_DISASM__*/ |