1
0
mirror of https://github.com/ksherlock/x65.git synced 2024-09-29 13:54:54 +00:00

65C02 CPU added

- Added SAV as a Merlin directive
- cpu command line option to switch CPU target
This commit is contained in:
Carl-Henrik Skårstedt 2015-10-21 22:34:01 -07:00
parent fbade4eb06
commit cc99faed16
2 changed files with 290 additions and 93 deletions

View File

@ -48,6 +48,7 @@ x65 [-DLabel] [-iIncDir] (source.s) (dest.prg) [-lst[=file.lst]] [-opcodes[=file
x65 filename.s code.prg [options] x65 filename.s code.prg [options]
* -i(path): Add include path * -i(path): Add include path
* -D(label)[=(value)]: Define a label with an optional value (otherwise defined as 1) * -D(label)[=(value)]: Define a label with an optional value (otherwise defined as 1)
* -cpu=6502/65c02: assemble with opcodes for a different cpu
* -obj (file.x65): generate object file for later linking * -obj (file.x65): generate object file for later linking
* -bin: Raw binary * -bin: Raw binary
* -c64: Include load address (default) * -c64: Include load address (default)
@ -516,6 +517,10 @@ A variation of **INCLUDE** that applies an oddball set of filename rules. These
In Merlin USR calls a function at a fixed address in memory, x65 safely avoids this. If there is a requirement for a user defined macro you've got the source code to do it in. In Merlin USR calls a function at a fixed address in memory, x65 safely avoids this. If there is a requirement for a user defined macro you've got the source code to do it in.
**SAV**
SAV causes Merlin to save the result it has generated so far, which is somewhat similar to the [EXPORT](#export) directive. If the SAV name is different than the source name the section will have a different EXPORT name appended and exported to a separate binary file.
## <a name="expressions">Expression syntax ## <a name="expressions">Expression syntax
Expressions contain values, such as labels or raw numbers and operators including +, -, \*, /, & (and), | (or), ^ (eor), << (shift left), >> (shift right) similar to how expressions work in C. Parenthesis are supported for managing order of operations where C style precedence needs to be overrided. In addition there are some special characters supported: Expressions contain values, such as labels or raw numbers and operators including +, -, \*, /, & (and), | (or), ^ (eor), << (shift left), >> (shift right) similar to how expressions work in C. Parenthesis are supported for managing order of operations where C style precedence needs to be overrided. In addition there are some special characters supported:
@ -636,7 +641,7 @@ FindFirstSpace
Currently the assembler is in an early revision and while features are tested individually it is fairly certain that untested combinations of features will indicate flaws and certain features are not in a complete state. Currently the assembler is in an early revision and while features are tested individually it is fairly certain that untested combinations of features will indicate flaws and certain features are not in a complete state.
**TODO** **TODO**
* 65c02 * 65C02 enabled through directives (PROCESSOR/CPU/XC)
* 65816 * 65816
* Macro parameters should replace only whole words instead of any substring * Macro parameters should replace only whole words instead of any substring
* Add 'import' directive as a catch-all include/incbin/etc. alternative * Add 'import' directive as a catch-all include/incbin/etc. alternative
@ -644,6 +649,7 @@ Currently the assembler is in an early revision and while features are tested in
* boolean operators (==, <, >, etc.) for better conditional expressions * boolean operators (==, <, >, etc.) for better conditional expressions
**FIXED** **FIXED**
* 65c02 (currently only through command line, not as a directive)
* Now accepts negative numbers, Merlin LUP and MAC keyword support * Now accepts negative numbers, Merlin LUP and MAC keyword support
* Merlin syntax fixes (no '!' in labels, don't skip ':' if first character of label), symbol file fix for included object files with resolved labels for relative sections. List output won't disassemble lines that wasn't built from source code. * Merlin syntax fixes (no '!' in labels, don't skip ':' if first character of label), symbol file fix for included object files with resolved labels for relative sections. List output won't disassemble lines that wasn't built from source code.
* Export full memory of fixed sections instead of a single section * Export full memory of fixed sections instead of a single section
@ -668,18 +674,9 @@ Currently the assembler is in an early revision and while features are tested in
* TEXT directive converts ascii to petscii (respect uppercase or lowercase petscii) (simplistic) * TEXT directive converts ascii to petscii (respect uppercase or lowercase petscii) (simplistic)
Revisions: Revisions:
* 2015-10-20 Fixed negative numbers, Merlin macros and 'endmacro' alternative, merlin LUP directive * 6 - 65C02 support
* 2015-10-18 Fixed exporting binary files * 5 - Merlin syntax
* 2015-10-18 Added list file output which is disassembly with inline source that generated the code. * 4 - Object files, relative sections and linking
* 2015-10-16 XDEF and file protected symbols added for better recursive object file assembling * 3 - 6502 full support
* 2015-10-15 Object file reading, additional bugs debugged. * 2 - Moved file out of struse samples
* 2015-10-10 Relative Sections and Link support, adding -merlin command line to clean up code * 1 - Built a sample of a simple assembler within struse
* 2015-10-06 Added ENUM and MERLIN / LISA assembler directives (EJECT, DUM, DEND, DS, DB, DFB, DDB, IF, ENDIF, etc.)
* 2015-10-05 Added INCDIR, some command line options (-D, -i, -vice)
* 2015-10-04 Added [REPT](#rept) directive
* 2015-10-04 Added [STRUCT](#struct) directive, sorted functions by grouping a bit more, bug fixes
* 2015-10-02 Cleanup hid an error (#else without #if), exit with nonzero if error was encountered
* 2015-10-02 General cleanup, wrapping [conditional assembly](#conditional) in functions
* 2015-10-01 Added [Label Pools](#pool) and conditional assembly
* 2015-09-29 Moved Asm6502 out of Struse Samples.
* 2015-09-28 First commit

354
x65.cpp
View File

@ -225,6 +225,7 @@ enum AssemblerDirective {
AD_DUMMY_END, // DEND: End a dummy section AD_DUMMY_END, // DEND: End a dummy section
AD_DS, // DS: Define section, zero out # bytes or rewind the address if negative AD_DS, // DS: Define section, zero out # bytes or rewind the address if negative
AD_USR, // USR: MERLIN user defined pseudo op, runs some code at a hard coded address on apple II, on PC does nothing. AD_USR, // USR: MERLIN user defined pseudo op, runs some code at a hard coded address on apple II, on PC does nothing.
AD_SAV, // SAV: MERLIN version of export but contains full filename, not an appendable name
}; };
// Operators are either instructions or directives // Operators are either instructions or directives
@ -264,7 +265,8 @@ typedef struct {
} OP_ID; } OP_ID;
enum AddrMode { enum AddrMode {
AMB_ZP_REL_X, // 0 ($12,x) address mode bit index // address mode bit index
AMB_ZP_REL_X, // 0 ($12,x)
AMB_ZP, // 1 $12 AMB_ZP, // 1 $12
AMB_IMM, // 2 #$12 AMB_IMM, // 2 #$12
AMB_ABS, // 3 $1234 AMB_ABS, // 3 $1234
@ -282,7 +284,7 @@ enum AddrMode {
AMB_FLIPXY = AMB_COUNT, // e AMB_FLIPXY = AMB_COUNT, // e
AMB_BRANCH, // f AMB_BRANCH, // f
// address mode masks // address mode masks
AMM_NON = 1<<AMB_NON, AMM_NON = 1<<AMB_NON,
AMM_IMM = 1<<AMB_IMM, AMM_IMM = 1<<AMB_IMM,
AMM_ABS = 1<<AMB_ABS, AMM_ABS = 1<<AMB_ABS,
@ -294,22 +296,36 @@ enum AddrMode {
AMM_ZP_X = 1<<AMB_ZP_X, AMM_ZP_X = 1<<AMB_ZP_X,
AMM_ZP_REL_X = 1<<AMB_ZP_REL_X, AMM_ZP_REL_X = 1<<AMB_ZP_REL_X,
AMM_ZP_Y_REL = 1<<AMB_ZP_Y_REL, AMM_ZP_Y_REL = 1<<AMB_ZP_Y_REL,
AMM_ZP_REL = 1<<AMB_ZP_REL, // b ($12)
AMM_REL_X = 1<<AMB_REL_X, // c ($1234,x)
AMM_ZP_ABS = 1<<AMB_ZP_ABS, // d $12, *+$12
AMM_FLIPXY = 1<<AMB_FLIPXY, AMM_FLIPXY = 1<<AMB_FLIPXY,
AMM_BRANCH = 1<<AMB_BRANCH, AMM_BRANCH = 1<<AMB_BRANCH,
// instruction group specific masks // instruction group specific masks
AMM_BRA = AMM_BRANCH | AMM_ABS, 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_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_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_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_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_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_STY = AMM_ZP | AMM_ZP_X | AMM_ABS,
AMM_LDY = AMM_IMM | AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_X, // note: for x ,x/,y flipped for this instr. AMM_LDY = AMM_IMM | AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_X,
AMM_DEC = AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_X, AMM_DEC = AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_X,
AMM_BIT = AMM_ZP | AMM_ABS, AMM_BIT = AMM_ZP | AMM_ABS,
AMM_JMP = AMM_ABS | AMM_REL, AMM_JMP = AMM_ABS | AMM_REL,
AMM_CPY = AMM_IMM | AMM_ZP | AMM_ABS, AMM_CPY = AMM_IMM | AMM_ZP | AMM_ABS,
// 65C02 groups
AMC_ORA = AMM_ORA | AMM_ZP_REL,
AMC_STA = AMM_STA | AMM_ZP_REL,
AMC_BIT = AMM_BIT | AMM_IMM | AMM_ZP_X | AMM_ABS_X,
AMC_DEC = AMM_DEC | AMM_NON | AMM_ACC,
AMC_JMP = AMM_JMP | AMM_REL_X,
AMC_STZ = AMM_ZP | AMM_ZP_X | AMM_ABS | AMM_ABS_X,
AMC_TRB = AMM_ZP | AMM_ABS,
AMC_BBR = AMM_ZP_ABS,
}; };
struct mnem { struct mnem {
@ -381,6 +397,100 @@ struct mnem opcodes_6502[] = {
static const int num_opcodes_6502 = sizeof(opcodes_6502) / sizeof(opcodes_6502[0]); static const int num_opcodes_6502 = sizeof(opcodes_6502) / sizeof(opcodes_6502[0]);
struct mnem opcodes_65C02[] = {
// nam modes (zp,x) zp # $0000 (zp),y zp,x abs,y abs,x (xx) A empty (zp)(abs,x)zp,abs
{ "brk", AMM_NON, { 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00 } },
{ "rti", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00 } },
{ "rts", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00 } },
{ "ora", AMC_ORA, { 0x01, 0x05, 0x09, 0x0d, 0x11, 0x15, 0x19, 0x1d, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00 } },
{ "and", AMC_ORA, { 0x21, 0x25, 0x29, 0x2d, 0x31, 0x35, 0x39, 0x3d, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00 } },
{ "eor", AMC_ORA, { 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00 } },
{ "adc", AMC_ORA, { 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00 } },
{ "sta", AMC_STA, { 0x81, 0x85, 0x00, 0x8d, 0x91, 0x95, 0x99, 0x9d, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00 } },
{ "lda", AMC_ORA, { 0xa1, 0xa5, 0xa9, 0xad, 0xb1, 0xb5, 0xb9, 0xbd, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00 } },
{ "cmp", AMC_ORA, { 0xc1, 0xc5, 0xc9, 0xcd, 0xd1, 0xd5, 0xd9, 0xdd, 0x00, 0x00, 0x00, 0xd2, 0x00, 0x00 } },
{ "sbc", AMC_ORA, { 0xe1, 0xe5, 0xe9, 0xed, 0xf1, 0xf5, 0xf9, 0xfd, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x00 } },
{ "asl", AMM_ASL, { 0x00, 0x06, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x1e, 0x00, 0x0a, 0x0a, 0x00, 0x00, 0x00 } },
{ "rol", AMM_ASL, { 0x00, 0x26, 0x00, 0x2e, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x2a, 0x2a, 0x00, 0x00, 0x00 } },
{ "lsr", AMM_ASL, { 0x00, 0x46, 0x00, 0x4e, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x4a, 0x4a, 0x00, 0x00, 0x00 } },
{ "ror", AMM_ASL, { 0x00, 0x66, 0x00, 0x6e, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x6a, 0x6a, 0x00, 0x00, 0x00 } },
{ "stx", AMM_STX, { 0x00, 0x86, 0x00, 0x8e, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "ldx", AMM_LDX, { 0x00, 0xa6, 0xa2, 0xae, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "dec", AMC_DEC, { 0x00, 0xc6, 0x00, 0xce, 0x00, 0xd6, 0x00, 0xde, 0x00, 0x3a, 0x3a, 0x00, 0x00, 0x00 } },
{ "inc", AMC_DEC, { 0x00, 0xe6, 0x00, 0xee, 0x00, 0xf6, 0x00, 0xfe, 0x00, 0x1a, 0x1a, 0x00, 0x00, 0x00 } },
{ "php", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 } },
{ "plp", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00 } },
{ "pha", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00 } },
{ "pla", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00 } },
{ "phy", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00 } },
{ "ply", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00 } },
{ "phx", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00 } },
{ "plx", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00 } },
{ "dey", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00 } },
{ "tay", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00 } },
{ "iny", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00 } },
{ "inx", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00 } },
// nam modes (zp,x) zp # $0000 (zp),y zp,x abs,y abs,x (xx) A empty (zp)(abs,x)zp,abs
{ "bpl", AMM_BRA, { 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bmi", AMM_BRA, { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bvc", AMM_BRA, { 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bvs", AMM_BRA, { 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bra", AMM_BRA, { 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bcc", AMM_BRA, { 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bcs", AMM_BRA, { 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "bne", AMM_BRA, { 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "beq", AMM_BRA, { 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "clc", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00 } },
{ "sec", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00 } },
{ "cli", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00 } },
{ "sei", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00 } },
{ "tya", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00 } },
{ "clv", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00 } },
{ "cld", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00 } },
{ "sed", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00 } },
{ "bit", AMC_BIT, { 0x00, 0x24, 0x89, 0x2c, 0x00, 0x34, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "stz", AMC_STZ, { 0x00, 0x64, 0x00, 0x9c, 0x00, 0x74, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "trb", AMC_TRB, { 0x00, 0x14, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "tsb", AMC_TRB, { 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "jmp", AMC_JMP, { 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x7c, 0x00 } },
{ "sty", AMM_STY, { 0x00, 0x84, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "ldy", AMM_LDY, { 0x00, 0xa4, 0xa0, 0xac, 0x00, 0xb4, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "cpy", AMM_CPY, { 0x00, 0xc4, 0xc0, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "cpx", AMM_CPY, { 0x00, 0xe4, 0xe0, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ "txa", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00 } },
{ "txs", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00 } },
{ "tax", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00 } },
{ "tsx", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00 } },
{ "dex", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00 } },
{ "nop", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00 } },
// WDC specific
// nam modes (zp,x) zp # $0000 (zp),y zp,x abs,y abs,x (xx) A empty (zp)(abs,x)zp,abs
{"bbr0", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f } },
{"bbr1", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f } },
{"bbr2", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f } },
{"bbr3", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f } },
{"bbr4", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f } },
{"bbr5", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f } },
{"bbr6", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f } },
{"bbr7", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f } },
{"bbs0", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f } },
{"bbs1", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f } },
{"bbs2", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf } },
{"bbs3", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf } },
{"bbs4", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf } },
{"bbs5", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf } },
{"bbs6", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef } },
{"bbs7", AMC_BBR, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0xff } },
{ "stp", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00 } },
{ "wai", AMM_NON, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00 } },
};
static const int num_opcodes_65C02 = sizeof(opcodes_65C02) / sizeof(opcodes_65C02[0]);
// 65C02 // 65C02
// http://6502.org/tutorials/65c02opcodes.html // http://6502.org/tutorials/65c02opcodes.html
// http://www.oxyron.de/html/opcodesc02.html // http://www.oxyron.de/html/opcodesc02.html
@ -394,7 +504,8 @@ enum CODE_ARG {
CA_NONE, // single byte instruction CA_NONE, // single byte instruction
CA_ONE_BYTE, // instruction carries one byte CA_ONE_BYTE, // instruction carries one byte
CA_TWO_BYTES, // instruction carries two bytes CA_TWO_BYTES, // instruction carries two bytes
CA_BRANCH // instruction carries a relative address CA_BRANCH, // instruction carries a relative address
CA_BYTE_BRANCH // instruction carries one byte and one branch
}; };
// hardtexted strings // hardtexted strings
@ -409,17 +520,21 @@ 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[] = { static const char* aAddrModeFmt[] = {
"%s ($%02x,x)", "%s ($%02x,x)",
"%s $%02x", "%s $%02x",
"%s #$%02x", "%s #$%02x",
"%s $%04x", "%s $%04x",
"%s ($%02x),y", "%s ($%02x),y",
"%s $%02x,x", "%s $%02x,x",
"%s $%04x,y", "%s $%04x,y",
"%s $%04x,x", "%s $%04x,x",
"%s ($%04x)", "%s ($%04x)",
"%s A", "%s A",
"%s " }; "%s ",
"%s ($%02x)",
"%s ($%04x,x)",
"%s $%02x, $%04x",
};
// 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
@ -794,6 +909,10 @@ public:
std::vector<Section> allSections; std::vector<Section> allSections;
std::vector<ExtLabels> externals; // external labels organized by object file std::vector<ExtLabels> externals; // external labels organized by object file
MapSymbolArray map; MapSymbolArray map;
// CPU target
struct mnem *opcode_table;
int opcode_count;
// context for macros / include files // context for macros / include files
ContextStack contextStack; ContextStack contextStack;
@ -819,6 +938,7 @@ public:
int lastEvalValue; int lastEvalValue;
Reloc::Type lastEvalPart; Reloc::Type lastEvalPart;
strref export_base_name;
strref last_label; strref last_label;
bool errorEncountered; bool errorEncountered;
bool list_assembly; bool list_assembly;
@ -889,7 +1009,7 @@ public:
// Manage locals // Manage locals
void MarkLabelLocal(strref label, bool scope_label = false); void MarkLabelLocal(strref label, bool scope_label = false);
void FlushLocalLabels(int scope_exit = -1); StatusCode FlushLocalLabels(int scope_exit = -1);
// Label pools // Label pools
LabelPool* GetLabelPool(strref pool_name); LabelPool* GetLabelPool(strref pool_name);
@ -940,7 +1060,8 @@ public:
char* LoadBinary(strref filename, size_t &size); char* LoadBinary(strref filename, size_t &size);
// constructor // constructor
Asm() { Cleanup(); localLabels.reserve(256); loadedData.reserve(16); lateEval.reserve(64); } Asm() : opcode_table(opcodes_6502), opcode_count(num_opcodes_6502) {
Cleanup(); localLabels.reserve(256); loadedData.reserve(16); lateEval.reserve(64); }
}; };
// Clean up work allocations // Clean up work allocations
@ -1554,14 +1675,12 @@ StatusCode Asm::BuildMacro(Macro &m, strref arg_list)
macexp.replace(param, a); macexp.replace(param, a);
} }
contextStack.push(m.source_name, macexp.get_strref(), macexp.get_strref()); contextStack.push(m.source_name, macexp.get_strref(), macexp.get_strref());
FlushLocalLabels(); return FlushLocalLabels();
return STATUS_OK;
} else } else
return ERROR_OUT_OF_MEMORY_FOR_MACRO_EXPANSION; return ERROR_OUT_OF_MEMORY_FOR_MACRO_EXPANSION;
} }
contextStack.push(m.source_name, m.source_file, macro_src); contextStack.push(m.source_name, m.source_file, macro_src);
FlushLocalLabels(); return FlushLocalLabels();
return STATUS_OK;
} }
@ -2153,9 +2272,10 @@ StatusCode Asm::CheckLateEval(strref added_label, int scope_end)
switch (i->type) { switch (i->type) {
case LateEval::LET_BRANCH: case LateEval::LET_BRANCH:
value -= i->address+1; value -= i->address+1;
if (value<-128 || value>127) if (value<-128 || value>127) {
i = lateEval.erase(i);
return ERROR_BRANCH_OUT_OF_RANGE; return ERROR_BRANCH_OUT_OF_RANGE;
if (trg >= allSections[sec].size()) } if (trg >= allSections[sec].size())
return ERROR_SECTION_TARGET_OFFSET_OUT_OF_RANGE; return ERROR_SECTION_TARGET_OFFSET_OUT_OF_RANGE;
allSections[sec].SetByte(trg, value); allSections[sec].SetByte(trg, value);
break; break;
@ -2288,8 +2408,9 @@ void Asm::MarkLabelLocal(strref label, bool scope_reserve)
} }
// find all local labels or up to given scope level and remove them // find all local labels or up to given scope level and remove them
void Asm::FlushLocalLabels(int scope_exit) StatusCode Asm::FlushLocalLabels(int scope_exit)
{ {
StatusCode status = STATUS_OK;
// iterate from end of local label records and early out if the label scope is lower than the current. // iterate from end of local label records and early out if the label scope is lower than the current.
std::vector<LocalLabelRecord>::iterator i = localLabels.end(); std::vector<LocalLabelRecord>::iterator i = localLabels.end();
while (i!=localLabels.begin()) { while (i!=localLabels.begin()) {
@ -2297,6 +2418,9 @@ void Asm::FlushLocalLabels(int scope_exit)
if (i->scope_depth < scope_depth) if (i->scope_depth < scope_depth)
break; break;
strref label = i->label; strref label = i->label;
StatusCode this_status = CheckLateEval(label);
if (this_status>FIRST_ERROR)
status = this_status;
if (!i->scope_reserve || i->scope_depth<=scope_exit) { if (!i->scope_reserve || i->scope_depth<=scope_exit) {
unsigned int index = FindLabelIndex(label.fnv1a(), labels.getKeys(), labels.count()); unsigned int index = FindLabelIndex(label.fnv1a(), labels.getKeys(), labels.count());
while (index<labels.count()) { while (index<labels.count()) {
@ -2315,6 +2439,7 @@ void Asm::FlushLocalLabels(int scope_exit)
i = localLabels.erase(i); i = localLabels.erase(i);
} }
} }
return status;
} }
// Get a label pool by name // Get a label pool by name
@ -2549,6 +2674,7 @@ StatusCode Asm::AssignLabel(strref label, strref line, bool make_constant)
// Adding a fixed address label // Adding a fixed address label
StatusCode Asm::AddressLabel(strref label) StatusCode Asm::AddressLabel(strref label)
{ {
StatusCode status = STATUS_OK;
Label *pLabel = GetLabel(label); Label *pLabel = GetLabel(label);
bool constLabel = false; bool constLabel = false;
if (!pLabel) if (!pLabel)
@ -2571,9 +2697,13 @@ StatusCode Asm::AddressLabel(strref label)
LabelAdded(pLabel, local); LabelAdded(pLabel, local);
if (local) if (local)
MarkLabelLocal(label); MarkLabelLocal(label);
else if (label[0]!=']') // MERLIN: Variable label does not invalidate local labels status = CheckLateEval(label);
FlushLocalLabels(); if (!local && label[0]!=']') { // MERLIN: Variable label does not invalidate local labels
return CheckLateEval(label); StatusCode this_status = FlushLocalLabels();
if (status<FIRST_ERROR && this_status>=FIRST_ERROR)
status = this_status;
}
return status;
} }
// include symbols listed from a .sym file or all if no listing // include symbols listed from a .sym file or all if no listing
@ -2822,6 +2952,7 @@ DirectiveName aDirectiveNames[] {
{ "DS", AD_DS }, // MERLIN { "DS", AD_DS }, // MERLIN
{ "LUP", AD_REPT }, // MERLIN { "LUP", AD_REPT }, // MERLIN
{ "MAC", AD_MACRO }, // MERLIN { "MAC", AD_MACRO }, // MERLIN
{ "SAV", AD_SAV }, // MERLIN
}; };
static const int nDirectiveNames = sizeof(aDirectiveNames) / sizeof(aDirectiveNames[0]); static const int nDirectiveNames = sizeof(aDirectiveNames) / sizeof(aDirectiveNames[0]);
@ -3121,6 +3252,13 @@ StatusCode Asm::ApplyDirective(AssemblerDirective dir, strref line, strref sourc
case AD_USR: case AD_USR:
line.clear(); line.clear();
break; break;
case AD_SAV:
line.trim_whitespace();
if (line.has_prefix(export_base_name))
line.skip(export_base_name.get_len());
if (line)
CurrSection().export_append = line.split_label();
break;
case AD_TEXT: { // text: add text within quotes case AD_TEXT: { // text: add text within quotes
// for now just copy the windows ascii. TODO: Convert to petscii. // for now just copy the windows ascii. TODO: Convert to petscii.
// https://en.wikipedia.org/wiki/PETSCII // https://en.wikipedia.org/wiki/PETSCII
@ -3349,13 +3487,13 @@ 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, int maxInstructions) int BuildInstructionTable(OP_ID *pInstr, int maxInstructions, struct mnem *opcodes, int count)
{ {
// create an instruction table (mnemonic hash lookup) // create an instruction table (mnemonic hash lookup)
int numInstructions = 0; int numInstructions = 0;
for (int i = 0; i < num_opcodes_6502; i++) { for (int i = 0; i < count; i++) {
OP_ID &op = pInstr[numInstructions++]; OP_ID &op = pInstr[numInstructions++];
op.op_hash = strref(opcodes_6502[i].instr).fnv1a_lower(); op.op_hash = strref(opcodes[i].instr).fnv1a_lower();
op.index = i; op.index = i;
op.type = OT_MNEMONIC; op.type = OT_MNEMONIC;
} }
@ -3364,7 +3502,6 @@ int BuildInstructionTable(OP_ID *pInstr, int maxInstructions)
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.index = (unsigned char)aDirectiveNames[d].directive; op_hash.index = (unsigned char)aDirectiveNames[d].directive;
op_hash.type = OT_DIRECTIVE; op_hash.type = OT_DIRECTIVE;
} }
@ -3446,10 +3583,30 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
{ {
StatusCode error = STATUS_OK; StatusCode error = STATUS_OK;
strref expression; strref expression;
// allowed modes
unsigned int validModes = opcode_table[index].modes;
// Get the addressing mode and the expression it refers to // Get the addressing mode and the expression it refers to
AddrMode addrMode = GetAddressMode(line, AddrMode addrMode;
!!(opcodes_6502[index].modes & AMM_FLIPXY), error, expression); switch (validModes) {
case AMC_BBR:
addrMode = AMB_ZP_ABS;
expression = line.split_token_trim(',');
if (!expression || !line)
return ERROR_INVALID_ADDRESSING_MODE;
break;
case AMM_BRA:
addrMode = AMB_ABS;
expression = line;
break;
case AMM_NON:
addrMode = AMB_NON;
break;
default:
addrMode = GetAddressMode(line, !!(validModes & AMM_FLIPXY), error, expression);
break;
}
int value = 0; int value = 0;
int target_section = -1; int target_section = -1;
@ -3458,7 +3615,7 @@ StatusCode Asm::AddOpcode(strref line, 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,
!!(opcodes_6502[index].modes & AMM_BRA) ? SectionId() : -1); !!(validModes & AMM_BRANCH) ? 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;
@ -3475,11 +3632,11 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
if (!evalLater && value>=0 && value<0x100 && error != STATUS_RELATIVE_SECTION) { if (!evalLater && value>=0 && value<0x100 && error != STATUS_RELATIVE_SECTION) {
switch (addrMode) { switch (addrMode) {
case AMB_ABS: case AMB_ABS:
if (opcodes_6502[index].modes & AMM_ZP) if (validModes & AMM_ZP)
addrMode = AMB_ZP; addrMode = AMB_ZP;
break; break;
case AMB_ABS_X: case AMB_ABS_X:
if (opcodes_6502[index].modes & AMM_ZP_X) if (validModes & AMM_ZP_X)
addrMode = AMB_ZP_X; addrMode = AMB_ZP_X;
break; break;
default: default:
@ -3487,35 +3644,61 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
} }
} }
CODE_ARG codeArg = CA_NONE; bool valid_addressing_mode = !!(validModes & (1 << addrMode));
unsigned char opcode = opcodes_6502[index].aCodes[addrMode];
if (!(opcodes_6502[index].modes & (1 << addrMode))) if (!valid_addressing_mode) {
error = ERROR_INVALID_ADDRESSING_MODE; if (addrMode==AMB_ZP_REL_X && (validModes & AMM_REL_X)) {
else { addrMode = AMB_REL_X;
if (opcodes_6502[index].modes & AMM_BRANCH) valid_addressing_mode = true;
} else if (addrMode==AMB_REL && (validModes & AMM_ZP_REL)) {
addrMode = AMB_ZP_REL;
valid_addressing_mode = true;
} else
error = ERROR_INVALID_ADDRESSING_MODE;
}
// Add the instruction and argument to the code
if (error == STATUS_OK || error == STATUS_RELATIVE_SECTION) {
unsigned char opcode = opcode_table[index].aCodes[addrMode];
CheckOutputCapacity(4);
AddByte(opcode);
CODE_ARG codeArg = CA_NONE;
if (validModes & AMM_BRANCH)
codeArg = CA_BRANCH; codeArg = CA_BRANCH;
else if (addrMode == AMB_ABS || addrMode == AMB_REL || addrMode == AMB_ABS_X || addrMode == AMB_ABS_Y) else if (addrMode == AMB_ABS || addrMode == AMB_REL || addrMode == AMB_ABS_X || addrMode == AMB_ABS_Y)
codeArg = CA_TWO_BYTES; codeArg = CA_TWO_BYTES;
else if (addrMode == AMB_ZP_ABS)
codeArg = CA_BYTE_BRANCH;
else if (addrMode != AMB_NON && addrMode != AMB_ACC) else if (addrMode != AMB_NON && addrMode != AMB_ACC)
codeArg = CA_ONE_BYTE; codeArg = CA_ONE_BYTE;
}
// Add the instruction and argument to the code
if (error == STATUS_OK || error == STATUS_RELATIVE_SECTION) {
CheckOutputCapacity(4);
switch (codeArg) { switch (codeArg) {
case CA_BYTE_BRANCH: {
if (evalLater)
AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_BYTE);
else if (error == STATUS_RELATIVE_SECTION) {
CurrSection().AddReloc(target_section_offs, CurrSection().DataOffset(), target_section,
target_section_type == Reloc::HI_BYTE ? Reloc::HI_BYTE : Reloc::LO_BYTE);
}
AddByte(value);
struct EvalContext etx(CurrSection().GetPC()-2, scope_address[scope_depth], -1, SectionId());
error = EvalExpression(line, etx, value);
if (error==STATUS_NOT_READY)
AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], line, source_file, LateEval::LET_BRANCH);
else if (((int)value - (int)CurrSection().GetPC() - 1) < -128 || ((int)value - (int)CurrSection().GetPC() - 1) > 127)
error = ERROR_BRANCH_OUT_OF_RANGE;
AddByte(error == STATUS_NOT_READY ? 0 : (unsigned char)((int)value - (int)CurrSection().GetPC()) - 1);
break;
}
case CA_BRANCH: case CA_BRANCH:
AddByte(opcode);
if (evalLater) if (evalLater)
AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_BRANCH); AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_BRANCH);
else if (((int)value - (int)CurrSection().GetPC()-1) < -128 || ((int)value - (int)CurrSection().GetPC()-1) > 127) { else if (((int)value - (int)CurrSection().GetPC()-1) < -128 || ((int)value - (int)CurrSection().GetPC()-1) > 127)
error = ERROR_BRANCH_OUT_OF_RANGE; error = ERROR_BRANCH_OUT_OF_RANGE;
break;
}
AddByte(evalLater ? 0 : (unsigned char)((int)value - (int)CurrSection().GetPC()) - 1); AddByte(evalLater ? 0 : (unsigned char)((int)value - (int)CurrSection().GetPC()) - 1);
break; break;
case CA_ONE_BYTE: case CA_ONE_BYTE:
AddByte(opcode);
if (evalLater) if (evalLater)
AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_BYTE); AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_BYTE);
else if (error == STATUS_RELATIVE_SECTION) else if (error == STATUS_RELATIVE_SECTION)
@ -3524,7 +3707,6 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
AddByte(value); AddByte(value);
break; break;
case CA_TWO_BYTES: case CA_TWO_BYTES:
AddByte(opcode);
if (evalLater) if (evalLater)
AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_ABS_REF); AddLateEval(CurrSection().DataOffset(), CurrSection().GetPC(), scope_address[scope_depth], expression, source_file, LateEval::LET_ABS_REF);
else if (error == STATUS_RELATIVE_SECTION) { else if (error == STATUS_RELATIVE_SECTION) {
@ -3535,7 +3717,6 @@ StatusCode Asm::AddOpcode(strref line, int index, strref source_file)
AddWord(value); AddWord(value);
break; break;
case CA_NONE: case CA_NONE:
AddByte(opcode);
break; break;
} }
} }
@ -3780,10 +3961,10 @@ bool Asm::List(strref filename)
unsigned char addrmode[256]; unsigned char addrmode[256];
memset(mnemonic, 255, sizeof(mnemonic)); memset(mnemonic, 255, sizeof(mnemonic));
memset(addrmode, 255, sizeof(addrmode)); memset(addrmode, 255, sizeof(addrmode));
for (int i = 0; i < num_opcodes_6502; i++) { for (int i = 0; i < opcode_count; i++) {
for (int j = 0; j < AMB_COUNT; j++) { for (int j = 0; j < AMB_COUNT; j++) {
if (opcodes_6502[i].modes & (1 << j)) { if (opcode_table[i].modes & (1 << j)) {
unsigned char op = opcodes_6502[i].aCodes[j]; unsigned char op = opcode_table[i].aCodes[j];
mnemonic[op] = i; mnemonic[op] = i;
addrmode[op] = j; addrmode[op] = j;
} }
@ -3835,23 +4016,25 @@ bool Asm::List(strref filename)
unsigned char *buf = si->output + lst.address; unsigned char *buf = si->output + lst.address;
unsigned char op = mnemonic[*buf]; unsigned char op = mnemonic[*buf];
unsigned char am = addrmode[*buf]; unsigned char am = addrmode[*buf];
if (op != 255 && am != 255) { if (op != 255 && am != 255 && am<(sizeof(aAddrModeFmt)/sizeof(aAddrModeFmt[0]))) {
const char *fmt = aAddrModeFmt[am]; const char *fmt = aAddrModeFmt[am];
if (opcodes_6502[op].modes & AMM_FLIPXY) { if (opcode_table[op].modes & AMM_FLIPXY) {
if (am == AMB_ZP_X) fmt = "%s $%02x,y"; if (am == AMB_ZP_X) fmt = "%s $%02x,y";
else if (am == AMB_ABS_X) fmt = "%s $%04x,y"; else if (am == AMB_ABS_X) fmt = "%s $%04x,y";
} }
if (opcodes_6502[op].modes & AMM_BRANCH) if (opcode_table[op].modes & AMM_ZP_ABS)
out.sprintf_append(fmt, opcodes_6502[op].instr, (char)buf[1] + lst.address + si->start_address + 2); out.sprintf_append(fmt, opcode_table[op].instr, buf[1], (char)buf[2] + lst.address + si->start_address + 3);
else if (opcode_table[op].modes & AMM_BRANCH)
out.sprintf_append(fmt, opcode_table[op].instr, (char)buf[1] + lst.address + si->start_address + 2);
else if (am == AMB_NON || am == AMB_ACC) else if (am == AMB_NON || am == AMB_ACC)
out.sprintf_append(fmt, opcodes_6502[op].instr); out.sprintf_append(fmt, opcode_table[op].instr);
else if (am == AMB_ABS || am == AMB_ABS_X || am == AMB_ABS_Y || am == AMB_REL) else if (am == AMB_ABS || am == AMB_ABS_X || am == AMB_ABS_Y || am == AMB_REL || am == AMB_REL_X)
out.sprintf_append(fmt, opcodes_6502[op].instr, buf[1] | (buf[2] << 8)); out.sprintf_append(fmt, opcode_table[op].instr, buf[1] | (buf[2] << 8));
else else
out.sprintf_append(fmt, opcodes_6502[op].instr, buf[1]); out.sprintf_append(fmt, opcode_table[op].instr, buf[1]);
} }
} }
out.append_to(' ', 30); out.append_to(' ', 33);
strref line = lst.code.get_skipped(lst.line_offs).get_line(); strref line = lst.code.get_skipped(lst.line_offs).get_line();
line.clip_trailing_whitespace(); line.clip_trailing_whitespace();
strown<128> line_fix(line); strown<128> line_fix(line);
@ -3881,21 +4064,24 @@ bool Asm::AllOpcodes(strref filename)
return false; return false;
opened = true; opened = true;
} }
for (int i = 0; i < num_opcodes_6502; i++) { for (int i = 0; i < opcode_count; i++) {
for (int a = 0; a < AMB_COUNT; a++) { for (int a = 0; a < AMB_COUNT; a++) {
if (opcodes_6502[i].modes & (1 << a)) { if (opcode_table[i].modes & (1 << a)) {
const char *fmt = aAddrModeFmt[a]; const char *fmt = aAddrModeFmt[a];
if (opcodes_6502[i].modes & AMM_BRANCH) fputs("\t", f);
fprintf(f, "%s *+%d", opcodes_6502[i].instr, 5); if (opcode_table[i].modes & AMM_BRANCH)
fprintf(f, "%s *+%d", opcode_table[i].instr, 5);
else if (a==AMB_ZP_ABS)
fprintf(f, "%s $%02x,*+%d", opcode_table[i].instr, 0x23, 13);
else { else {
if (opcodes_6502[i].modes & AMM_FLIPXY) { if (opcode_table[i].modes & AMM_FLIPXY) {
if (a == AMB_ZP_X) fmt = "%s $%02x,y"; if (a == AMB_ZP_X) fmt = "%s $%02x,y";
else if (a == AMB_ABS_X) fmt = "%s $%04x,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) if (a == AMB_ABS || a == AMB_ABS_X || a == AMB_ABS_Y || a == AMB_REL || a == AMB_REL_X)
fprintf(f, fmt, opcodes_6502[i].instr, 0x2120); fprintf(f, fmt, opcode_table[i].instr, 0x2120);
else else
fprintf(f, fmt, opcodes_6502[i].instr, 0x21, 0x20, 0x1f); fprintf(f, fmt, opcode_table[i].instr, 0x21, 0x20, 0x1f);
} }
fputs("\n", f); fputs("\n", f);
} }
@ -3910,7 +4096,7 @@ bool Asm::AllOpcodes(strref filename)
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, 256); int numInstructions = BuildInstructionTable(pInstr, 256, opcode_table, opcode_count);
StatusCode error = STATUS_OK; StatusCode error = STATUS_OK;
contextStack.push(filename, source, source); contextStack.push(filename, source, source);
@ -4365,6 +4551,7 @@ int main(int argc, char **argv)
const strref listing("lst"); const strref listing("lst");
const strref allinstr("opcodes"); const strref allinstr("opcodes");
const strref endmacro("endm"); const strref endmacro("endm");
const strref cpu("cpu");
int return_value = 0; int return_value = 0;
bool load_header = true; bool load_header = true;
bool size_header = false; bool size_header = false;
@ -4409,6 +4596,15 @@ int main(int argc, char **argv)
} else if (arg.has_prefix(allinstr) && (arg.get_len() == allinstr.get_len() || arg[allinstr.get_len()] == '=')) { } else if (arg.has_prefix(allinstr) && (arg.get_len() == allinstr.get_len() || arg[allinstr.get_len()] == '=')) {
gen_allinstr = true; gen_allinstr = true;
allinstr_file = arg.after('='); allinstr_file = arg.after('=');
} else if (arg.has_prefix(cpu) && (arg.get_len() == cpu.get_len() || arg[cpu.get_len()] == '=')) {
arg.split_token_trim('=');
if (arg.same_str("6502")) {
assembler.opcode_table = opcodes_6502;
assembler.opcode_count = num_opcodes_6502;
} else if (arg.same_str("65c02")) {
assembler.opcode_table = opcodes_65C02;
assembler.opcode_count = num_opcodes_65C02;
}
} else if (arg.same_str("sym") && (a + 1) < argc) } 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)
@ -4428,6 +4624,7 @@ int main(int argc, char **argv)
" x65 filename.s code.prg [options]\n" " x65 filename.s code.prg [options]\n"
" * -i(path) : Add include path\n" " * -i(path) : Add include path\n"
" * -D(label)[=<value>] : Define a label with an optional value(otherwise defined as 1)\n" " * -D(label)[=<value>] : Define a label with an optional value(otherwise defined as 1)\n"
" * -cpu=6502/65c02: assemble with opcodes for a different cpu\n"
" * -obj(file.x65) : generate object file for later linking\n" " * -obj(file.x65) : generate object file for later linking\n"
" * -bin : Raw binary\n" " * -bin : Raw binary\n"
" * -c64 : Include load address(default)\n" " * -c64 : Include load address(default)\n"
@ -4446,6 +4643,9 @@ int main(int argc, char **argv)
if (source_filename) { if (source_filename) {
size_t size = 0; size_t size = 0;
strref srcname(source_filename); strref srcname(source_filename);
assembler.export_base_name = strref(binary_out_name).after_last_or_full('/', '\\').before_or_full('.');
if (char *buffer = assembler.LoadText(srcname, size)) { if (char *buffer = assembler.LoadText(srcname, size)) {
// if source_filename contains a path add that as a search path for include files // if source_filename contains a path add that as a search path for include files
assembler.AddIncludeFolder(srcname.before_last('/', '\\')); assembler.AddIncludeFolder(srcname.before_last('/', '\\'));