acme/src/mnemo.c
marcobaye 7d4200faa4 step 2 to fix ALU_* calls
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@141 4df02467-bbd4-4a76-a152-e7ce94205b78
2020-05-08 00:34:46 +00:00

1281 lines
56 KiB
C

// ACME - a crossassembler for producing 6502/65c02/65816/65ce02 code.
// Copyright (C) 1998-2020 Marco Baye
// Have a look at "acme.c" for further info
//
// Mnemonics stuff
#include "mnemo.h"
#include "config.h"
#include "alu.h"
#include "cpu.h"
#include "dynabuf.h"
#include "global.h"
#include "input.h"
#include "output.h"
#include "tree.h"
#include "typesystem.h"
// Constants
#define s_ror (s_error + 2) // Yes, I know I'm sick
#define MNEMO_DYNABUF_INITIALSIZE 8 // 4 + terminator should suffice
// These values are needed to recognize addressing modes:
// indexing:
#define INDEX_NONE 0 // no index
#define INDEX_S 1 // stack-indexed (",s" or ",sp"), for 65816 and 65ce02
#define INDEX_X 2 // x-indexed (",x")
#define INDEX_Y 3 // y-indexed (",y")
#define INDEX_Z 4 // z-indexed (",z"), only for 65ce02
// 5..7 are left for future expansion, 8 would need the AMB_INDEX macro below to be adjusted!
// adress mode bits:
#define AMB_IMPLIED (1u << 0) // no value given
#define AMB_IMMEDIATE (1u << 1) // '#' at start
#define AMB_INDIRECT (1u << 2) // value has at least one unnecessary pair of "()"
#define AMB_LONGINDIRECT (1u << 3) // value is given in []
#define AMB_PREINDEX(idx) ((idx) << 4) // three bits for indexing inside ()
#define AMB_INDEX(idx) ((idx) << 7) // three bits for external indexing
// end values (here, "absolute addressing" always includes zeropage addressing, because they look the same)
#define IMPLIED_ADDRESSING AMB_IMPLIED
#define IMMEDIATE_ADDRESSING AMB_IMMEDIATE
#define ABSOLUTE_ADDRESSING 0
#define X_INDEXED_ADDRESSING AMB_INDEX(INDEX_X)
#define Y_INDEXED_ADDRESSING AMB_INDEX(INDEX_Y)
#define INDIRECT_ADDRESSING AMB_INDIRECT
#define X_INDEXED_INDIRECT_ADDRESSING (AMB_PREINDEX(INDEX_X) | AMB_INDIRECT)
#define INDIRECT_Y_INDEXED_ADDRESSING (AMB_INDIRECT | AMB_INDEX(INDEX_Y))
// only for 65ce02:
#define INDIRECT_Z_INDEXED_ADDRESSING (AMB_INDIRECT | AMB_INDEX(INDEX_Z))
// for 65816 and 65ce02:
#define STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING (AMB_PREINDEX(INDEX_S) | AMB_INDIRECT | AMB_INDEX(INDEX_Y))
// only for 65816:
#define STACK_INDEXED_ADDRESSING AMB_INDEX(INDEX_S)
#define LONG_INDIRECT_ADDRESSING AMB_LONGINDIRECT
#define LONG_INDIRECT_Y_INDEXED_ADDRESSING (AMB_LONGINDIRECT | AMB_INDEX(INDEX_Y))
// Constant values, used to mark the possible parameter lengths of commands.
// Not all of the eight possible combinations are actually used, however (because of the supported CPUs).
#define MAYBE______ 0
#define MAYBE_1____ NUMBER_FORCES_8
#define MAYBE___2__ NUMBER_FORCES_16
#define MAYBE_____3 NUMBER_FORCES_24
// The mnemonics are split up into groups, each group has its own function to be dealt with:
enum mnemogroup {
GROUP_ACCU, // main accumulator stuff, plus PEI Byte value = table index
GROUP_MISC, // read-modify-write and others Byte value = table index
GROUP_ALLJUMPS, // the jump instructions Byte value = table index
GROUP_IMPLIEDONLY, // mnemonics using only implied addressing Byte value = opcode
GROUP_RELATIVE8, // short branch instructions Byte value = opcode
GROUP_BITBRANCH, // bbr0..7 and bbs0..7 Byte value = opcode
GROUP_REL16_2, // 16bit relative to pc+2 Byte value = opcode
GROUP_REL16_3, // 16bit relative to pc+3 Byte value = opcode
GROUP_BOTHMOVES, // the "move" commands MVP and MVN Byte value = opcode
GROUP_ZPONLY // rmb0..7 and smb0..7 Byte value = opcode FIXME - use for IDX816COP,IDXeDEW,IDXeINW as well!
};
// save some space
#define SCB static const unsigned char
#define SCS static const unsigned short
#define SCL static const unsigned long
// Code tables for group GROUP_ACCU:
// These tables are used for the main accumulator-related mnemonics. By reading
// the mnemonic's byte value (from the mnemotable), the assembler finds out the
// column to use here. The row depends on the used addressing mode. A zero
// entry in these tables means that the combination of mnemonic and addressing
// mode is illegal.
// | 6502/65c02/65816/65ce02 | 65816 | 6510 illegals |
enum { IDX_ORA,IDXcORA,IDX816ORA,IDXeORA,IDX_AND,IDXcAND,IDX816AND,IDXeAND,IDX_EOR,IDXcEOR,IDX816EOR,IDXeEOR,IDX_ADC,IDXcADC,IDX816ADC,IDXeADC,IDX_STA,IDXcSTA,IDX816STA,IDXeSTA,IDX_LDA,IDXcLDA,IDX816LDA,IDXeLDA,IDX_CMP,IDXcCMP,IDX816CMP,IDXeCMP,IDX_SBC,IDXcSBC,IDX816SBC,IDXeSBC,IDX816PEI,IDX_SLO,IDX_RLA,IDX_SRE,IDX_RRA,IDX_SAX,IDX_LAX,IDX_DCP,IDX_ISC,IDX_SHA};
SCB accu_imm[] = { 0x09, 0x09, 0x09, 0x09, 0x29, 0x29, 0x29, 0x29, 0x49, 0x49, 0x49, 0x49, 0x69, 0x69, 0x69, 0x69, 0, 0, 0, 0, 0xa9, 0xa9, 0xa9, 0xa9, 0xc9, 0xc9, 0xc9, 0xc9, 0xe9, 0xe9, 0xe9, 0xe9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // #$ff #$ffff
SCL accu_abs[] = { 0x0d05, 0x0d05, 0x0f0d05, 0x0d05, 0x2d25, 0x2d25, 0x2f2d25, 0x2d25, 0x4d45, 0x4d45, 0x4f4d45, 0x4d45, 0x6d65, 0x6d65, 0x6f6d65, 0x6d65, 0x8d85, 0x8d85, 0x8f8d85, 0x8d85, 0xada5, 0xada5, 0xafada5, 0xada5, 0xcdc5, 0xcdc5, 0xcfcdc5, 0xcdc5, 0xede5, 0xede5, 0xefede5, 0xede5, 0, 0x0f07, 0x2f27, 0x4f47, 0x6f67, 0x8f87, 0xafa7, 0xcfc7, 0xefe7, 0}; // $ff $ffff $ffffff
SCL accu_xabs[] = { 0x1d15, 0x1d15, 0x1f1d15, 0x1d15, 0x3d35, 0x3d35, 0x3f3d35, 0x3d35, 0x5d55, 0x5d55, 0x5f5d55, 0x5d55, 0x7d75, 0x7d75, 0x7f7d75, 0x7d75, 0x9d95, 0x9d95, 0x9f9d95, 0x9d95, 0xbdb5, 0xbdb5, 0xbfbdb5, 0xbdb5, 0xddd5, 0xddd5, 0xdfddd5, 0xddd5, 0xfdf5, 0xfdf5, 0xfffdf5, 0xfdf5, 0, 0x1f17, 0x3f37, 0x5f57, 0x7f77, 0, 0, 0xdfd7, 0xfff7, 0}; // $ff,x $ffff,x $ffffff,x
SCS accu_yabs[] = { 0x1900, 0x1900, 0x1900, 0x1900, 0x3900, 0x3900, 0x3900, 0x3900, 0x5900, 0x5900, 0x5900, 0x5900, 0x7900, 0x7900, 0x7900, 0x7900, 0x9900, 0x9900, 0x9900, 0x9900, 0xb900, 0xb900, 0xb900, 0xb900, 0xd900, 0xd900, 0xd900, 0xd900, 0xf900, 0xf900, 0xf900, 0xf900, 0, 0x1b00, 0x3b00, 0x5b00, 0x7b00, 0x97, 0xbfb7, 0xdb00, 0xfb00, 0x9f00}; // $ff,y $ffff,y
SCB accu_xind8[] = { 0x01, 0x01, 0x01, 0x01, 0x21, 0x21, 0x21, 0x21, 0x41, 0x41, 0x41, 0x41, 0x61, 0x61, 0x61, 0x61, 0x81, 0x81, 0x81, 0x81, 0xa1, 0xa1, 0xa1, 0xa1, 0xc1, 0xc1, 0xc1, 0xc1, 0xe1, 0xe1, 0xe1, 0xe1, 0, 0x03, 0x23, 0x43, 0x63, 0x83, 0xa3, 0xc3, 0xe3, 0}; // ($ff,x)
SCB accu_indy8[] = { 0x11, 0x11, 0x11, 0x11, 0x31, 0x31, 0x31, 0x31, 0x51, 0x51, 0x51, 0x51, 0x71, 0x71, 0x71, 0x71, 0x91, 0x91, 0x91, 0x91, 0xb1, 0xb1, 0xb1, 0xb1, 0xd1, 0xd1, 0xd1, 0xd1, 0xf1, 0xf1, 0xf1, 0xf1, 0, 0x13, 0x33, 0x53, 0x73, 0, 0xb3, 0xd3, 0xf3, 0x93}; // ($ff),y
SCB accu_ind8[] = { 0, 0x12, 0x12, 0, 0, 0x32, 0x32, 0, 0, 0x52, 0x52, 0, 0, 0x72, 0x72, 0, 0, 0x92, 0x92, 0, 0, 0xb2, 0xb2, 0, 0, 0xd2, 0xd2, 0, 0, 0xf2, 0xf2, 0, 0xd4, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff)
SCB accu_sabs8[] = { 0, 0, 0x03, 0, 0, 0, 0x23, 0, 0, 0, 0x43, 0, 0, 0, 0x63, 0, 0, 0, 0x83, 0, 0, 0, 0xa3, 0, 0, 0, 0xc3, 0, 0, 0, 0xe3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff,s
SCB accu_sindy8[] = { 0, 0, 0x13, 0, 0, 0, 0x33, 0, 0, 0, 0x53, 0, 0, 0, 0x73, 0, 0, 0, 0x93, 0x82, 0, 0, 0xb3, 0xe2, 0, 0, 0xd3, 0, 0, 0, 0xf3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff,s),y
SCB accu_lind8[] = { 0, 0, 0x07, 0, 0, 0, 0x27, 0, 0, 0, 0x47, 0, 0, 0, 0x67, 0, 0, 0, 0x87, 0, 0, 0, 0xa7, 0, 0, 0, 0xc7, 0, 0, 0, 0xe7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff]
SCB accu_lindy8[] = { 0, 0, 0x17, 0, 0, 0, 0x37, 0, 0, 0, 0x57, 0, 0, 0, 0x77, 0, 0, 0, 0x97, 0, 0, 0, 0xb7, 0, 0, 0, 0xd7, 0, 0, 0, 0xf7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // [$ff],y
SCB accu_indz8[] = { 0, 0, 0, 0x12, 0, 0, 0, 0x32, 0, 0, 0, 0x52, 0, 0, 0, 0x72, 0, 0, 0, 0x92, 0, 0, 0, 0xb2, 0, 0, 0, 0xd2, 0, 0, 0, 0xf2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // ($ff),z
// Code tables for group GROUP_MISC:
// These tables are needed for finding out the correct code in cases when
// there are no general rules. By reading the mnemonic's byte value (from the
// mnemotable), the assembler finds out the column to use here. The row
// depends on the used addressing mode. A zero entry in these tables means
// that the combination of mnemonic and addressing mode is illegal.
// | 6502 | 6502/65c02/65ce02 | 65c02 | 65ce02 | 65816 | 6510 illegals | C64DTV2 |
enum { IDX_ASL,IDX_ROL,IDX_LSR,IDX_ROR,IDX_LDY,IDX_LDX,IDX_CPY,IDX_CPX,IDX_BIT,IDXcBIT,IDX_STX,IDXeSTX,IDX_STY,IDXeSTY,IDX_DEC,IDXcDEC,IDX_INC,IDXcINC,IDXcTSB,IDXcTRB,IDXcSTZ,IDXeASR,IDXeASW,IDXeCPZ,IDXeDEW,IDXeINW,IDXeLDZ,IDXePHW,IDXeROW,IDXeRTN,IDX816COP,IDX816REP,IDX816SEP,IDX816PEA,IDX_ANC,IDX_ASR,IDX_ARR,IDX_SBX,IDX_DOP,IDX_TOP,IDX_JAM,IDX_LXA,IDX_ANE,IDX_LAS,IDX_TAS,IDX_SHX,IDX_SHY,IDX_SAC,IDX_SIR};
SCB misc_impl[] = { 0x0a, 0x2a, 0x4a, 0x6a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3a, 0, 0x1a, 0, 0, 0, 0x43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x0c, 0x02, 0, 0, 0, 0, 0, 0, 0, 0}; // implied/accu
SCB misc_imm[] = { 0, 0, 0, 0, 0xa0, 0xa2, 0xc0, 0xe0, 0, 0x89, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc2, 0, 0, 0xa3, 0xf4, 0, 0x62, 0, 0xc2, 0xe2, 0, 0x0b, 0x4b, 0x6b, 0xcb, 0x80, 0, 0, 0xab, 0x8b, 0, 0, 0, 0, 0x32, 0x42}; // #$ff #$ffff
SCS misc_abs[] = { 0x0e06, 0x2e26, 0x4e46, 0x6e66, 0xaca4, 0xaea6, 0xccc4, 0xece4, 0x2c24, 0x2c24, 0x8e86, 0x8e86, 0x8c84, 0x8c84, 0xcec6, 0xcec6, 0xeee6, 0xeee6, 0x0c04, 0x1c14, 0x9c64, 0x44, 0xcb00, 0xdcd4, 0xc3, 0xe3, 0xab00, 0xfc00, 0xeb00, 0, 0x02, 0, 0, 0xf400, 0, 0, 0, 0, 0x04, 0x0c00, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // $ff $ffff
SCS misc_xabs[] = { 0x1e16, 0x3e36, 0x5e56, 0x7e76, 0xbcb4, 0, 0, 0, 0, 0x3c34, 0, 0, 0x94, 0x8b94, 0xded6, 0xded6, 0xfef6, 0xfef6, 0, 0, 0x9e74, 0x54, 0, 0, 0, 0, 0xbb00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x14, 0x1c00, 0, 0, 0, 0, 0, 0, 0x9c00, 0, 0}; // $ff,x $ffff,x
SCS misc_yabs[] = { 0, 0, 0, 0, 0, 0xbeb6, 0, 0, 0, 0, 0x96, 0x9b96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbb00, 0x9b00, 0x9e00, 0, 0, 0}; // $ff,y $ffff,y
// Code tables for group GROUP_ALLJUMPS:
// These tables are needed for finding out the correct code when the mnemonic
// is "jmp" or "jsr" (or the long versions "jml" and "jsl").
// By reading the mnemonic's byte value (from the mnemotable), the assembler
// finds out the column to use here. The row depends on the used addressing
// mode. A zero entry in these tables means that the combination of mnemonic
// and addressing mode is illegal.
// | 6502/65c02/65816/65ce02 | 65816 |
enum { IDX_JMP,IDXcJMP,IDX816JMP,IDX_JSR,IDXeJSR,IDX816JSR,IDX816JML,IDX816JSL};
SCL jump_abs[] = { 0x4c00, 0x4c00, 0x5c4c00, 0x2000, 0x2000, 0x222000, 0x5c0000, 0x220000}; // $ffff $ffffff
SCS jump_ind[] = { 0x6c00, 0x6c00, 0x6c00, 0, 0x2200, 0, 0, 0}; // ($ffff)
SCS jump_xind[] = { 0, 0x7c00, 0x7c00, 0, 0x2300, 0xfc00, 0, 0}; // ($ffff,x)
SCS jump_lind[] = { 0, 0, 0xdc00, 0, 0, 0, 0xdc00, 0}; // [$ffff]
#undef SCB
#undef SCS
#undef SCL
// error message strings
static const char exception_illegal_combination[] = "Illegal combination of command and addressing mode.";
static const char exception_oversized_addrmode[] = "Using oversized addressing mode.";
// Variables
static struct dynabuf *mnemo_dyna_buf; // dynamic buffer for mnemonics
// predefined stuff
static struct ronode *mnemo_6502_tree = NULL; // 6502 mnemonics
static struct ronode *mnemo_6502undoc1_tree = NULL; // 6502 undocumented ("illegal") opcodes supported by DTV2
static struct ronode *mnemo_6502undoc2_tree = NULL; // remaining 6502 undocumented ("illegal") opcodes (currently ANC only, maybe more will get moved)
static struct ronode *mnemo_c64dtv2_tree = NULL; // C64DTV2 extensions (BRA/SAC/SIR)
static struct ronode *mnemo_65c02_tree = NULL; // 65c02 extensions
static struct ronode *mnemo_bitmanips_tree = NULL; // Rockwell's bit manipulation extensions
static struct ronode *mnemo_stp_wai_tree = NULL; // WDC's "stp" and "wai" instructions
static struct ronode *mnemo_65816_tree = NULL; // WDC 65816 extensions
static struct ronode *mnemo_65ce02_tree = NULL; // CSG 65ce02/4502 extensions
static struct ronode *mnemo_aug_tree = NULL; // CSG 65ce02's "aug" instruction
static struct ronode *mnemo_map_eom_tree = NULL; // CSG 4502's "map" and "eom" instructions
// Command's code and group values are stored together in a single integer.
// To extract the code, use "& CODEMASK".
// To extract the immediate mode, use "& IMMASK".
// To extract the group, use GROUP()
#define CODEMASK 0x0ff // opcode or table index
// immediate mode:
#define IM_FORCE8 0x000 // immediate values are 8 bits (CAUTION - program relies on "no bits set" being the default!)
#define IM_FORCE16 0x100 // immediate value is 16 bits (for 65ce02's PHW#)
#define IM_ACCUMULATOR 0x200 // immediate value depends on accumulator length
#define IM_INDEXREGS 0x300 // immediate value depends on index register length
#define IMMASK 0x300 // mask for immediate modes
#define MERGE(g, v) (((g) << 10) | (v))
#define GROUP(v) ((v) >> 10)
static struct ronode mnemos_6502[] = {
PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX_ORA)),
PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDX_AND)),
PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDX_EOR)),
PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX_ADC)),
PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX_STA)),
PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX_LDA)),
PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDX_CMP)),
PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDX_SBC)),
PREDEFNODE("bit", MERGE(GROUP_MISC, IDX_BIT)),
PREDEFNODE(s_asl, MERGE(GROUP_MISC, IDX_ASL)),
PREDEFNODE("rol", MERGE(GROUP_MISC, IDX_ROL)),
PREDEFNODE(s_lsr, MERGE(GROUP_MISC, IDX_LSR)),
PREDEFNODE(s_ror, MERGE(GROUP_MISC, IDX_ROR)),
PREDEFNODE("sty", MERGE(GROUP_MISC, IDX_STY)),
PREDEFNODE("stx", MERGE(GROUP_MISC, IDX_STX)),
PREDEFNODE("ldy", MERGE(GROUP_MISC, IDX_LDY)),
PREDEFNODE("ldx", MERGE(GROUP_MISC, IDX_LDX)),
PREDEFNODE("cpy", MERGE(GROUP_MISC, IDX_CPY)),
PREDEFNODE("cpx", MERGE(GROUP_MISC, IDX_CPX)),
PREDEFNODE("dec", MERGE(GROUP_MISC, IDX_DEC)),
PREDEFNODE("inc", MERGE(GROUP_MISC, IDX_INC)),
PREDEFNODE("bpl", MERGE(GROUP_RELATIVE8, 0x10)),
PREDEFNODE("bmi", MERGE(GROUP_RELATIVE8, 0x30)),
PREDEFNODE("bvc", MERGE(GROUP_RELATIVE8, 0x50)),
PREDEFNODE("bvs", MERGE(GROUP_RELATIVE8, 0x70)),
PREDEFNODE("bcc", MERGE(GROUP_RELATIVE8, 0x90)),
PREDEFNODE("bcs", MERGE(GROUP_RELATIVE8, 0xb0)),
PREDEFNODE("bne", MERGE(GROUP_RELATIVE8, 0xd0)),
PREDEFNODE("beq", MERGE(GROUP_RELATIVE8, 0xf0)),
PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDX_JMP)),
PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDX_JSR)),
PREDEFNODE("brk", MERGE(GROUP_IMPLIEDONLY, 0)),
PREDEFNODE("php", MERGE(GROUP_IMPLIEDONLY, 8)),
PREDEFNODE("clc", MERGE(GROUP_IMPLIEDONLY, 24)),
PREDEFNODE("plp", MERGE(GROUP_IMPLIEDONLY, 40)),
PREDEFNODE("sec", MERGE(GROUP_IMPLIEDONLY, 56)),
PREDEFNODE("rti", MERGE(GROUP_IMPLIEDONLY, 64)),
PREDEFNODE("pha", MERGE(GROUP_IMPLIEDONLY, 72)),
PREDEFNODE("cli", MERGE(GROUP_IMPLIEDONLY, 88)),
PREDEFNODE("rts", MERGE(GROUP_IMPLIEDONLY, 96)),
PREDEFNODE("pla", MERGE(GROUP_IMPLIEDONLY, 104)),
PREDEFNODE("sei", MERGE(GROUP_IMPLIEDONLY, 120)),
PREDEFNODE("dey", MERGE(GROUP_IMPLIEDONLY, 136)),
PREDEFNODE("txa", MERGE(GROUP_IMPLIEDONLY, 138)),
PREDEFNODE("tya", MERGE(GROUP_IMPLIEDONLY, 152)),
PREDEFNODE("txs", MERGE(GROUP_IMPLIEDONLY, 154)),
PREDEFNODE("tay", MERGE(GROUP_IMPLIEDONLY, 168)),
PREDEFNODE("tax", MERGE(GROUP_IMPLIEDONLY, 170)),
PREDEFNODE("clv", MERGE(GROUP_IMPLIEDONLY, 184)),
PREDEFNODE("tsx", MERGE(GROUP_IMPLIEDONLY, 186)),
PREDEFNODE("iny", MERGE(GROUP_IMPLIEDONLY, 200)),
PREDEFNODE("dex", MERGE(GROUP_IMPLIEDONLY, 202)),
PREDEFNODE("cld", MERGE(GROUP_IMPLIEDONLY, 216)),
PREDEFNODE("inx", MERGE(GROUP_IMPLIEDONLY, 232)),
PREDEFNODE("nop", MERGE(GROUP_IMPLIEDONLY, 234)),
PREDEFLAST("sed", MERGE(GROUP_IMPLIEDONLY, 248)),
// ^^^^ this marks the last element
};
// undocumented opcodes of the NMOS 6502 that are also supported by c64dtv2:
static struct ronode mnemos_6502undoc1[] = {
PREDEFNODE("slo", MERGE(GROUP_ACCU, IDX_SLO)), // ASL + ORA (aka ASO)
PREDEFNODE("rla", MERGE(GROUP_ACCU, IDX_RLA)), // ROL + AND
PREDEFNODE("sre", MERGE(GROUP_ACCU, IDX_SRE)), // LSR + EOR (aka LSE)
PREDEFNODE("rra", MERGE(GROUP_ACCU, IDX_RRA)), // ROR + ADC
PREDEFNODE("sax", MERGE(GROUP_ACCU, IDX_SAX)), // STX + STA (aka AXS aka AAX)
PREDEFNODE("lax", MERGE(GROUP_ACCU, IDX_LAX)), // LDX + LDA
PREDEFNODE("dcp", MERGE(GROUP_ACCU, IDX_DCP)), // DEC + CMP (aka DCM)
PREDEFNODE("isc", MERGE(GROUP_ACCU, IDX_ISC)), // INC + SBC (aka ISB aka INS)
PREDEFNODE("las", MERGE(GROUP_MISC, IDX_LAS)), // A,X,S = {addr} & S (aka LAR aka LAE)
PREDEFNODE("tas", MERGE(GROUP_MISC, IDX_TAS)), // S = A & X {addr} = A&X& {H+1} (aka SHS aka XAS)
PREDEFNODE("sha", MERGE(GROUP_ACCU, IDX_SHA)), // {addr} = A & X & {H+1} (aka AXA aka AHX)
PREDEFNODE("shx", MERGE(GROUP_MISC, IDX_SHX)), // {addr} = X & {H+1} (aka XAS aka SXA)
PREDEFNODE("shy", MERGE(GROUP_MISC, IDX_SHY)), // {addr} = Y & {H+1} (aka SAY aka SYA)
PREDEFNODE(s_asr, MERGE(GROUP_MISC, IDX_ASR)), // LSR + EOR (aka ALR)
PREDEFNODE("arr", MERGE(GROUP_MISC, IDX_ARR)), // ROR + ADC
PREDEFNODE("sbx", MERGE(GROUP_MISC, IDX_SBX)), // DEX + CMP (aka AXS aka SAX)
PREDEFNODE("dop", MERGE(GROUP_MISC, IDX_DOP)), // skip next byte
PREDEFNODE("top", MERGE(GROUP_MISC, IDX_TOP)), // skip next word
PREDEFNODE("jam", MERGE(GROUP_MISC, IDX_JAM)), // jam/crash/kill/halt-and-catch-fire
PREDEFNODE("ane", MERGE(GROUP_MISC, IDX_ANE)), // A = (A | ??) & X & arg (aka XAA)
PREDEFLAST("lxa", MERGE(GROUP_MISC, IDX_LXA)), // A,X = (A | ??) & arg (aka OAL aka ATX)
// ^^^^ this marks the last element
};
// undocumented opcodes of the NMOS 6502 that are _not_ supported by c64dtv2:
static struct ronode mnemos_6502undoc2[] = {
PREDEFLAST("anc", MERGE(GROUP_MISC, IDX_ANC)), // ROL + AND, ASL + ORA (aka AAC)
// ^^^^ this marks the last element
};
// additional opcodes of c64dtv2:
static struct ronode mnemos_c64dtv2[] = {
PREDEFNODE(s_bra, MERGE(GROUP_RELATIVE8, 0x12)), // branch always
PREDEFNODE("sac", MERGE(GROUP_MISC, IDX_SAC)), // set accumulator mapping
PREDEFLAST("sir", MERGE(GROUP_MISC, IDX_SIR)), // set index register mapping
// ^^^^ this marks the last element
};
// new stuff in CMOS re-design:
static struct ronode mnemos_65c02[] = {
// more addressing modes for some mnemonics:
PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXcORA)),
PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDXcAND)),
PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDXcEOR)),
PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXcADC)),
PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXcSTA)),
PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXcLDA)),
PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDXcCMP)),
PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDXcSBC)),
PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDXcJMP)),
PREDEFNODE("bit", MERGE(GROUP_MISC, IDXcBIT)),
PREDEFNODE("dec", MERGE(GROUP_MISC, IDXcDEC)),
PREDEFNODE("inc", MERGE(GROUP_MISC, IDXcINC)),
// and eight new mnemonics:
PREDEFNODE(s_bra, MERGE(GROUP_RELATIVE8, 0x80)),
PREDEFNODE("phy", MERGE(GROUP_IMPLIEDONLY, 90)),
PREDEFNODE("ply", MERGE(GROUP_IMPLIEDONLY, 122)),
PREDEFNODE("phx", MERGE(GROUP_IMPLIEDONLY, 218)),
PREDEFNODE("plx", MERGE(GROUP_IMPLIEDONLY, 250)),
PREDEFNODE("tsb", MERGE(GROUP_MISC, IDXcTSB)),
PREDEFNODE("trb", MERGE(GROUP_MISC, IDXcTRB)),
PREDEFLAST("stz", MERGE(GROUP_MISC, IDXcSTZ)),
// ^^^^ this marks the last element
};
// bit-manipulation extensions (by Rockwell?)
static struct ronode mnemos_bitmanips[] = {
PREDEFNODE("rmb0", MERGE(GROUP_ZPONLY, 0x07)),
PREDEFNODE("rmb1", MERGE(GROUP_ZPONLY, 0x17)),
PREDEFNODE("rmb2", MERGE(GROUP_ZPONLY, 0x27)),
PREDEFNODE("rmb3", MERGE(GROUP_ZPONLY, 0x37)),
PREDEFNODE("rmb4", MERGE(GROUP_ZPONLY, 0x47)),
PREDEFNODE("rmb5", MERGE(GROUP_ZPONLY, 0x57)),
PREDEFNODE("rmb6", MERGE(GROUP_ZPONLY, 0x67)),
PREDEFNODE("rmb7", MERGE(GROUP_ZPONLY, 0x77)),
PREDEFNODE("smb0", MERGE(GROUP_ZPONLY, 0x87)),
PREDEFNODE("smb1", MERGE(GROUP_ZPONLY, 0x97)),
PREDEFNODE("smb2", MERGE(GROUP_ZPONLY, 0xa7)),
PREDEFNODE("smb3", MERGE(GROUP_ZPONLY, 0xb7)),
PREDEFNODE("smb4", MERGE(GROUP_ZPONLY, 0xc7)),
PREDEFNODE("smb5", MERGE(GROUP_ZPONLY, 0xd7)),
PREDEFNODE("smb6", MERGE(GROUP_ZPONLY, 0xe7)),
PREDEFNODE("smb7", MERGE(GROUP_ZPONLY, 0xf7)),
PREDEFNODE("bbr0", MERGE(GROUP_BITBRANCH, 0x0f)),
PREDEFNODE("bbr1", MERGE(GROUP_BITBRANCH, 0x1f)),
PREDEFNODE("bbr2", MERGE(GROUP_BITBRANCH, 0x2f)),
PREDEFNODE("bbr3", MERGE(GROUP_BITBRANCH, 0x3f)),
PREDEFNODE("bbr4", MERGE(GROUP_BITBRANCH, 0x4f)),
PREDEFNODE("bbr5", MERGE(GROUP_BITBRANCH, 0x5f)),
PREDEFNODE("bbr6", MERGE(GROUP_BITBRANCH, 0x6f)),
PREDEFNODE("bbr7", MERGE(GROUP_BITBRANCH, 0x7f)),
PREDEFNODE("bbs0", MERGE(GROUP_BITBRANCH, 0x8f)),
PREDEFNODE("bbs1", MERGE(GROUP_BITBRANCH, 0x9f)),
PREDEFNODE("bbs2", MERGE(GROUP_BITBRANCH, 0xaf)),
PREDEFNODE("bbs3", MERGE(GROUP_BITBRANCH, 0xbf)),
PREDEFNODE("bbs4", MERGE(GROUP_BITBRANCH, 0xcf)),
PREDEFNODE("bbs5", MERGE(GROUP_BITBRANCH, 0xdf)),
PREDEFNODE("bbs6", MERGE(GROUP_BITBRANCH, 0xef)),
PREDEFLAST("bbs7", MERGE(GROUP_BITBRANCH, 0xff)),
// ^^^^ this marks the last element
};
// "stp" and "wai" extensions by WDC:
static struct ronode mnemos_stp_wai[] = {
PREDEFNODE("stp", MERGE(GROUP_IMPLIEDONLY, 219)),
PREDEFLAST("wai", MERGE(GROUP_IMPLIEDONLY, 203)),
// ^^^^ this marks the last element
};
// most of the 65816 stuff
static struct ronode mnemos_65816[] = {
// CAUTION - these use 6502/65c02 indices, because the opcodes are the same - but I need flags for immediate mode!
PREDEFNODE("ldy", MERGE(GROUP_MISC, IDX_LDY | IM_INDEXREGS)),
PREDEFNODE("ldx", MERGE(GROUP_MISC, IDX_LDX | IM_INDEXREGS)),
PREDEFNODE("cpy", MERGE(GROUP_MISC, IDX_CPY | IM_INDEXREGS)),
PREDEFNODE("cpx", MERGE(GROUP_MISC, IDX_CPX | IM_INDEXREGS)),
PREDEFNODE("bit", MERGE(GROUP_MISC, IDXcBIT | IM_ACCUMULATOR)),
// more addressing modes for some mnemonics:
PREDEFNODE("ora", MERGE(GROUP_ACCU, IDX816ORA | IM_ACCUMULATOR)),
PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDX816AND | IM_ACCUMULATOR)),
PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDX816EOR | IM_ACCUMULATOR)),
PREDEFNODE("adc", MERGE(GROUP_ACCU, IDX816ADC | IM_ACCUMULATOR)),
PREDEFNODE("sta", MERGE(GROUP_ACCU, IDX816STA)),
PREDEFNODE("lda", MERGE(GROUP_ACCU, IDX816LDA | IM_ACCUMULATOR)),
PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDX816CMP | IM_ACCUMULATOR)),
PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDX816SBC | IM_ACCUMULATOR)),
PREDEFNODE("jmp", MERGE(GROUP_ALLJUMPS, IDX816JMP)),
PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDX816JSR)),
//
PREDEFNODE("pei", MERGE(GROUP_ACCU, IDX816PEI)),
PREDEFNODE("jml", MERGE(GROUP_ALLJUMPS, IDX816JML)),
PREDEFNODE("jsl", MERGE(GROUP_ALLJUMPS, IDX816JSL)),
PREDEFNODE("mvp", MERGE(GROUP_BOTHMOVES, 0x44)),
PREDEFNODE("mvn", MERGE(GROUP_BOTHMOVES, 0x54)),
PREDEFNODE("per", MERGE(GROUP_REL16_3, 98)),
PREDEFNODE(s_brl, MERGE(GROUP_REL16_3, 130)),
PREDEFNODE("cop", MERGE(GROUP_MISC, IDX816COP)),
PREDEFNODE("rep", MERGE(GROUP_MISC, IDX816REP)),
PREDEFNODE("sep", MERGE(GROUP_MISC, IDX816SEP)),
PREDEFNODE("pea", MERGE(GROUP_MISC, IDX816PEA)),
PREDEFNODE("phd", MERGE(GROUP_IMPLIEDONLY, 11)),
PREDEFNODE("tcs", MERGE(GROUP_IMPLIEDONLY, 27)),
PREDEFNODE("pld", MERGE(GROUP_IMPLIEDONLY, 43)),
PREDEFNODE("tsc", MERGE(GROUP_IMPLIEDONLY, 59)),
PREDEFNODE("wdm", MERGE(GROUP_IMPLIEDONLY, 66)),
PREDEFNODE("phk", MERGE(GROUP_IMPLIEDONLY, 75)),
PREDEFNODE("tcd", MERGE(GROUP_IMPLIEDONLY, 91)),
PREDEFNODE("rtl", MERGE(GROUP_IMPLIEDONLY, 107)),
PREDEFNODE("tdc", MERGE(GROUP_IMPLIEDONLY, 123)),
PREDEFNODE("phb", MERGE(GROUP_IMPLIEDONLY, 139)),
PREDEFNODE("txy", MERGE(GROUP_IMPLIEDONLY, 155)),
PREDEFNODE("plb", MERGE(GROUP_IMPLIEDONLY, 171)),
PREDEFNODE("tyx", MERGE(GROUP_IMPLIEDONLY, 187)),
PREDEFNODE("xba", MERGE(GROUP_IMPLIEDONLY, 235)),
PREDEFLAST("xce", MERGE(GROUP_IMPLIEDONLY, 251)),
// ^^^^ this marks the last element
};
// 65ce02 has 46 new opcodes and a few changes:
static struct ronode mnemos_65ce02[] = {
// 65ce02 changes (zp) addressing of 65c02 to (zp),z addressing:
PREDEFNODE("ora", MERGE(GROUP_ACCU, IDXeORA)),
PREDEFNODE(s_and, MERGE(GROUP_ACCU, IDXeAND)),
PREDEFNODE(s_eor, MERGE(GROUP_ACCU, IDXeEOR)),
PREDEFNODE("adc", MERGE(GROUP_ACCU, IDXeADC)),
PREDEFNODE("sta", MERGE(GROUP_ACCU, IDXeSTA)), // +1 for (8,s),y
PREDEFNODE("lda", MERGE(GROUP_ACCU, IDXeLDA)), // +1 for (8,s),y
PREDEFNODE("cmp", MERGE(GROUP_ACCU, IDXeCMP)),
PREDEFNODE("sbc", MERGE(GROUP_ACCU, IDXeSBC)),
// more addressing modes:
PREDEFNODE("jsr", MERGE(GROUP_ALLJUMPS, IDXeJSR)), // +2
PREDEFNODE("stx", MERGE(GROUP_MISC, IDXeSTX)), // +1
PREDEFNODE("sty", MERGE(GROUP_MISC, IDXeSTY)), // +1
// +10 long branches (8 normal, 1 unconditional, BSR uncond to subroutine))
PREDEFNODE("lbpl", MERGE(GROUP_REL16_2, 0x13)),
PREDEFNODE("lbmi", MERGE(GROUP_REL16_2, 0x33)),
PREDEFNODE("lbvc", MERGE(GROUP_REL16_2, 0x53)),
PREDEFNODE("lbvs", MERGE(GROUP_REL16_2, 0x73)),
PREDEFNODE("lbcc", MERGE(GROUP_REL16_2, 0x93)),
PREDEFNODE("lbcs", MERGE(GROUP_REL16_2, 0xb3)),
PREDEFNODE("lbne", MERGE(GROUP_REL16_2, 0xd3)),
PREDEFNODE("lbeq", MERGE(GROUP_REL16_2, 0xf3)),
PREDEFNODE("bru", MERGE(GROUP_RELATIVE8, 0x80)), // alias for 65c02's "bra"
PREDEFNODE("bsr", MERGE(GROUP_REL16_2, 0x63)),
PREDEFNODE("lbru", MERGE(GROUP_REL16_2, 0x83)),
PREDEFNODE("lbra", MERGE(GROUP_REL16_2, 0x83)), // alias
// new mnemonics:
PREDEFNODE("asr", MERGE(GROUP_MISC, IDXeASR)),
PREDEFNODE("asw", MERGE(GROUP_MISC, IDXeASW)),
PREDEFNODE("cpz", MERGE(GROUP_MISC, IDXeCPZ)),
PREDEFNODE("dew", MERGE(GROUP_MISC, IDXeDEW)),
PREDEFNODE("inw", MERGE(GROUP_MISC, IDXeINW)),
PREDEFNODE("ldz", MERGE(GROUP_MISC, IDXeLDZ)),
PREDEFNODE("phw", MERGE(GROUP_MISC, IDXePHW | IM_FORCE16)), // when using immediate addressing, arg is 16 bit
PREDEFNODE("row", MERGE(GROUP_MISC, IDXeROW)),
PREDEFNODE("rtn", MERGE(GROUP_MISC, IDXeRTN)),
PREDEFNODE("cle", MERGE(GROUP_IMPLIEDONLY, 0x02)),
PREDEFNODE("see", MERGE(GROUP_IMPLIEDONLY, 0x03)),
PREDEFNODE("inz", MERGE(GROUP_IMPLIEDONLY, 0x1b)),
PREDEFNODE("dez", MERGE(GROUP_IMPLIEDONLY, 0x3b)),
PREDEFNODE("neg", MERGE(GROUP_IMPLIEDONLY, 0x42)),
PREDEFNODE("tsy", MERGE(GROUP_IMPLIEDONLY, 0x0b)),
PREDEFNODE("tys", MERGE(GROUP_IMPLIEDONLY, 0x2b)),
PREDEFNODE("taz", MERGE(GROUP_IMPLIEDONLY, 0x4b)),
PREDEFNODE("tab", MERGE(GROUP_IMPLIEDONLY, 0x5b)),
PREDEFNODE("tza", MERGE(GROUP_IMPLIEDONLY, 0x6b)),
PREDEFNODE("tba", MERGE(GROUP_IMPLIEDONLY, 0x7b)),
PREDEFNODE("phz", MERGE(GROUP_IMPLIEDONLY, 0xdb)),
PREDEFLAST("plz", MERGE(GROUP_IMPLIEDONLY, 0xfb)),
// ^^^^ this marks the last element
};
// 65ce02's "aug" opcode:
static struct ronode mnemos_aug[] = {
PREDEFLAST("aug", MERGE(GROUP_IMPLIEDONLY, 0x5c)), // actually a "4-byte NOP reserved for future expansion"
// ^^^^ this marks the last element
};
// 4502's "map" and "eom" opcodes:
static struct ronode mnemos_map_eom[] = {
PREDEFNODE("map", MERGE(GROUP_IMPLIEDONLY, 0x5c)), // change memory mapping
PREDEFLAST("eom", MERGE(GROUP_IMPLIEDONLY, 0xea)), // actually the NOP opcode
// ^^^^ this marks the last element
};
// Functions
// create dynamic buffer, build keyword trees
void Mnemo_init(void)
{
mnemo_dyna_buf = DynaBuf_create(MNEMO_DYNABUF_INITIALSIZE);
Tree_add_table(&mnemo_6502_tree, mnemos_6502);
Tree_add_table(&mnemo_6502undoc1_tree, mnemos_6502undoc1);
Tree_add_table(&mnemo_6502undoc2_tree, mnemos_6502undoc2);
Tree_add_table(&mnemo_c64dtv2_tree, mnemos_c64dtv2);
Tree_add_table(&mnemo_65c02_tree, mnemos_65c02);
Tree_add_table(&mnemo_bitmanips_tree, mnemos_bitmanips);
Tree_add_table(&mnemo_stp_wai_tree, mnemos_stp_wai);
Tree_add_table(&mnemo_65816_tree, mnemos_65816);
Tree_add_table(&mnemo_65ce02_tree, mnemos_65ce02);
Tree_add_table(&mnemo_aug_tree, mnemos_aug);
Tree_add_table(&mnemo_map_eom_tree, mnemos_map_eom);
}
// Address mode parsing
// utility function for parsing indices. result must be processed via AMB_PREINDEX() or AMB_INDEX() macro!
static int get_index(int next)
{
if (next)
GetByte();
if (!Input_accept_comma())
return INDEX_NONE;
// there was a comma, so check next character (spaces will have been skipped):
switch (GotByte) {
case 's':
case 'S':
// if next character is 'p' or 'P', eat that as well, so ",sp" works just like ",s"
GetByte();
if ((GotByte == 'p') || (GotByte == 'P'))
GetByte();
SKIPSPACE();
return INDEX_S;
case 'x':
case 'X':
NEXTANDSKIPSPACE();
return INDEX_X;
case 'y':
case 'Y':
NEXTANDSKIPSPACE();
return INDEX_Y;
case 'z':
case 'Z':
NEXTANDSKIPSPACE();
return INDEX_Z;
}
Throw_error(exception_syntax);
return INDEX_NONE;
}
// wrapper function to read integer argument
static void get_int_arg(struct number *result, boolean complain_about_indirect)
{
struct expression expression;
ALU_addrmode_int(&expression, 0); // accept 0 parentheses still open (-> complain!)
if (expression.is_parenthesized && complain_about_indirect) {
// FIXME - add warning for "there are useless (), you know this mnemonic does not have indirect addressing, right?"
// or rather raise error and be done with it?
}
*result = expression.number;
}
// wrapper function to detect addressing mode, and, if not IMPLIED, read arg.
// argument is stored in given result structure, addressing mode is returned.
static int get_addr_mode(struct number *result)
{
struct expression expression;
int address_mode_bits = 0;
SKIPSPACE();
switch (GotByte) {
case CHAR_EOS:
address_mode_bits = AMB_IMPLIED;
break;
case '#':
GetByte(); // proceed with next char
address_mode_bits = AMB_IMMEDIATE;
get_int_arg(result, FALSE);
typesystem_want_nonaddr(result); // FIXME - this is wrong for 65ce02's PHW#
break;
case '[':
GetByte(); // proceed with next char
get_int_arg(result, FALSE);
typesystem_want_addr(result);
if (GotByte == ']')
address_mode_bits = AMB_LONGINDIRECT | AMB_INDEX(get_index(TRUE));
else
Throw_error(exception_syntax);
break;
default:
ALU_addrmode_int(&expression, 1); // direct call instead of wrapper, to allow for "(...,"
*result = expression.number;
typesystem_want_addr(result);
// check for indirect addressing
if (expression.is_parenthesized)
address_mode_bits |= AMB_INDIRECT;
// check for internal index (before closing parenthesis)
if (expression.open_parentheses) {
// in case there are still open parentheses,
// read internal index
address_mode_bits |= AMB_PREINDEX(get_index(FALSE));
if (GotByte == ')')
GetByte(); // go on with next char
else
Throw_error(exception_syntax);
}
// check for external index (after closing parenthesis)
address_mode_bits |= AMB_INDEX(get_index(FALSE));
}
// ensure end of line
Input_ensure_EOS();
//printf("AM: %x\n", address_mode_bits);
return address_mode_bits;
}
// Helper function for calc_arg_size()
// Only call with "size_bit = NUMBER_FORCES_16" or "size_bit = NUMBER_FORCES_24"
static int check_oversize(int size_bit, struct number *argument)
{
// only check if value is *defined*
if ((argument->flags & NUMBER_IS_DEFINED) == 0)
return size_bit; // pass on result
// value is defined, so check
if (size_bit == NUMBER_FORCES_16) {
// check 16-bit argument for high byte zero
if ((argument->val.intval <= 255) && (argument->val.intval >= -128))
Throw_warning(exception_oversized_addrmode);
} else {
// check 24-bit argument for bank byte zero
if ((argument->val.intval <= 65535) && (argument->val.intval >= -32768))
Throw_warning(exception_oversized_addrmode);
}
return size_bit; // pass on result
}
// Utility function for comparing force bits, argument value, argument size,
// "unsure"-flag and possible addressing modes. Returns force bit matching
// number of parameter bytes to send. If it returns zero, an error occurred
// (and has already been delivered).
// force_bit none, 8b, 16b, 24b
// argument value and flags of parameter
// addressing_modes adressing modes (8b, 16b, 24b or any combination)
// Return value = force bit for number of parameter bytes to send (0 = error)
static int calc_arg_size(int force_bit, struct number *argument, int addressing_modes)
{
// if there are no possible addressing modes, complain
if (addressing_modes == MAYBE______) {
Throw_error(exception_illegal_combination);
return 0;
}
// if command has force bit, act upon it
if (force_bit) {
// if command allows this force bit, return it
if (addressing_modes & force_bit)
return force_bit;
// if not, complain
Throw_error("Illegal combination of command and postfix.");
return 0;
}
// Command has no force bit. Check whether value has one
// if value has force bit, act upon it
if (argument->flags & NUMBER_FORCEBITS) {
// Value has force bit set, so return this or bigger size
if (NUMBER_FORCES_8 & addressing_modes & argument->flags)
return NUMBER_FORCES_8;
if (NUMBER_FORCES_16 & addressing_modes & argument->flags)
return NUMBER_FORCES_16;
if (NUMBER_FORCES_24 & addressing_modes)
return NUMBER_FORCES_24;
Throw_error(exception_number_out_of_range); // else error
return 0;
}
// Value has no force bit. Check whether there's only one addr mode
// if only one addressing mode, use that
if ((addressing_modes == NUMBER_FORCES_8)
|| (addressing_modes == NUMBER_FORCES_16)
|| (addressing_modes == NUMBER_FORCES_24))
return addressing_modes; // There's only one, so use it
// There's more than one addressing mode. Check whether value is sure
// if value is unsure, use default size
if (argument->flags & NUMBER_EVER_UNDEFINED) {
// if there is an 8-bit addressing mode *and* the value
// is sure to fit into 8 bits, use the 8-bit mode
if ((addressing_modes & NUMBER_FORCES_8) && (argument->flags & NUMBER_FITS_BYTE))
return NUMBER_FORCES_8;
// if there is a 16-bit addressing, use that
// call helper function for "oversized addr mode" warning
if (NUMBER_FORCES_16 & addressing_modes)
return check_oversize(NUMBER_FORCES_16, argument);
// if there is a 24-bit addressing, use that
// call helper function for "oversized addr mode" warning
if (NUMBER_FORCES_24 & addressing_modes)
return check_oversize(NUMBER_FORCES_24, argument);
// otherwise, use 8-bit-addressing, which will raise an
// error later on if the value won't fit
return NUMBER_FORCES_8;
}
// Value is sure, so use its own size
// if value is negative, size cannot be chosen. Complain!
if (argument->val.intval < 0) {
Throw_error("Negative value - cannot choose addressing mode.");
return 0;
}
// Value is positive or zero. Check size ranges
// if there is an 8-bit addressing mode and value fits, use 8 bits
if ((addressing_modes & NUMBER_FORCES_8) && (argument->val.intval < 256))
return NUMBER_FORCES_8;
// if there is a 16-bit addressing mode and value fits, use 16 bits
if ((addressing_modes & NUMBER_FORCES_16) && (argument->val.intval < 65536))
return NUMBER_FORCES_16;
// if there is a 24-bit addressing mode, use that. In case the
// value doesn't fit, the output function will do the complaining.
if (addressing_modes & NUMBER_FORCES_24)
return NUMBER_FORCES_24;
// Value is too big for all possible addressing modes
Throw_error(exception_number_out_of_range);
return 0;
}
// Mnemonics using only implied addressing.
static void group_only_implied_addressing(int opcode)
{
// for 65ce02 and 4502, warn about buggy decimal mode
if ((opcode == 248) && (CPU_state.type->flags & CPUFLAG_DECIMALSUBTRACTBUGGY))
Throw_first_pass_warning("Found SED instruction for CPU with known decimal SBC bug.");
Output_byte(opcode);
Input_ensure_EOS();
}
// helper function to output "Target not in bank" message
static void not_in_bank(intval_t target)
{
char buffer[60]; // 640K should be enough for anybody
sprintf(buffer, "Target not in bank (0x%lx).", (long) target);
Throw_error(buffer);
}
// helper function for branches with 8-bit offset (including bbr0..7/bbs0..7)
static void near_branch(int preoffset)
{
struct number target;
intval_t offset = 0; // dummy value, to not throw more errors than necessary
get_int_arg(&target, TRUE);
typesystem_want_addr(&target);
// FIXME - read pc via function call instead!
if (CPU_state.pc.flags & target.flags & NUMBER_IS_DEFINED) {
if ((target.val.intval | 0xffff) != 0xffff) {
not_in_bank(target.val.intval);
} else {
offset = (target.val.intval - (CPU_state.pc.val.intval + preoffset)) & 0xffff; // clip to 16 bit offset
// fix sign
if (offset & 0x8000)
offset -= 0x10000;
// range check
if ((offset < -128) || (offset > 127)) {
char buffer[60]; // 640K should be enough for anybody
sprintf(buffer, "Target out of range (%ld; %ld too far).", (long) offset, (long) (offset < -128 ? -128 - offset : offset - 127));
Throw_error(buffer);
}
}
}
// this fn has its own range check (see above).
// No reason to irritate the user with another error message,
// so use Output_byte() instead of output_8()
//output_8(offset);
Output_byte(offset);
Input_ensure_EOS();
}
// helper function for relative addressing with 16-bit offset
static void far_branch(int preoffset)
{
struct number target;
intval_t offset = 0; // dummy value, to not throw more errors than necessary
get_int_arg(&target, TRUE);
typesystem_want_addr(&target);
// FIXME - read pc via function call instead!
if (CPU_state.pc.flags & target.flags & NUMBER_IS_DEFINED) {
if ((target.val.intval | 0xffff) != 0xffff) {
not_in_bank(target.val.intval);
} else {
offset = (target.val.intval - (CPU_state.pc.val.intval + preoffset)) & 0xffff;
// no further checks necessary, 16-bit branches can access whole bank
}
}
output_le16(offset);
Input_ensure_EOS();
}
// set addressing mode bits depending on which opcodes exist, then calculate
// argument size and output both opcode and argument
static void make_command(int force_bit, struct number *result, unsigned long opcodes)
{
int addressing_modes = MAYBE______;
if (opcodes & 0x0000ff)
addressing_modes |= MAYBE_1____;
if (opcodes & 0x00ff00)
addressing_modes |= MAYBE___2__;
if (opcodes & 0xff0000)
addressing_modes |= MAYBE_____3;
switch (calc_arg_size(force_bit, result, addressing_modes)) {
case NUMBER_FORCES_8:
Output_byte(opcodes & 255);
output_8(result->val.intval);
break;
case NUMBER_FORCES_16:
Output_byte((opcodes >> 8) & 255);
output_le16(result->val.intval);
break;
case NUMBER_FORCES_24:
Output_byte((opcodes >> 16) & 255);
output_le24(result->val.intval);
}
}
// check whether 16bit immediate addressing is allowed. If not, return given
// opcode. If it is allowed, set force bits according to CPU register length
// and return given opcode for both 8- and 16-bit mode.
static unsigned int imm_ops(int *force_bit, unsigned char opcode, int immediate_mode)
{
int long_register = 0;
switch (immediate_mode) {
case IM_FORCE8:
return opcode; // result in bits 0..7 forces single-byte argument
case IM_FORCE16: // currently only for 65ce02's PHW#
return ((unsigned int) opcode) << 8; // opcode in bits8.15 forces two-byte argument
case IM_ACCUMULATOR: // for 65816
long_register = CPU_state.a_is_long;
break;
case IM_INDEXREGS: // for 65816
long_register = CPU_state.xy_are_long;
break;
default:
Bug_found("IllegalImmediateMode", immediate_mode);
}
// if the CPU does not support long registers...
if ((CPU_state.type->flags & CPUFLAG_SUPPORTSLONGREGS) == 0)
return opcode; // result in bits 0..7 forces single-byte argument
// check force bits - if no force bits given, use cpu state and convert to force bit
if (*force_bit == 0)
*force_bit = long_register ? NUMBER_FORCES_16 : NUMBER_FORCES_8;
// return identical opcodes for single-byte and two-byte arguments:
return (((unsigned int) opcode) << 8) | opcode;
}
// helper function to warn if zp pointer wraps around
static void check_zp_wraparound(struct number *result)
{
if ((result->val.intval == 0xff)
&& (result->flags & NUMBER_IS_DEFINED))
Throw_warning("Zeropage pointer wraps around from $ff to $00");
}
// The main accumulator stuff (ADC, AND, CMP, EOR, LDA, ORA, SBC, STA)
// plus PEI.
static void group_main(int index, int immediate_mode)
{
unsigned long immediate_opcodes;
struct number result;
int force_bit = Input_get_force_bit(); // skips spaces after
switch (get_addr_mode(&result)) {
case IMMEDIATE_ADDRESSING: // #$ff or #$ffff (depending on accu length)
immediate_opcodes = imm_ops(&force_bit, accu_imm[index], immediate_mode);
// CAUTION - do not incorporate the line above into the line
// below - "force_bit" might be undefined (depends on compiler).
make_command(force_bit, &result, immediate_opcodes);
break;
case ABSOLUTE_ADDRESSING: // $ff, $ffff, $ffffff
make_command(force_bit, &result, accu_abs[index]);
break;
case X_INDEXED_ADDRESSING: // $ff,x, $ffff,x, $ffffff,x
make_command(force_bit, &result, accu_xabs[index]);
break;
case Y_INDEXED_ADDRESSING: // $ffff,y (in theory, "$ff,y" as well)
make_command(force_bit, &result, accu_yabs[index]);
break;
case STACK_INDEXED_ADDRESSING: // $ff,s
make_command(force_bit, &result, accu_sabs8[index]);
break;
case X_INDEXED_INDIRECT_ADDRESSING: // ($ff,x)
make_command(force_bit, &result, accu_xind8[index]);
break;
case INDIRECT_ADDRESSING: // ($ff)
make_command(force_bit, &result, accu_ind8[index]);
check_zp_wraparound(&result);
break;
case INDIRECT_Y_INDEXED_ADDRESSING: // ($ff),y
make_command(force_bit, &result, accu_indy8[index]);
check_zp_wraparound(&result);
break;
case INDIRECT_Z_INDEXED_ADDRESSING: // ($ff),z
make_command(force_bit, &result, accu_indz8[index]);
check_zp_wraparound(&result);
break;
case LONG_INDIRECT_ADDRESSING: // [$ff]
make_command(force_bit, &result, accu_lind8[index]);
break;
case LONG_INDIRECT_Y_INDEXED_ADDRESSING: // [$ff],y
make_command(force_bit, &result, accu_lindy8[index]);
break;
case STACK_INDEXED_INDIRECT_Y_INDEXED_ADDRESSING: // ($ff,s),y
make_command(force_bit, &result, accu_sindy8[index]);
break;
default: // other combinations are illegal
Throw_error(exception_illegal_combination);
}
}
// Various mnemonics with different addressing modes.
static void group_misc(int index, int immediate_mode)
{
unsigned long immediate_opcodes;
struct number result;
int force_bit = Input_get_force_bit(); // skips spaces after
switch (get_addr_mode(&result)) {
case IMPLIED_ADDRESSING: // implied addressing
if (misc_impl[index])
Output_byte(misc_impl[index]);
else
Throw_error(exception_illegal_combination);
break;
case IMMEDIATE_ADDRESSING: // #$ff or #$ffff (depending on index register length)
immediate_opcodes = imm_ops(&force_bit, misc_imm[index], immediate_mode);
// CAUTION - do not incorporate the line above into the line
// below - "force_bit" might be undefined (depends on compiler).
make_command(force_bit, &result, immediate_opcodes);
// check whether to warn about 6510's unstable ANE/LXA
if ((CPU_state.type->flags & CPUFLAG_8B_AND_AB_NEED_0_ARG)
&& ((result.val.intval & 0xff) != 0x00)
&& (result.flags & NUMBER_IS_DEFINED)) {
if (immediate_opcodes == 0x8b)
Throw_warning("Assembling unstable ANE #NONZERO instruction");
else if (immediate_opcodes == 0xab)
Throw_warning("Assembling unstable LXA #NONZERO instruction");
}
break;
case ABSOLUTE_ADDRESSING: // $ff or $ffff
make_command(force_bit, &result, misc_abs[index]);
break;
case X_INDEXED_ADDRESSING: // $ff,x or $ffff,x
make_command(force_bit, &result, misc_xabs[index]);
break;
case Y_INDEXED_ADDRESSING: // $ff,y or $ffff,y
make_command(force_bit, &result, misc_yabs[index]);
break;
default: // other combinations are illegal
Throw_error(exception_illegal_combination);
}
}
// mnemonics using only 8bit relative addressing (short branch instructions).
static void group_std_branches(int opcode)
{
Output_byte(opcode);
near_branch(2);
}
// "bbr0..7" and "bbs0..7"
static void group_bbr_bbs(int opcode)
{
struct number zpmem;
get_int_arg(&zpmem, TRUE);
typesystem_want_addr(&zpmem);
if (Input_accept_comma()) {
Output_byte(opcode);
Output_byte(zpmem.val.intval);
near_branch(3);
} else {
Throw_error(exception_syntax);
}
}
// mnemonics using only 16bit relative addressing (BRL and PER of 65816, and the long branches of 65ce02)
static void group_relative16(int opcode, int preoffset)
{
Output_byte(opcode);
far_branch(preoffset);
}
// "mvn" and "mvp"
// TODO - allow alternative syntax with '#' and 24-bit addresses (select via CLI switch)
static void group_mvn_mvp(int opcode)
{
struct number source,
target;
// assembler syntax: "mnemonic source, target"
get_int_arg(&source, TRUE);
typesystem_want_nonaddr(&source);
if (Input_accept_comma()) {
get_int_arg(&target, TRUE);
typesystem_want_nonaddr(&target);
// machine language order: "opcode target source"
Output_byte(opcode);
output_8(target.val.intval);
output_8(source.val.intval);
Input_ensure_EOS();
} else {
Throw_error(exception_syntax);
}
}
// "rmb0..7" and "smb0..7"
static void group_only_zp(int opcode)
{
struct number target;
get_int_arg(&target, TRUE);
typesystem_want_addr(&target);
Output_byte(opcode);
output_8(target.val.intval);
Input_ensure_EOS();
}
// The jump instructions.
static void group_jump(int index)
{
struct number result;
int force_bit = Input_get_force_bit(); // skips spaces after
switch (get_addr_mode(&result)) {
case ABSOLUTE_ADDRESSING: // absolute16 or absolute24
make_command(force_bit, &result, jump_abs[index]);
break;
case INDIRECT_ADDRESSING: // ($ffff)
make_command(force_bit, &result, jump_ind[index]);
// check whether to warn about 6502's JMP() bug
if (((result.val.intval & 0xff) == 0xff)
&& (result.flags & NUMBER_IS_DEFINED)
&& (CPU_state.type->flags & CPUFLAG_INDIRECTJMPBUGGY))
Throw_warning("Assembling buggy JMP($xxff) instruction");
break;
case X_INDEXED_INDIRECT_ADDRESSING: // ($ffff,x)
make_command(force_bit, &result, jump_xind[index]);
break;
case LONG_INDIRECT_ADDRESSING: // [$ffff]
make_command(force_bit, &result, jump_lind[index]);
break;
default: // other combinations are illegal
Throw_error(exception_illegal_combination);
}
}
// Work function
static int check_mnemo_tree(struct ronode *tree, struct dynabuf *dyna_buf)
{
void *node_body;
int code,
immediate_mode; // size of immediate argument
// search for tree item
if (!Tree_easy_scan(tree, &node_body, dyna_buf))
return FALSE;
code = ((int) node_body) & CODEMASK; // get opcode or table index
immediate_mode = ((int) node_body) & IMMASK; // get immediate mode
switch (GROUP((long) node_body)) {
case GROUP_ACCU: // main accumulator stuff
group_main(code, immediate_mode);
break;
case GROUP_MISC: // misc
group_misc(code, immediate_mode);
break;
case GROUP_ALLJUMPS: // the jump instructions
group_jump(code);
break;
case GROUP_IMPLIEDONLY: // mnemonics with only implied addressing
group_only_implied_addressing(code);
break;
case GROUP_RELATIVE8: // short relative
group_std_branches(code);
break;
case GROUP_BITBRANCH: // "bbr0..7" and "bbs0..7"
group_bbr_bbs(code);
break;
case GROUP_REL16_2: // long relative to pc+2
group_relative16(code, 2);
break;
case GROUP_REL16_3: // long relative to pc+3
group_relative16(code, 3);
break;
case GROUP_BOTHMOVES: // "mvp" and "mvn"
group_mvn_mvp(code);
break;
case GROUP_ZPONLY: // "rmb0..7" and "smb0..7"
group_only_zp(code);
break;
default: // others indicate bugs
Bug_found("IllegalGroupIndex", code);
}
return TRUE;
}
// check whether mnemonic in GlobalDynaBuf is supported by 6502 cpu.
boolean keyword_is_6502_mnemo(int length)
{
if (length != 3)
return FALSE;
// make lower case version of mnemonic in local dynamic buffer
DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf);
return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf) ? TRUE : FALSE;
}
// check whether mnemonic in GlobalDynaBuf is supported by 6510 cpu.
boolean keyword_is_6510_mnemo(int length)
{
if (length != 3)
return FALSE;
// make lower case version of mnemonic in local dynamic buffer
DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf);
// first check undocumented ("illegal") opcodes...
if (check_mnemo_tree(mnemo_6502undoc1_tree, mnemo_dyna_buf))
return TRUE;
// then check some more undocumented ("illegal") opcodes...
if (check_mnemo_tree(mnemo_6502undoc2_tree, mnemo_dyna_buf))
return TRUE;
// ...then check original opcodes
return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf) ? TRUE : FALSE;
}
// check whether mnemonic in GlobalDynaBuf is supported by C64DTV2 cpu.
boolean keyword_is_c64dtv2_mnemo(int length)
{
if (length != 3)
return FALSE;
// make lower case version of mnemonic in local dynamic buffer
DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf);
// first check C64DTV2 extensions...
if (check_mnemo_tree(mnemo_c64dtv2_tree, mnemo_dyna_buf))
return TRUE;
// ...then check a few undocumented ("illegal") opcodes...
if (check_mnemo_tree(mnemo_6502undoc1_tree, mnemo_dyna_buf))
return TRUE;
// ...then check original opcodes
return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf) ? TRUE : FALSE;
}
// check whether mnemonic in GlobalDynaBuf is supported by 65c02 cpu.
boolean keyword_is_65c02_mnemo(int length)
{
if (length != 3)
return FALSE;
// make lower case version of mnemonic in local dynamic buffer
DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf);
// first check extensions because some mnemonics gained new addressing modes...
if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf))
return TRUE;
// ...then check original opcodes
return check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf) ? TRUE : FALSE;
}
// check whether mnemonic in GlobalDynaBuf is supported by Rockwell 65c02 cpu.
boolean keyword_is_r65c02_mnemo(int length)
{
if ((length != 3) && (length != 4))
return FALSE;
// make lower case version of mnemonic in local dynamic buffer
DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf);
// first check 65c02 extensions because some mnemonics gained new addressing modes...
if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf))
return TRUE;
// ...then check original opcodes...
if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf))
return TRUE;
// ...then check Rockwell extensions (rmb, smb, bbr, bbs)
return check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf) ? TRUE : FALSE;
}
// check whether mnemonic in GlobalDynaBuf is supported by WDC w65c02 cpu.
boolean keyword_is_w65c02_mnemo(int length)
{
if ((length != 3) && (length != 4))
return FALSE;
// make lower case version of mnemonic in local dynamic buffer
DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf);
// first check 65c02 extensions because some mnemonics gained new addressing modes...
if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf))
return TRUE;
// ...then check original opcodes...
if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf))
return TRUE;
// ...then check Rockwell extensions (rmb, smb, bbr, bbs)...
if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf))
return TRUE;
// ...then check WDC extensions "stp" and "wai"
return check_mnemo_tree(mnemo_stp_wai_tree, mnemo_dyna_buf) ? TRUE : FALSE;
}
// check whether mnemonic in GlobalDynaBuf is supported by CSG 65CE02 cpu.
boolean keyword_is_65ce02_mnemo(int length)
{
if ((length != 3) && (length != 4))
return FALSE;
// make lower case version of mnemonic in local dynamic buffer
DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf);
// first check 65ce02 extensions because some mnemonics gained new addressing modes...
if (check_mnemo_tree(mnemo_65ce02_tree, mnemo_dyna_buf))
return TRUE;
// ...then check 65c02 extensions because of the same reason...
if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf))
return TRUE;
// ...then check original opcodes...
if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf))
return TRUE;
// ...then check Rockwell extensions (rmb, smb, bbr, bbs)...
if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf))
return TRUE;
// ...then check "aug"
return check_mnemo_tree(mnemo_aug_tree, mnemo_dyna_buf) ? TRUE : FALSE;
}
// check whether mnemonic in GlobalDynaBuf is supported by CSG 4502 cpu.
boolean keyword_is_4502_mnemo(int length)
{
if ((length != 3) && (length != 4))
return FALSE;
// make lower case version of mnemonic in local dynamic buffer
DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf);
// first check 65ce02 extensions because some mnemonics gained new addressing modes...
if (check_mnemo_tree(mnemo_65ce02_tree, mnemo_dyna_buf))
return TRUE;
// ...then check 65c02 extensions because of the same reason...
if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf))
return TRUE;
// ...then check original opcodes...
if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf))
return TRUE;
// ...then check Rockwell extensions (rmb, smb, bbr, bbs)...
if (check_mnemo_tree(mnemo_bitmanips_tree, mnemo_dyna_buf))
return TRUE;
// ...then check "map" and "eom"
return check_mnemo_tree(mnemo_map_eom_tree, mnemo_dyna_buf) ? TRUE : FALSE;
}
// check whether mnemonic in GlobalDynaBuf is supported by 65816 cpu.
boolean keyword_is_65816_mnemo(int length)
{
if (length != 3)
return FALSE;
// make lower case version of mnemonic in local dynamic buffer
DynaBuf_to_lower(mnemo_dyna_buf, GlobalDynaBuf);
// first check 65816 extensions because some mnemonics gained new addressing modes...
if (check_mnemo_tree(mnemo_65816_tree, mnemo_dyna_buf))
return TRUE;
// ...then check 65c02 extensions because of the same reason...
if (check_mnemo_tree(mnemo_65c02_tree, mnemo_dyna_buf))
return TRUE;
// ...then check original opcodes...
if (check_mnemo_tree(mnemo_6502_tree, mnemo_dyna_buf))
return TRUE;
// ...then check WDC extensions "stp" and "wai"
return check_mnemo_tree(mnemo_stp_wai_tree, mnemo_dyna_buf) ? TRUE : FALSE;
}