1
0
mirror of https://github.com/ksherlock/x65.git synced 2025-01-04 04:32:25 +00:00

List file option added

- using -lst the disassembled and assembled source is written out side
by side
- option -opcodes saves out a source file with all opcodes & addressing
modes for testing
- cleaned up opcode table to be simpler and more robust so I can
consider supporting other cpus
- bug fixes
This commit is contained in:
Carl-Henrik Skårstedt 2015-10-18 12:42:10 -07:00
parent c8ceffdbf6
commit b7d0b195af
2 changed files with 468 additions and 255 deletions

File diff suppressed because one or more lines are too long

668
x65.cpp
View File

@ -79,7 +79,7 @@ enum StatusCode {
ERROR_EXPRESSION_OPERATION, ERROR_EXPRESSION_OPERATION,
ERROR_EXPRESSION_MISSING_VALUES, ERROR_EXPRESSION_MISSING_VALUES,
ERROR_INSTRUCTION_NOT_ZP, ERROR_INSTRUCTION_NOT_ZP,
ERROR_INVALID_ADDRESSING_MODE_FOR_BRANCH, ERROR_INVALID_ADDRESSING_MODE,
ERROR_BRANCH_OUT_OF_RANGE, ERROR_BRANCH_OUT_OF_RANGE,
ERROR_LABEL_MISPLACED_INTERNAL, ERROR_LABEL_MISPLACED_INTERNAL,
ERROR_BAD_ADDRESSING_MODE, ERROR_BAD_ADDRESSING_MODE,
@ -136,7 +136,7 @@ const char *aStatusStrings[STATUSCODE_COUNT] = {
"Expression operation", "Expression operation",
"Expression missing values", "Expression missing values",
"Instruction can not be zero page", "Instruction can not be zero page",
"Invalid addressing mode for branch instruction", "Invalid addressing mode for instruction",
"Branch out of range", "Branch out of range",
"Internal label organization mishap", "Internal label organization mishap",
"Bad addressing mode", "Bad addressing mode",
@ -248,35 +248,125 @@ enum EvalOperator {
// Opcode encoding // Opcode encoding
typedef struct { typedef struct {
unsigned int op_hash; unsigned int op_hash;
unsigned char group; // group #
unsigned char index; // ground index unsigned char index; // ground index
unsigned char type; // mnemonic or unsigned char type; // mnemonic or
} OP_ID; } OP_ID;
// enum AddrMode {
// 6502 instruction encoding according to this page AMB_ZP_REL_X, // address mode bit index
// http://www.llx.com/~nparker/a2/opcodes.html AMB_ZP,
// decoded instruction: AMB_IMM,
// XXY10000 for branches AMB_ABS,
// AAABBBCC for CC=00, 01, 10 AMB_ZP_Y_REL,
// and some custom ops AMB_ZP_X,
// AMB_ABS_Y,
AMB_ABS_X,
AMB_REL,
AMB_ACC,
AMB_NON,
AMB_COUNT,
enum AddressingMode { AMB_FLIPXY = AMB_COUNT,
AM_REL_ZP_X, // 0 (zp,x) AMB_BRANCH,
AM_ZP, // 1 zp // address mode masks
AM_IMMEDIATE, // 2 #$hh AMM_NON = 1<<AMB_NON,
AM_ABSOLUTE, // 3 $hhhh AMM_IMM = 1<<AMB_IMM,
AM_REL_ZP_Y, // 4 (zp),y AMM_ABS = 1<<AMB_ABS,
AM_ZP_X, // 5 zp,x AMM_REL = 1<<AMB_REL,
AM_ABSOLUTE_Y, // 6 $hhhh,y AMM_ACC = 1<<AMB_ACC,
AM_ABSOLUTE_X, // 7 $hhhh,x AMM_ZP = 1<<AMB_ZP,
AM_RELATIVE, // 8 ($xxxx) AMM_ABS_X = 1<<AMB_ABS_X,
AM_ACCUMULATOR, // 9 A AMM_ABS_Y = 1<<AMB_ABS_Y,
AM_NONE, // 10 <empty> AMM_ZP_X = 1<<AMB_ZP_X,
AM_INVALID, // 11 AMM_ZP_REL_X = 1<<AMB_ZP_REL_X,
AMM_ZP_Y_REL = 1<<AMB_ZP_Y_REL,
AMM_FLIPXY = 1<<AMB_FLIPXY,
AMM_BRANCH = 1<<AMB_BRANCH,
// instruction group specific masks
AMM_BRA = AMM_BRANCH | AMM_ABS,
AMM_ORA = AMM_IMM | AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_Y | AMM_ABS_X | AMM_ZP_REL_X | AMM_ZP_Y_REL,
AMM_STA = AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_Y | AMM_ABS_X | AMM_ZP_REL_X | AMM_ZP_Y_REL,
AMM_ASL = AMM_ACC | AMM_NON | AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_X,
AMM_STX = AMM_FLIPXY | AMM_ZP | AMM_ZP_X | AMM_ABS, // note: for x ,x/,y flipped for this instr.
AMM_LDX = AMM_FLIPXY | AMM_IMM | AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_X, // note: for x ,x/,y flipped for this instr.
AMM_STY = AMM_ZP | AMM_ZP_X | AMM_ABS, // note: for x ,x/,y flipped for this instr.
AMM_LDY = AMM_IMM | AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_X, // note: for x ,x/,y flipped for this instr.
AMM_DEC = AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_X,
AMM_BIT = AMM_ZP | AMM_ABS,
AMM_JMP = AMM_ABS | AMM_REL,
AMM_CPY = AMM_IMM | AMM_ZP | AMM_ABS,
}; };
struct mnem {
const char *instr;
unsigned short modes;
unsigned char aCodes[AMB_COUNT];
};
struct mnem opcodes_6502[] = {
// nam modes (zp,x) zp # $0000 (zp),y zp,x abs,y abs,x (xx) A empty
{ "brk", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "jsr", AMM_ABS, { 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "rti", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40 } },
{ "rts", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60 } },
{ "ora", AMM_ORA, { 0x01, 0x05, 0x09, 0x0d, 0x11, 0x15, 0x19, 0x1d, 0x00, 0x00, 0x00 } },
{ "and", AMM_ORA, { 0x21, 0x25, 0x29, 0x2d, 0x31, 0x35, 0x39, 0x3d, 0x00, 0x00, 0x00 } },
{ "eor", AMM_ORA, { 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d, 0x00, 0x00, 0x00 } },
{ "adc", AMM_ORA, { 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d, 0x00, 0x00, 0x00 } },
{ "sta", AMM_STA, { 0x81, 0x85, 0x00, 0x8d, 0x91, 0x95, 0x99, 0x9d, 0x00, 0x00, 0x00 } },
{ "lda", AMM_ORA, { 0xa1, 0xa5, 0xa9, 0xad, 0xb1, 0xb5, 0xb9, 0xbd, 0x00, 0x00, 0x00 } },
{ "cmp", AMM_ORA, { 0xc1, 0xc5, 0xc9, 0xcd, 0xd1, 0xd5, 0xd9, 0xdd, 0x00, 0x00, 0x00 } },
{ "sbc", AMM_ORA, { 0xe1, 0xe5, 0xe9, 0xed, 0xf1, 0xf5, 0xf9, 0xfd, 0x00, 0x00, 0x00 } },
{ "asl", AMM_ASL, { 0x00, 0x06, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x1e, 0x00, 0x0a, 0x0a } },
{ "rol", AMM_ASL, { 0x00, 0x26, 0x00, 0x2e, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x2a, 0x2a } },
{ "lsr", AMM_ASL, { 0x00, 0x46, 0x00, 0x4e, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x4a, 0x4a } },
{ "ror", AMM_ASL, { 0x00, 0x66, 0x00, 0x6e, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x6a, 0x6a } },
{ "stx", AMM_STX, { 0x00, 0x86, 0x00, 0x8e, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "ldx", AMM_LDX, { 0x00, 0xa6, 0xa2, 0xae, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0x00, 0x00 } },
{ "dec", AMM_DEC, { 0x00, 0xc6, 0x00, 0xce, 0x00, 0xd6, 0x00, 0xde, 0x00, 0x00, 0x00 } },
{ "inc", AMM_DEC, { 0x00, 0xe6, 0x00, 0xee, 0x00, 0xf6, 0x00, 0xfe, 0x00, 0x00, 0x00 } },
{ "php", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 } },
{ "plp", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28 } },
{ "pha", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48 } },
{ "pla", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68 } },
{ "dey", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88 } },
{ "tay", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8 } },
{ "iny", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8 } },
{ "inx", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8 } },
// nam modes (zp,x) zp # $0000 (zp),y zp,x abs,y abs,x (xx) A empty
{ "bpl", AMM_BRA, { 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bmi", AMM_BRA, { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bvc", AMM_BRA, { 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bvs", AMM_BRA, { 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bcc", AMM_BRA, { 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bcs", AMM_BRA, { 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bne", AMM_BRA, { 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "beq", AMM_BRA, { 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "clc", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18 } },
{ "sec", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38 } },
{ "cli", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58 } },
{ "sei", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78 } },
{ "tya", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98 } },
{ "clv", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8 } },
{ "cld", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8 } },
{ "sed", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8 } },
{ "bit", AMM_BIT, { 0x00, 0x24, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "jmp", AMM_JMP, { 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00 } },
{ "sty", AMM_STY, { 0x00, 0x84, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "ldy", AMM_LDY, { 0x00, 0xa4, 0xa0, 0xac, 0x00, 0xb4, 0x00, 0xbc, 0x00, 0x00, 0x00 } },
{ "cpy", AMM_CPY, { 0x00, 0xc4, 0xc0, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "cpx", AMM_CPY, { 0x00, 0xe4, 0xe0, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "txa", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a } },
{ "txs", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a } },
{ "tax", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa } },
{ "tsx", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba } },
{ "dex", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca } },
{ "nop", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea } }
};
static const int num_opcodes_6502 = sizeof(opcodes_6502) / sizeof(opcodes_6502[0]);
// How instruction argument is encoded // How instruction argument is encoded
enum CODE_ARG { enum CODE_ARG {
CA_NONE, // single byte instruction CA_NONE, // single byte instruction
@ -285,62 +375,6 @@ enum CODE_ARG {
CA_BRANCH // instruction carries a relative address CA_BRANCH // instruction carries a relative address
}; };
// opcode groups
enum OP_GROUP {
OPG_SUBROUT,
OPG_CC01,
OPG_CC10,
OPG_STACK,
OPG_BRANCH,
OPG_FLAG,
OPG_CC00,
OPG_TRANS
};
// opcode exception indices
enum OP_INDICES {
OPI_JSR = 1,
OPI_LDX = 5,
OPI_STX = 4,
OPI_STA = 4,
OPI_JMP = 1,
};
#define RELATIVE_JMP_DELTA 0x20
// opcode names in groups (prefix by group size)
const char aInstr[] = {
"BRK,JSR,RTI,RTS\n"
"ORA,AND,EOR,ADC,STA,LDA,CMP,SBC\n"
"ASL,ROL,LSR,ROR,STX,LDX,DEC,INC\n"
"PHP,PLP,PHA,PLA,DEY,TAY,INY,INX\n"
"BPL,BMI,BVC,BVS,BCC,BCS,BNE,BEQ\n"
"CLC,SEC,CLI,SEI,TYA,CLV,CLD,SED\n"
"BIT,JMP,,STY,LDY,CPY,CPX\n"
"TXA,TXS,TAX,TSX,DEX,,NOP"
};
// group # + index => base opcode
const unsigned char aMulAddGroup[][2] = {
{ 0x20,0x00 },
{ 0x20,0x01 },
{ 0x20,0x02 },
{ 0x20,0x08 },
{ 0x20,0x10 },
{ 0x20,0x18 },
{ 0x20,0x20 },
{ 0x10,0x8a }
};
char aCC00Modes[] = { AM_IMMEDIATE, AM_ZP, AM_INVALID, AM_ABSOLUTE, AM_INVALID, AM_ZP_X, AM_INVALID, AM_ABSOLUTE_X };
char aCC01Modes[] = { AM_REL_ZP_X, AM_ZP, AM_IMMEDIATE, AM_ABSOLUTE, AM_REL_ZP_Y, AM_ZP_X, AM_ABSOLUTE_X, AM_ABSOLUTE_Y };
char aCC10Modes[] = { AM_IMMEDIATE, AM_ZP, AM_NONE, AM_ABSOLUTE, AM_INVALID, AM_ZP_X, AM_INVALID, AM_ABSOLUTE_X };
unsigned char CC00ModeAdd[] = { 0xff, 4, 0, 12, 0xff, 20, 0xff, 28 };
unsigned char CC00Mask[] = { 0x0a, 0x08, 0x08, 0x2a, 0xae, 0x0e, 0x0e };
unsigned char CC10ModeAdd[] = { 0xff, 4, 0, 12, 0xff, 20, 0xff, 28 };
unsigned char CC10Mask[] = { 0xaa, 0xaa, 0xaa, 0xaa, 0x2a, 0xae, 0xaa, 0xaa };
// hardtexted strings // hardtexted strings
static const strref c_comment("//"); static const strref c_comment("//");
static const strref word_char_range("!0-9a-zA-Z_@$!#"); static const strref word_char_range("!0-9a-zA-Z_@$!#");
@ -352,6 +386,19 @@ static const strref str_label("label");
static const strref str_const("const"); static const strref str_const("const");
static const strref struct_byte("byte"); static const strref struct_byte("byte");
static const strref struct_word("word"); static const strref struct_word("word");
static const char* aAddrModeFmt[] = {
"%s ($%02x,x)",
"%s $%02x",
"%s #$%02x",
"%s $%04x",
"%s ($%02x),y",
"%s $%02x,x",
"%s $%04x,y",
"%s $%04x,x",
"%s ($%04x)",
"%s A",
"%s " };
// Binary search over an array of unsigned integers, may contain multiple instances of same key // Binary search over an array of unsigned integers, may contain multiple instances of same key
unsigned int FindLabelIndex(unsigned int hash, unsigned int *table, unsigned int count) unsigned int FindLabelIndex(unsigned int hash, unsigned int *table, unsigned int count)
@ -459,7 +506,7 @@ public:
}; };
// relocs are cheaper than full expressions and work with // relocs are cheaper than full expressions and work with
// local labels for relative sections which would otherwise // local labels for relative sections which could otherwise
// be out of scope at link time. // be out of scope at link time.
struct Reloc { struct Reloc {
@ -480,6 +527,16 @@ struct Reloc {
}; };
typedef std::vector<struct Reloc> relocList; typedef std::vector<struct Reloc> relocList;
// For assembly listing this remembers the location of each line
struct ListLine {
int address; // start address of this line
int size; // number of bytes generated for this line
int line_offs; // offset into code
strref source_name; // source file index name
strref code; // line of code this represents
};
typedef std::vector<struct ListLine> Listing;
// start of data section support // start of data section support
// Default is a fixed address section at $1000 // Default is a fixed address section at $1000
// Whenever org or dum with address is encountered => new section // Whenever org or dum with address is encountered => new section
@ -492,7 +549,6 @@ typedef struct Section {
int load_address; // if assigned a load address int load_address; // if assigned a load address
int start_address; int start_address;
int address; // relative or absolute PC int address; // relative or absolute PC
bool address_assigned; // address is absolute if assigned
// merged sections // merged sections
int merged_offset; // -1 if not merged int merged_offset; // -1 if not merged
@ -505,6 +561,9 @@ typedef struct Section {
// reloc data // reloc data
relocList *pRelocs; // link time resolve (not all sections need this) relocList *pRelocs; // link time resolve (not all sections need this)
Listing *pListing; // if list output
bool address_assigned; // address is absolute if assigned
bool dummySection; // true if section does not generate data, only labels bool dummySection; // true if section does not generate data, only labels
void reset() { void reset() {
@ -512,7 +571,10 @@ typedef struct Section {
address_assigned = false; output = nullptr; curr = nullptr; address_assigned = false; output = nullptr; curr = nullptr;
dummySection = false; output_capacity = 0; dummySection = false; output_capacity = 0;
merged_offset = -1; merged_offset = -1;
if (pRelocs) delete pRelocs; pRelocs = nullptr; if (pRelocs) delete pRelocs;
pRelocs = nullptr;
if (pListing) delete pListing;
pListing = nullptr;
} }
void Cleanup() { if (output) free(output); reset(); } void Cleanup() { if (output) free(output); reset(); }
@ -533,10 +595,11 @@ typedef struct Section {
bool IsMergedSection() const { return merged_offset >= 0; } bool IsMergedSection() const { return merged_offset >= 0; }
void AddReloc(int base, int offset, int section, Reloc::Type type = Reloc::WORD); void AddReloc(int base, int offset, int section, Reloc::Type type = Reloc::WORD);
Section() : pRelocs(nullptr) { reset(); } Section() : pRelocs(nullptr), pListing(nullptr) { reset(); }
Section(strref _name, int _address) : pRelocs(nullptr) { reset(); name = _name; Section(strref _name, int _address) : pRelocs(nullptr), pListing(nullptr)
start_address = load_address = address = _address; address_assigned = true; } { reset(); name = _name; start_address = load_address = address = _address;
Section(strref _name) : pRelocs(nullptr) { reset(); name = _name; address_assigned = true; }
Section(strref _name) : pRelocs(nullptr), pListing(nullptr) { reset(); name = _name;
start_address = load_address = address = 0; address_assigned = false; } start_address = load_address = address = 0; address_assigned = false; }
~Section() { reset(); } ~Section() { reset(); }
@ -733,10 +796,17 @@ public:
bool symbol_export, last_label_local; bool symbol_export, last_label_local;
bool errorEncountered; bool errorEncountered;
bool list_assembly;
// Convert source to binary // Convert source to binary
void Assemble(strref source, strref filename, bool obj_target); void Assemble(strref source, strref filename, bool obj_target);
// Generate assembler listing if requested
bool List(strref filename);
// Generate source for all valid instructions
bool AllOpcodes(strref filename);
// Clean up memory allocations, reset assembler state // Clean up memory allocations, reset assembler state
void Cleanup(); void Cleanup();
@ -808,9 +878,9 @@ public:
// Assembler steps // Assembler steps
StatusCode ApplyDirective(AssemblerDirective dir, strref line, strref source_file); StatusCode ApplyDirective(AssemblerDirective dir, strref line, strref source_file);
AddressingMode GetAddressMode(strref line, bool flipXY, AddrMode GetAddressMode(strref line, bool flipXY,
StatusCode &error, strref &expression); StatusCode &error, strref &expression);
StatusCode AddOpcode(strref line, int group, int index, strref source_file); StatusCode AddOpcode(strref line, int index, strref source_file);
StatusCode BuildLine(OP_ID *pInstr, int numInstructions, strref line); StatusCode BuildLine(OP_ID *pInstr, int numInstructions, strref line);
StatusCode BuildSegment(OP_ID *pInstr, int numInstructions); StatusCode BuildSegment(OP_ID *pInstr, int numInstructions);
@ -866,6 +936,7 @@ void Asm::Cleanup() {
symbol_export = false; symbol_export = false;
last_label_local = false; last_label_local = false;
errorEncountered = false; errorEncountered = false;
list_assembly = false;
} }
// Read in text data (main source, include, etc.) // Read in text data (main source, include, etc.)
@ -1078,6 +1149,7 @@ StatusCode Asm::LinkRelocs(int section_id, int section_address)
return STATUS_OK; return STATUS_OK;
} }
// Link sections with a specific name at this point
StatusCode Asm::LinkSections(strref name) { StatusCode Asm::LinkSections(strref name) {
if (CurrSection().IsRelativeSection()) if (CurrSection().IsRelativeSection())
return ERROR_LINKER_MUST_BE_IN_FIXED_ADDRESS_SECTION; return ERROR_LINKER_MUST_BE_IN_FIXED_ADDRESS_SECTION;
@ -1092,8 +1164,9 @@ StatusCode Asm::LinkSections(strref name) {
// Get base addresses // Get base addresses
CheckOutputCapacity((int)s.size()); CheckOutputCapacity((int)s.size());
int section_address = CurrSection().GetPC(); Section &curr = CurrSection();
unsigned char *section_out = CurrSection().curr; int section_address = curr.GetPC();
unsigned char *section_out = curr.curr;
if (s.output) if (s.output)
memcpy(section_out, s.output, s.size()); memcpy(section_out, s.output, s.size());
CurrSection().address += (int)s.size(); CurrSection().address += (int)s.size();
@ -1110,6 +1183,22 @@ StatusCode Asm::LinkSections(strref name) {
s.merged_section = SectionId(); s.merged_section = SectionId();
s.merged_offset = (int)(section_out - CurrSection().output); s.merged_offset = (int)(section_out - CurrSection().output);
// Merge in the listing at this point
if (s.pListing) {
if (!curr.pListing)
curr.pListing = new Listing;
if ((curr.pListing->size() + s.pListing->size()) > curr.pListing->capacity())
curr.pListing->reserve(curr.pListing->size() + s.pListing->size() + 256);
for (Listing::iterator si = s.pListing->begin(); si != s.pListing->end(); ++si) {
struct ListLine lst = *si;
lst.address += s.merged_offset;
curr.pListing->push_back(lst);
}
delete s.pListing;
s.pListing = nullptr;
}
// All labels in this section can now be assigned // All labels in this section can now be assigned
LinkLabelsToAddress(section_id, section_address); LinkLabelsToAddress(section_id, section_address);
@ -1140,6 +1229,7 @@ void Section::CheckOutputCapacity(unsigned int addSize) {
} }
} }
// Add one byte to a section
void Section::AddByte(int b) { void Section::AddByte(int b) {
if (!dummySection) { if (!dummySection) {
CheckOutputCapacity(1); CheckOutputCapacity(1);
@ -1148,6 +1238,7 @@ void Section::AddByte(int b) {
address++; address++;
} }
// Add a 16 bit word to a section
void Section::AddWord(int w) { void Section::AddWord(int w) {
if (!dummySection) { if (!dummySection) {
CheckOutputCapacity(2); CheckOutputCapacity(2);
@ -1157,6 +1248,7 @@ void Section::AddWord(int w) {
address += 2; address += 2;
} }
// Add arbitrary length data to a section
void Section::AddBin(unsigned const char *p, int size) { void Section::AddBin(unsigned const char *p, int size) {
if (!dummySection) { if (!dummySection) {
CheckOutputCapacity(size); CheckOutputCapacity(size);
@ -1166,6 +1258,7 @@ void Section::AddBin(unsigned const char *p, int size) {
address += size; address += size;
} }
// Add a relocation marker to a section
void Section::AddReloc(int base, int offset, int section, Reloc::Type type) void Section::AddReloc(int base, int offset, int section, Reloc::Type type)
{ {
if (!pRelocs) if (!pRelocs)
@ -2987,32 +3080,22 @@ int sortHashLookup(const void *A, const void *B) {
return _A->op_hash > _B->op_hash ? 1 : -1; return _A->op_hash > _B->op_hash ? 1 : -1;
} }
int BuildInstructionTable(OP_ID *pInstr, strref instr_text, int maxInstructions) int BuildInstructionTable(OP_ID *pInstr, int maxInstructions)
{ {
// create an instruction table (mnemonic hash lookup) // create an instruction table (mnemonic hash lookup)
int numInstructions = 0; int numInstructions = 0;
char group_num = 0; for (int i = 0; i < num_opcodes_6502; i++) {
while (strref line = instr_text.next_line()) { OP_ID &op = pInstr[numInstructions++];
int index_num = 0; op.op_hash = strref(opcodes_6502[i].instr).fnv1a_lower();
while (line) { op.index = i;
strref mnemonic = line.split_token_trim(','); op.type = OT_MNEMONIC;
if (mnemonic) {
OP_ID &op_hash = pInstr[numInstructions++];
op_hash.op_hash = mnemonic.fnv1a_lower();
op_hash.group = group_num;
op_hash.index = index_num;
op_hash.type = OT_MNEMONIC;
}
index_num++;
}
group_num++;
} }
// add assembler directives // add assembler directives
for (int d=0; d<nDirectiveNames; d++) { for (int d=0; d<nDirectiveNames; d++) {
OP_ID &op_hash = pInstr[numInstructions++]; OP_ID &op_hash = pInstr[numInstructions++];
op_hash.op_hash = strref(aDirectiveNames[d].name).fnv1a_lower(); op_hash.op_hash = strref(aDirectiveNames[d].name).fnv1a_lower();
op_hash.group = 0xff; // op_hash.group = 0xff;
op_hash.index = (unsigned char)aDirectiveNames[d].directive; op_hash.index = (unsigned char)aDirectiveNames[d].directive;
op_hash.type = OT_DIRECTIVE; op_hash.type = OT_DIRECTIVE;
} }
@ -3022,38 +3105,38 @@ int BuildInstructionTable(OP_ID *pInstr, strref instr_text, int maxInstructions)
return numInstructions; return numInstructions;
} }
AddressingMode Asm::GetAddressMode(strref line, bool flipXY, StatusCode &error, strref &expression) AddrMode Asm::GetAddressMode(strref line, bool flipXY, StatusCode &error, strref &expression)
{ {
bool force_zp = false; bool force_zp = false;
bool need_more = true; bool need_more = true;
strref arg, deco; strref arg, deco;
AddressingMode addrMode = AM_INVALID; AddrMode addrMode = AMB_COUNT;
while (need_more) { while (need_more) {
need_more = false; need_more = false;
switch (line.get_first()) { switch (line.get_first()) {
case 0: // empty line, empty addressing mode case 0: // empty line, empty addressing mode
addrMode = AM_NONE; addrMode = AMB_NON;
break; break;
case '(': // relative (jmp (addr), (zp,x), (zp),y) case '(': // relative (jmp (addr), (zp,x), (zp),y)
deco = line.scoped_block_skip(); deco = line.scoped_block_skip();
line.skip_whitespace(); line.skip_whitespace();
expression = deco.split_token_trim(','); expression = deco.split_token_trim(',');
addrMode = AM_RELATIVE; addrMode = AMB_REL;
if (deco[0]=='x' || deco[0]=='X') if (deco[0]=='x' || deco[0]=='X')
addrMode = AM_REL_ZP_X; addrMode = AMB_ZP_REL_X;
else if (line[0]==',') { else if (line[0]==',') {
++line; ++line;
line.skip_whitespace(); line.skip_whitespace();
if (line[0]=='y' || line[0]=='Y') { if (line[0]=='y' || line[0]=='Y') {
addrMode = AM_REL_ZP_Y; addrMode = AMB_ZP_Y_REL;
++line; ++line;
} }
} }
break; break;
case '#': // immediate, determine if value is ok case '#': // immediate, determine if value is ok
++line; ++line;
addrMode = AM_IMMEDIATE; addrMode = AMB_IMM;
expression = line; expression = line;
break; break;
default: { // accumulator or absolute default: { // accumulator or absolute
@ -3063,20 +3146,20 @@ AddressingMode Asm::GetAddressMode(strref line, bool flipXY, StatusCode &error,
line += 3; line += 3;
need_more = true; need_more = true;
} else if (strref("A").is_prefix_word(line)) { } else if (strref("A").is_prefix_word(line)) {
addrMode = AM_ACCUMULATOR; addrMode = AMB_ACC;
} else { // absolute (zp, offs x, offs y) } else { // absolute (zp, offs x, offs y)
addrMode = force_zp ? AM_ZP : AM_ABSOLUTE; addrMode = force_zp ? AMB_ZP : AMB_ABS;
expression = line.split_token_trim(','); expression = line.split_token_trim(',');
bool relX = line && (line[0]=='x' || line[0]=='X'); bool relX = line && (line[0]=='x' || line[0]=='X');
bool relY = line && (line[0]=='y' || line[0]=='Y'); bool relY = line && (line[0]=='y' || line[0]=='Y');
if ((flipXY && relY) || (!flipXY && relX)) if ((flipXY && relY) || (!flipXY && relX))
addrMode = addrMode==AM_ZP ? AM_ZP_X : AM_ABSOLUTE_X; addrMode = addrMode==AMB_ZP ? AMB_ZP_X : AMB_ABS_X;
else if ((flipXY && relX) || (!flipXY && relY)) { else if ((flipXY && relX) || (!flipXY && relY)) {
if (force_zp) { if (force_zp) {
error = ERROR_INSTRUCTION_NOT_ZP; error = ERROR_INSTRUCTION_NOT_ZP;
break; break;
} }
addrMode = AM_ABSOLUTE_Y; addrMode = AMB_ABS_Y;
} }
} }
} }
@ -3090,15 +3173,14 @@ AddressingMode Asm::GetAddressMode(strref line, bool flipXY, StatusCode &error,
// Push an opcode to the output buffer // Push an opcode to the output buffer
StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file) StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
{ {
StatusCode error = STATUS_OK; StatusCode error = STATUS_OK;
int base_opcode = aMulAddGroup[group][1] + index * aMulAddGroup[group][0];
strref expression; strref expression;
// Get the addressing mode and the expression it refers to // Get the addressing mode and the expression it refers to
AddressingMode addrMode = GetAddressMode(line, AddrMode addrMode = GetAddressMode(line,
group==OPG_CC10&&index>=OPI_STX&&index<=OPI_LDX, error, expression); !!(opcodes_6502[index].modes & AMM_FLIPXY), error, expression);
int value = 0; int value = 0;
int target_section = -1; int target_section = -1;
@ -3107,7 +3189,7 @@ StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file)
bool evalLater = false; bool evalLater = false;
if (expression) { if (expression) {
struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1, struct EvalContext etx(CurrSection().GetPC(), scope_address[scope_depth], -1,
group==OPG_BRANCH ? SectionId() : -1); !!(opcodes_6502[index].modes & AMM_BRA) ? SectionId() : -1);
error = EvalExpression(expression, etx, value); error = EvalExpression(expression, etx, value);
if (error == STATUS_NOT_READY) { if (error == STATUS_NOT_READY) {
evalLater = true; evalLater = true;
@ -3121,13 +3203,15 @@ StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file)
} }
// check if address is in zero page range and should use a ZP mode instead of absolute // check if address is in zero page range and should use a ZP mode instead of absolute
if (!evalLater && value>=0 && value<0x100 && group!=OPG_BRANCH && error != STATUS_RELATIVE_SECTION) { if (!evalLater && value>=0 && value<0x100 && error != STATUS_RELATIVE_SECTION) {
switch (addrMode) { switch (addrMode) {
case AM_ABSOLUTE: case AMB_ABS:
addrMode = AM_ZP; if (opcodes_6502[index].modes & AMM_ZP)
addrMode = AMB_ZP;
break; break;
case AM_ABSOLUTE_X: case AMB_ABS_X:
addrMode = AM_ZP_X; if (opcodes_6502[index].modes & AMM_ZP_X)
addrMode = AMB_ZP_X;
break; break;
default: default:
break; break;
@ -3135,97 +3219,17 @@ StatusCode Asm::AddOpcode(strref line, int group, int index, strref source_file)
} }
CODE_ARG codeArg = CA_NONE; CODE_ARG codeArg = CA_NONE;
unsigned char opcode = base_opcode; unsigned char opcode = opcodes_6502[index].aCodes[addrMode];
// analyze addressing mode per mnemonic group if (!(opcodes_6502[index].modes & (1 << addrMode)))
switch (group) { error = ERROR_INVALID_ADDRESSING_MODE;
case OPG_BRANCH: else {
if (addrMode != AM_ABSOLUTE) { if (opcodes_6502[index].modes & AMM_BRANCH)
error = ERROR_INVALID_ADDRESSING_MODE_FOR_BRANCH;
break;
}
codeArg = CA_BRANCH; codeArg = CA_BRANCH;
break; else if (addrMode == AMB_ABS || addrMode == AMB_REL || addrMode == AMB_ABS_X || addrMode == AMB_ABS_Y)
case OPG_SUBROUT:
if (index==OPI_JSR) { // jsr
if (addrMode != AM_ABSOLUTE)
error = ERROR_INVALID_ADDRESSING_MODE_FOR_BRANCH;
else
codeArg = CA_TWO_BYTES; codeArg = CA_TWO_BYTES;
} else if (addrMode != AMB_NON && addrMode != AMB_ACC)
break;
case OPG_STACK:
case OPG_FLAG:
case OPG_TRANS:
codeArg = CA_NONE;
break;
case OPG_CC00:
// jump relative exception
if (addrMode==AM_RELATIVE && index==OPI_JMP) {
base_opcode += RELATIVE_JMP_DELTA;
addrMode = AM_ABSOLUTE; // the relative address is in an absolute location ;)
}
if (addrMode>7 || (CC00Mask[index]&(1<<addrMode))==0)
error = ERROR_BAD_ADDRESSING_MODE;
else {
opcode = base_opcode + CC00ModeAdd[addrMode];
switch (addrMode) {
case AM_ABSOLUTE:
case AM_ABSOLUTE_Y:
case AM_ABSOLUTE_X:
codeArg = CA_TWO_BYTES;
break;
default:
codeArg = CA_ONE_BYTE; codeArg = CA_ONE_BYTE;
break;
}
}
break;
case OPG_CC01:
if (addrMode>7 || (addrMode==AM_IMMEDIATE && index==OPI_STA))
error = ERROR_BAD_ADDRESSING_MODE;
else {
opcode = base_opcode + addrMode*4;
switch (addrMode) {
case AM_ABSOLUTE:
case AM_ABSOLUTE_Y:
case AM_ABSOLUTE_X:
codeArg = CA_TWO_BYTES;
break;
default:
codeArg = CA_ONE_BYTE;
break;
}
}
break;
case OPG_CC10: {
if (addrMode == AM_NONE || addrMode == AM_ACCUMULATOR) {
if (index>=4)
error = ERROR_BAD_ADDRESSING_MODE;
else {
opcode = base_opcode + 8;
codeArg = CA_NONE;
}
} else {
if (addrMode>7 || (CC10Mask[index]&(1<<addrMode))==0)
error = ERROR_BAD_ADDRESSING_MODE;
else {
opcode = base_opcode + CC10ModeAdd[addrMode];
switch (addrMode) {
case AM_IMMEDIATE:
case AM_ZP:
case AM_ZP_X:
codeArg = CA_ONE_BYTE;
break;
default:
codeArg = CA_TWO_BYTES;
break;
}
}
}
break;
}
} }
// Add the instruction and argument to the code // Add the instruction and argument to the code
if (error == STATUS_OK || error == STATUS_RELATIVE_SECTION) { if (error == STATUS_OK || error == STATUS_RELATIVE_SECTION) {
@ -3296,6 +3300,10 @@ StatusCode Asm::BuildLine(OP_ID *pInstr, int numInstructions, strref line)
if (syntax==SYNTAX_MERLIN && line[0]=='*') if (syntax==SYNTAX_MERLIN && line[0]=='*')
return STATUS_OK; return STATUS_OK;
// remember for listing
int start_section = SectionId();
int start_address = CurrSection().address;
strref code_line = line;
while (line && error == STATUS_OK) { while (line && error == STATUS_OK) {
strref line_start = line; strref line_start = line;
char char0 = line[0]; // first char including white space char char0 = line[0]; // first char including white space
@ -3364,12 +3372,8 @@ StatusCode Asm::BuildLine(OP_ID *pInstr, int numInstructions, strref line)
line.skip_whitespace(); line.skip_whitespace();
} }
error = ApplyDirective((AssemblerDirective)pInstr[op_idx].index, line, contextStack.curr().source_file); error = ApplyDirective((AssemblerDirective)pInstr[op_idx].index, line, contextStack.curr().source_file);
} else if (ConditionalAsm() && pInstr[op_idx].type==OT_MNEMONIC) { } else if (ConditionalAsm() && pInstr[op_idx].type==OT_MNEMONIC)
OP_ID &id = pInstr[op_idx]; error = AddOpcode(line, pInstr[op_idx].index, contextStack.curr().source_file);
int group = id.group;
int index = id.index;
error = AddOpcode(line, group, index, contextStack.curr().source_file);
}
line.clear(); line.clear();
} else if (!ConditionalAsm()) { } else if (!ConditionalAsm()) {
line.clear(); // do nothing if conditional nesting so clear the current line line.clear(); // do nothing if conditional nesting so clear the current line
@ -3440,6 +3444,33 @@ StatusCode Asm::BuildLine(OP_ID *pInstr, int numInstructions, strref line)
if (error < ERROR_STOP_PROCESSING_ON_HIGHER) if (error < ERROR_STOP_PROCESSING_ON_HIGHER)
error = STATUS_OK; error = STATUS_OK;
} }
// update listing
if (error == STATUS_OK && list_assembly) {
Section &curr = CurrSection();
if (!curr.pListing)
curr.pListing = new Listing;
if (curr.pListing && curr.pListing->size() == curr.pListing->capacity())
curr.pListing->reserve(curr.pListing->size() + 256);
if (SectionId() == start_section) {
struct ListLine lst;
lst.address = start_address - curr.start_address;
lst.size = curr.address - start_address;
lst.code = contextStack.curr().source_file;
lst.source_name = contextStack.curr().source_name;
lst.line_offs = int(code_line.get() - lst.code.get());
if (lst.size)
curr.pListing->push_back(lst);
} else {
struct ListLine lst;
lst.address = 0;
lst.size = curr.address - curr.start_address;
lst.code = contextStack.curr().source_file;
lst.source_name = contextStack.curr().source_name;
lst.line_offs = int(code_line.get() - lst.code.get());
if (lst.size)
curr.pListing->push_back(lst);
}
}
return error; return error;
} }
@ -3460,11 +3491,156 @@ StatusCode Asm::BuildSegment(OP_ID *pInstr, int numInstructions)
return error; return error;
} }
// Produce the assembler listing
bool Asm::List(strref filename)
{
FILE *f = stdout;
bool opened = false;
if (filename) {
f = fopen(strown<512>(filename).c_str(), "w");
if (!f)
return false;
opened = true;
}
// Build a disassembly lookup table
unsigned char mnemonic[256];
unsigned char addrmode[256];
memset(mnemonic, 255, sizeof(mnemonic));
memset(addrmode, 255, sizeof(addrmode));
for (int i = 0; i < num_opcodes_6502; i++) {
for (int j = 0; j < AMB_COUNT; j++) {
if (opcodes_6502[i].modes & (1 << j)) {
unsigned char op = opcodes_6502[i].aCodes[j];
mnemonic[op] = i;
addrmode[op] = j;
}
}
}
strref prev_src;
int prev_offs = 0;
for (std::vector<Section>::iterator si = allSections.begin(); si != allSections.end(); ++si) {
if (!si->pListing)
continue;
for (Listing::iterator li = si->pListing->begin(); li != si->pListing->end(); ++li) {
strown<256> out;
const struct ListLine &lst = *li;
if (prev_src.fnv1a() != lst.source_name.fnv1a() || lst.line_offs < prev_offs) {
fprintf(f, STRREF_FMT "(%d):\n", STRREF_ARG(lst.source_name), lst.code.count_lines(lst.line_offs));
prev_src = lst.source_name;
}
else {
strref prvline = lst.code.get_substr(prev_offs, lst.line_offs - prev_offs);
prvline.next_line();
if (prvline.count_lines() < 5) {
while (strref space_line = prvline.line()) {
space_line.clip_trailing_whitespace();
strown<128> line_fix(space_line);
for (strl_t pos = 0; pos < line_fix.len(); ++pos) {
if (line_fix[pos] == '\t')
line_fix.exchange(pos, 1, pos & 1 ? strref(" ") : strref(" "));
}
out.append_to(' ', 30);
out.append(line_fix.get_strref());
fprintf(f, STRREF_FMT "\n", STRREF_ARG(out));
out.clear();
}
}
else {
fprintf(f, STRREF_FMT "(%d):\n", STRREF_ARG(lst.source_name), lst.code.count_lines(lst.line_offs));
}
}
out.sprintf_append("$%04x ", lst.address + si->start_address);
int s = lst.size < 4 ? lst.size : 4;
if (si->output && si->output_capacity >= (lst.address + s)) {
for (int b = 0; b < s; ++b)
out.sprintf_append("%02x ", si->output[lst.address + b]);
}
else
s = 0;
out.append_to(' ', 18);
if (lst.size) {
unsigned char *buf = si->output + lst.address;
unsigned char op = mnemonic[*buf];
unsigned char am = addrmode[*buf];
if (op != 255 && am != 255) {
const char *fmt = aAddrModeFmt[am];
if (opcodes_6502[op].modes & AMM_FLIPXY) {
if (am == AMB_ZP_X) fmt = "%s $%02x,y";
else if (am == AMB_ABS_X) fmt = "%s $%04x,y";
}
if (opcodes_6502[op].modes & AMM_BRANCH)
out.sprintf_append(fmt, opcodes_6502[op].instr, (char)buf[1] + lst.address + si->start_address + 2);
else if (am == AMB_NON || am == AMB_ACC)
out.sprintf_append(fmt, opcodes_6502[op].instr);
else if (am == AMB_ABS || am == AMB_ABS_X || am == AMB_ABS_Y || am == AMB_REL)
out.sprintf_append(fmt, opcodes_6502[op].instr, buf[1] | (buf[2] << 8));
else
out.sprintf_append(fmt, opcodes_6502[op].instr, buf[1]);
}
}
out.append_to(' ', 30);
strref line = lst.code.get_skipped(lst.line_offs).get_line();
line.clip_trailing_whitespace();
strown<128> line_fix(line);
for (strl_t pos = 0; pos < line_fix.len(); ++pos) {
if (line_fix[pos] == '\t')
line_fix.exchange(pos, 1, pos & 1 ? strref(" ") : strref(" "));
}
out.append(line_fix.get_strref());
fprintf(f, STRREF_FMT "\n", STRREF_ARG(out));
prev_offs = lst.line_offs;
}
}
if (opened)
fclose(f);
return true;
}
// Create a listing of all valid instructions and addressing modes
bool Asm::AllOpcodes(strref filename)
{
FILE *f = stdout;
bool opened = false;
if (filename) {
f = fopen(strown<512>(filename).c_str(), "w");
if (!f)
return false;
opened = true;
}
for (int i = 0; i < num_opcodes_6502; i++) {
for (int a = 0; a < AMB_COUNT; a++) {
if (opcodes_6502[i].modes & (1 << a)) {
const char *fmt = aAddrModeFmt[a];
if (opcodes_6502[i].modes & AMM_BRANCH)
fprintf(f, "%s *+%d", opcodes_6502[i].instr, 5);
else {
if (opcodes_6502[i].modes & AMM_FLIPXY) {
if (a == AMB_ZP_X) fmt = "%s $%02x,y";
else if (a == AMB_ABS_X) fmt = "%s $%04x,y";
}
if (a==AMB_ABS || a==AMB_ABS_X || a==AMB_ABS_Y || a==AMB_REL)
fprintf(f, fmt, opcodes_6502[i].instr, 0x2120);
else
fprintf(f, fmt, opcodes_6502[i].instr, 0x21, 0x20, 0x1f);
}
fputs("\n", f);
}
}
}
if (opened)
fclose(f);
return true;
}
// create an instruction table (mnemonic hash lookup + directives) // create an instruction table (mnemonic hash lookup + directives)
void Asm::Assemble(strref source, strref filename, bool obj_target) void Asm::Assemble(strref source, strref filename, bool obj_target)
{ {
OP_ID *pInstr = new OP_ID[256]; OP_ID *pInstr = new OP_ID[256];
int numInstructions = BuildInstructionTable(pInstr, strref(aInstr, strl_t(sizeof(aInstr)-1)), 256); int numInstructions = BuildInstructionTable(pInstr, 256);
StatusCode error = STATUS_OK; StatusCode error = STATUS_OK;
contextStack.push(filename, source, source); contextStack.push(filename, source, source);
@ -3502,6 +3678,10 @@ void Asm::Assemble(strref source, strref filename, bool obj_target)
} }
} }
} }
// dump the listing from each section
if (list_assembly) {
List(nullptr);
}
} }
@ -3910,15 +4090,19 @@ StatusCode Asm::ReadObjectFile(strref filename)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
const strref listing("lst");
const strref allinstr("opcodes");
int return_value = 0; int return_value = 0;
bool load_header = true; bool load_header = true;
bool size_header = false; bool size_header = false;
bool info = false; bool info = false;
bool gen_allinstr = false;
Asm assembler; Asm assembler;
const char *source_filename = nullptr, *obj_out_file = nullptr; const char *source_filename = nullptr, *obj_out_file = nullptr;
const char *binary_out_name = nullptr; const char *binary_out_name = nullptr;
const char *sym_file = nullptr, *vs_file = nullptr; const char *sym_file = nullptr, *vs_file = nullptr;
strref list_file, allinstr_file;
for (int a=1; a<argc; a++) { for (int a=1; a<argc; a++) {
strref arg(argv[a]); strref arg(argv[a]);
if (arg.get_first()=='-') { if (arg.get_first()=='-') {
@ -3944,28 +4128,43 @@ int main(int argc, char **argv)
size_header = false; size_header = false;
} else if (arg.same_str("info")) } else if (arg.same_str("info"))
info = true; info = true;
else if (arg.same_str("sym") && (a + 1) < argc) else if (arg.has_prefix(listing) && (arg.get_len() == listing.get_len() || arg[listing.get_len()] == '=')) {
assembler.list_assembly = true;
list_file = arg.after('=');
} else if (arg.has_prefix(allinstr) && (arg.get_len() == allinstr.get_len() || arg[allinstr.get_len()] == '=')) {
gen_allinstr = true;
allinstr_file = arg.after('=');
} else if (arg.same_str("sym") && (a + 1) < argc)
sym_file = argv[++a]; sym_file = argv[++a];
else if (arg.same_str("obj") && (a + 1) < argc) else if (arg.same_str("obj") && (a + 1) < argc)
obj_out_file = argv[++a]; obj_out_file = argv[++a];
else if (arg.same_str("vice") && (a + 1) < argc) else if (arg.same_str("vice") && (a + 1) < argc)
vs_file = argv[++a]; vs_file = argv[++a];
} } else if (!source_filename)
else if (!source_filename)
source_filename = arg.get(); source_filename = arg.get();
else if (!binary_out_name) else if (!binary_out_name)
binary_out_name = arg.get(); binary_out_name = arg.get();
} }
if (!source_filename) { if (gen_allinstr) {
puts("Usage:\nx65 [options] filename.s code.prg\n" assembler.AllOpcodes(allinstr_file);
" * -i<path>: Add include path\n * -D<label>[=<value>]: Define a label with an optional value (otherwise 1)\n" } else if (!source_filename) {
" * -bin: Raw binary\n * -c64: Include load address (default)\n * -a2b: Apple II Dos 3.3 Binary\n" puts( "Usage:\n" " x65 filename.s code.prg [options]\n"
" * -sym <file.sym>: vice/kick asm symbol file\n" " * -i(path) : Add include path\n"
" * -vice <file.vs>: export a vice symbol file\nhttps://github.com/sakrac/x65\n"); " * -D(label)[=<value>] : Define a label with an optional value(otherwise defined as 1)\n"
" * -obj(file.o65) : generate object file for later linking\n"
" * -bin : Raw binary\n"
" * -c64 : Include load address(default)\n"
" * -a2b : Apple II Dos 3.3 Binary\n"
" * -sym(file.sym) : symbol file\n"
" * -lst / -lst = (file.lst) : generate disassembly text from result(file or stdout)\n"
" * -opcodes / -opcodes = (file.s) : dump all available opcodes(file or stdout)\n"
" * -vice(file.vs) : export a vice symbol file\n");
return 0; return 0;
} }
// Load source // Load source
if (source_filename) { if (source_filename) {
size_t size = 0; size_t size = 0;
@ -3977,6 +4176,9 @@ int main(int argc, char **argv)
assembler.symbol_export = true;// sym_file!=nullptr; assembler.symbol_export = true;// sym_file!=nullptr;
assembler.Assemble(strref(buffer, strl_t(size)), strref(argv[1]), obj_out_file != nullptr); assembler.Assemble(strref(buffer, strl_t(size)), strref(argv[1]), obj_out_file != nullptr);
if (assembler.list_assembly)
assembler.List(list_file);
if (assembler.errorEncountered) if (assembler.errorEncountered)
return_value = 1; return_value = 1;
else { else {
@ -3985,7 +4187,7 @@ int main(int argc, char **argv)
if (obj_out_file) if (obj_out_file)
assembler.WriteObjectFile(obj_out_file); assembler.WriteObjectFile(obj_out_file);
Section exportSec = assembler.ExportSection(); Section &exportSec = assembler.ExportSection();
if (info) { if (info) {
printf("SECTIONS SUMMARY\n================\n"); printf("SECTIONS SUMMARY\n================\n");