use pest_derive::Parser;
use std::collections::HashMap;
use tracing::{debug, instrument};
#[derive(Parser)]
#[grammar = "mmixal.pest"]
struct MMixalParser;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SymbolType {
Register(u8), Constant(u64), }
impl std::fmt::Display for SymbolType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SymbolType::Register(reg) => write!(f, "${}", reg),
SymbolType::Constant(val) => write!(f, "{}", val),
}
}
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, PartialEq)]
pub enum MMixInstruction {
SET(u8, u64), SETRR(u8, u8), SETL(u8, u16), SETH(u8, u16), SETMH(u8, u16), SETML(u8, u16), INCH(u8, u16), INCMH(u8, u16), INCML(u8, u16), ORH(u8, u16), ORMH(u8, u16), ORML(u8, u16), ORL(u8, u16), ANDNH(u8, u16), ANDNMH(u8, u16), ANDNML(u8, u16), ANDNL(u8, u16),
LDB(u8, u8, u8), LDBI(u8, u8, u8), LDBU(u8, u8, u8), LDBUI(u8, u8, u8), LDW(u8, u8, u8), LDWI(u8, u8, u8), LDWU(u8, u8, u8), LDWUI(u8, u8, u8), LDT(u8, u8, u8), LDTI(u8, u8, u8), LDTU(u8, u8, u8), LDTUI(u8, u8, u8), LDO(u8, u8, u8), LDOI(u8, u8, u8), LDOU(u8, u8, u8), LDOUI(u8, u8, u8), LDUNC(u8, u8, u8), LDUNCI(u8, u8, u8), LDHT(u8, u8, u8), LDHTI(u8, u8, u8), LDSF(u8, u8, u8), LDSFI(u8, u8, u8), LDVTS(u8, u8, u8), LDVTSI(u8, u8, u8), CSWAP(u8, u8, u8), CSWAPI(u8, u8, u8), LDA(u8, u8, u8), LDAI(u8, u8, u8),
STB(u8, u8, u8), STBI(u8, u8, u8), STBU(u8, u8, u8), STBUI(u8, u8, u8), STW(u8, u8, u8), STWI(u8, u8, u8), STWU(u8, u8, u8), STWUI(u8, u8, u8), STT(u8, u8, u8), STTI(u8, u8, u8), STTU(u8, u8, u8), STTUI(u8, u8, u8), STO(u8, u8, u8), STOI(u8, u8, u8), STOU(u8, u8, u8), STOUI(u8, u8, u8), STUNC(u8, u8, u8), STUNCI(u8, u8, u8), STCO(u8, u8, u8), STCOI(u8, u8, u8), STHT(u8, u8, u8), STHTI(u8, u8, u8), STSF(u8, u8, u8), STSFI(u8, u8, u8),
ADD(u8, u8, u8), ADDI(u8, u8, u8), ADDU(u8, u8, u8), ADDUI(u8, u8, u8), ADDU2(u8, u8, u8), ADDU2I(u8, u8, u8), ADDU4(u8, u8, u8), ADDU4I(u8, u8, u8), ADDU8(u8, u8, u8), ADDU8I(u8, u8, u8), ADDU16(u8, u8, u8), ADDU16I(u8, u8, u8), SUB(u8, u8, u8), SUBI(u8, u8, u8), SUBU(u8, u8, u8), SUBUI(u8, u8, u8), NEG(u8, u8, u8), NEGI(u8, u8, u8), NEGU(u8, u8, u8), NEGUI(u8, u8, u8),
MUL(u8, u8, u8), MULI(u8, u8, u8), MULU(u8, u8, u8), MULUI(u8, u8, u8), DIV(u8, u8, u8), DIVI(u8, u8, u8), DIVU(u8, u8, u8), DIVUI(u8, u8, u8),
FCMP(u8, u8, u8), FUN(u8, u8, u8), FEQL(u8, u8, u8), FADD(u8, u8, u8), FIX(u8, u8, u8), FSUB(u8, u8, u8), FIXU(u8, u8, u8), FLOT(u8, u8, u8), FLOTI(u8, u8, u8), FLOTU(u8, u8, u8), FLOTUI(u8, u8, u8), SFLOT(u8, u8, u8), SFLOTI(u8, u8, u8), SFLOTU(u8, u8, u8), SFLOTUI(u8, u8, u8), FMUL(u8, u8, u8), FDIV(u8, u8, u8), FREM(u8, u8, u8), FSQRT(u8, u8, u8), FINT(u8, u8, u8),
CMP(u8, u8, u8), CMPI(u8, u8, u8), CMPU(u8, u8, u8), CMPUI(u8, u8, u8),
INCL(u8, u8, u8),
AND(u8, u8, u8), ANDI(u8, u8, u8), OR(u8, u8, u8), ORI(u8, u8, u8), XOR(u8, u8, u8), XORI(u8, u8, u8), ANDN(u8, u8, u8), ANDNI(u8, u8, u8), ORN(u8, u8, u8), ORNI(u8, u8, u8), NAND(u8, u8, u8), NANDI(u8, u8, u8), NOR(u8, u8, u8), NORI(u8, u8, u8), NXOR(u8, u8, u8), NXORI(u8, u8, u8), MUX(u8, u8, u8), MUXI(u8, u8, u8),
BDIF(u8, u8, u8), BDIFI(u8, u8, u8), WDIF(u8, u8, u8), WDIFI(u8, u8, u8), TDIF(u8, u8, u8), TDIFI(u8, u8, u8), ODIF(u8, u8, u8), ODIFI(u8, u8, u8), SADD(u8, u8, u8), SADDI(u8, u8, u8), MOR(u8, u8, u8), MORI(u8, u8, u8), MXOR(u8, u8, u8), MXORI(u8, u8, u8),
SL(u8, u8, u8), SLI(u8, u8, u8), SLU(u8, u8, u8), SLUI(u8, u8, u8), SR(u8, u8, u8), SRI(u8, u8, u8), SRU(u8, u8, u8), SRUI(u8, u8, u8),
JMP(u32), JE(u8, u16), JNE(u8, u16), JL(u8, u16), JG(u8, u16), BN(u8, u16), BNB(u8, u16), BZ(u8, u16), BZB(u8, u16), BP(u8, u16), BPB(u8, u16), BOD(u8, u16), BODB(u8, u16), BNN(u8, u16), BNNB(u8, u16), BNZ(u8, u16), BNZB(u8, u16), BNP(u8, u16), BNPB(u8, u16), BEV(u8, u16), BEVB(u8, u16), PBN(u8, u8, u8), PBNB(u8, u8, u8), PBZ(u8, u8, u8), PBZB(u8, u8, u8), PBP(u8, u8, u8), PBPB(u8, u8, u8), PBOD(u8, u8, u8), PBODB(u8, u8, u8), PBNN(u8, u8, u8), PBNNB(u8, u8, u8), PBNZ(u8, u8, u8), PBNZB(u8, u8, u8), PBNP(u8, u8, u8), PBNPB(u8, u8, u8), PBEV(u8, u8, u8), PBEVB(u8, u8, u8),
CSN(u8, u8, u8), CSNI(u8, u8, u8), CSZ(u8, u8, u8), CSZI(u8, u8, u8), CSP(u8, u8, u8), CSPI(u8, u8, u8), CSOD(u8, u8, u8), CSODI(u8, u8, u8), CSNN(u8, u8, u8), CSNNI(u8, u8, u8), CSNZ(u8, u8, u8), CSNZI(u8, u8, u8), CSNP(u8, u8, u8), CSNPI(u8, u8, u8), CSEV(u8, u8, u8), CSEVI(u8, u8, u8),
ZSN(u8, u8, u8), ZSNI(u8, u8, u8), ZSZ(u8, u8, u8), ZSZI(u8, u8, u8), ZSP(u8, u8, u8), ZSPI(u8, u8, u8), ZSOD(u8, u8, u8), ZSODI(u8, u8, u8), ZSNN(u8, u8, u8), ZSNNI(u8, u8, u8), ZSNZ(u8, u8, u8), ZSNZI(u8, u8, u8), ZSNP(u8, u8, u8), ZSNPI(u8, u8, u8), ZSEV(u8, u8, u8), ZSEVI(u8, u8, u8),
TRAP(u8, u8, u8), TRIP(u8, u8, u8), PUSHJ(u8, u8, u8), PUSHJB(u8, u8, u8), PUSHGO(u8, u8, u8), PUSHGOI(u8, u8, u8), POP(u8, u8), GO(u8, u8, u8), GOI(u8, u8, u8), GET(u8, u8), PUT(u8, u8), PUTI(u8, u8), SAVE(u8, u8), UNSAVE(u8, u8), RESUME(u8), SYNC(u8), SWYM, PRELD(u8, u8, u8), PRELDI(u8, u8, u8), PREGO(u8, u8, u8), PREGOI(u8, u8, u8), PREST(u8, u8, u8), PRESTI(u8, u8, u8), SYNCD(u8, u8, u8), SYNCDI(u8, u8, u8), SYNCID(u8, u8, u8), SYNCIDI(u8, u8, u8), GETA(u8, u8, u8), GETAB(u8, u8, u8),
BYTE(u8), WYDE(u16), TETRA(u32), OCTA(u64),
HALT, }
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(clippy::upper_case_acronyms)]
pub enum Opcode {
TRAP = 0x00,
FCMP = 0x01,
FUN = 0x02,
FEQL = 0x03,
FADD = 0x04,
FIX = 0x05,
FSUB = 0x06,
FIXU = 0x07,
FLOT = 0x08,
FLOTI = 0x09,
FLOTU = 0x0A,
FLOTUI = 0x0B,
SFLOT = 0x0C,
SFLOTI = 0x0D,
SFLOTU = 0x0E,
SFLOTUI = 0x0F,
FMUL = 0x10,
FCMPE = 0x11,
FUNE = 0x12,
FEQLE = 0x13,
FDIV = 0x14,
FSQRT = 0x15,
FREM = 0x16,
FINT = 0x17,
MUL = 0x18,
MULI = 0x19,
MULU = 0x1A,
MULUI = 0x1B,
DIV = 0x1C,
DIVI = 0x1D,
DIVU = 0x1E,
DIVUI = 0x1F,
ADD = 0x20,
ADDI = 0x21,
ADDU = 0x22,
ADDUI = 0x23,
SUB = 0x24,
SUBI = 0x25,
SUBU = 0x26,
SUBUI = 0x27,
ADDU2 = 0x28,
ADDU2I = 0x29,
ADDU4 = 0x2A,
ADDU4I = 0x2B,
ADDU8 = 0x2C,
ADDU8I = 0x2D,
ADDU16 = 0x2E,
ADDU16I = 0x2F,
CMP = 0x30,
CMPI = 0x31,
CMPU = 0x32,
CMPUI = 0x33,
NEG = 0x34,
NEGI = 0x35,
NEGU = 0x36,
NEGUI = 0x37,
SL = 0x38,
SLI = 0x39,
SLU = 0x3A,
SLUI = 0x3B,
SR = 0x3C,
SRI = 0x3D,
SRU = 0x3E,
SRUI = 0x3F,
BN = 0x40,
BNB = 0x41,
BZ = 0x42,
BZB = 0x43,
BP = 0x44,
BPB = 0x45,
BOD = 0x46,
BODB = 0x47,
BNN = 0x48,
BNNB = 0x49,
BNZ = 0x4A,
BNZB = 0x4B,
BNP = 0x4C,
BNPB = 0x4D,
BEV = 0x4E,
BEVB = 0x4F,
PBN = 0x50,
PBNB = 0x51,
PBZ = 0x52,
PBZB = 0x53,
PBP = 0x54,
PBPB = 0x55,
PBOD = 0x56,
PBODB = 0x57,
PBNN = 0x58,
PBNNB = 0x59,
PBNZ = 0x5A,
PBNZB = 0x5B,
PBNP = 0x5C,
PBNPB = 0x5D,
PBEV = 0x5E,
PBEVB = 0x5F,
CSN = 0x60,
CSNI = 0x61,
CSZ = 0x62,
CSZI = 0x63,
CSP = 0x64,
CSPI = 0x65,
CSOD = 0x66,
CSODI = 0x67,
CSNN = 0x68,
CSNNI = 0x69,
CSNZ = 0x6A,
CSNZI = 0x6B,
CSNP = 0x6C,
CSNPI = 0x6D,
CSEV = 0x6E,
CSEVI = 0x6F,
ZSN = 0x70,
ZSNI = 0x71,
ZSZ = 0x72,
ZSZI = 0x73,
ZSP = 0x74,
ZSPI = 0x75,
ZSOD = 0x76,
ZSODI = 0x77,
ZSNN = 0x78,
ZSNNI = 0x79,
ZSNZ = 0x7A,
ZSNZI = 0x7B,
ZSNP = 0x7C,
ZSNPI = 0x7D,
ZSEV = 0x7E,
ZSEVI = 0x7F,
LDB = 0x80,
LDBI = 0x81,
LDBU = 0x82,
LDBUI = 0x83,
LDW = 0x84,
LDWI = 0x85,
LDWU = 0x86,
LDWUI = 0x87,
LDT = 0x88,
LDTI = 0x89,
LDTU = 0x8A,
LDTUI = 0x8B,
LDO = 0x8C,
LDOI = 0x8D,
LDOU = 0x8E,
LDOUI = 0x8F,
LDSF = 0x90,
LDSFI = 0x91,
LDHT = 0x92,
LDHTI = 0x93,
CSWAP = 0x94,
CSWAPI = 0x95,
LDUNC = 0x96,
LDUNCI = 0x97,
LDVTS = 0x98,
LDVTSI = 0x99,
PRELD = 0x9A,
PRELDI = 0x9B,
PREGO = 0x9C,
PREGOI = 0x9D,
GO = 0x9E,
GOI = 0x9F,
STB = 0xA0,
STBI = 0xA1,
STBU = 0xA2,
STBUI = 0xA3,
STW = 0xA4,
STWI = 0xA5,
STWU = 0xA6,
STWUI = 0xA7,
STT = 0xA8,
STTI = 0xA9,
STTU = 0xAA,
STTUI = 0xAB,
STO = 0xAC,
STOI = 0xAD,
STOU = 0xAE,
STOUI = 0xAF,
STSF = 0xB0,
STSFI = 0xB1,
STHT = 0xB2,
STHTI = 0xB3,
STCO = 0xB4,
STCOI = 0xB5,
STUNC = 0xB6,
STUNCI = 0xB7,
SYNCD = 0xB8,
SYNCDI = 0xB9,
PREST = 0xBA,
PRESTI = 0xBB,
SYNCID = 0xBC,
SYNCIDI = 0xBD,
PUSHGO = 0xBE,
PUSHGOI = 0xBF,
OR = 0xC0,
ORI = 0xC1,
ORN = 0xC2,
ORNI = 0xC3,
NOR = 0xC4,
NORI = 0xC5,
XOR = 0xC6,
XORI = 0xC7,
AND = 0xC8,
ANDI = 0xC9,
ANDN = 0xCA,
ANDNI = 0xCB,
NAND = 0xCC,
NANDI = 0xCD,
NXOR = 0xCE,
NXORI = 0xCF,
BDIF = 0xD0,
BDIFI = 0xD1,
WDIF = 0xD2,
WDIFI = 0xD3,
TDIF = 0xD4,
TDIFI = 0xD5,
ODIF = 0xD6,
ODIFI = 0xD7,
MUX = 0xD8,
MUXI = 0xD9,
SADD = 0xDA,
SADDI = 0xDB,
MOR = 0xDC,
MORI = 0xDD,
MXOR = 0xDE,
MXORI = 0xDF,
SETH = 0xE0,
SETMH = 0xE1,
SETML = 0xE2,
SETL = 0xE3,
INCH = 0xE4,
INCMH = 0xE5,
INCML = 0xE6,
INCL = 0xE7,
ORH = 0xE8,
ORMH = 0xE9,
ORML = 0xEA,
ORL = 0xEB,
ANDNH = 0xEC,
ANDNMH = 0xED,
ANDNML = 0xEE,
ANDNL = 0xEF,
JMP = 0xF0,
JMPB = 0xF1,
PUSHJ = 0xF2,
PUSHJB = 0xF3,
GETA = 0xF4,
GETAB = 0xF5,
PUT = 0xF6,
PUTI = 0xF7,
POP = 0xF8,
RESUME = 0xF9,
SAVE = 0xFA,
UNSAVE = 0xFB,
SYNC = 0xFC,
SWYM = 0xFD,
GET = 0xFE,
TRIP = 0xFF,
}
impl TryFrom<u8> for Opcode {
type Error = String;
#[allow(unreachable_patterns)]
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x00 => Ok(Opcode::TRAP),
0x01 => Ok(Opcode::FCMP),
0x02 => Ok(Opcode::FUN),
0x03 => Ok(Opcode::FEQL),
0x04 => Ok(Opcode::FADD),
0x05 => Ok(Opcode::FIX),
0x06 => Ok(Opcode::FSUB),
0x07 => Ok(Opcode::FIXU),
0x08 => Ok(Opcode::FLOT),
0x09 => Ok(Opcode::FLOTI),
0x0A => Ok(Opcode::FLOTU),
0x0B => Ok(Opcode::FLOTUI),
0x0C => Ok(Opcode::SFLOT),
0x0D => Ok(Opcode::SFLOTI),
0x0E => Ok(Opcode::SFLOTU),
0x0F => Ok(Opcode::SFLOTUI),
0x10 => Ok(Opcode::FMUL),
0x11 => Ok(Opcode::FCMPE),
0x12 => Ok(Opcode::FUNE),
0x13 => Ok(Opcode::FEQLE),
0x14 => Ok(Opcode::FDIV),
0x15 => Ok(Opcode::FSQRT),
0x16 => Ok(Opcode::FREM),
0x17 => Ok(Opcode::FINT),
0x18 => Ok(Opcode::MUL),
0x19 => Ok(Opcode::MULI),
0x1A => Ok(Opcode::MULU),
0x1B => Ok(Opcode::MULUI),
0x1C => Ok(Opcode::DIV),
0x1D => Ok(Opcode::DIVI),
0x1E => Ok(Opcode::DIVU),
0x1F => Ok(Opcode::DIVUI),
0x20 => Ok(Opcode::ADD),
0x21 => Ok(Opcode::ADDI),
0x22 => Ok(Opcode::ADDU),
0x23 => Ok(Opcode::ADDUI),
0x24 => Ok(Opcode::SUB),
0x25 => Ok(Opcode::SUBI),
0x26 => Ok(Opcode::SUBU),
0x27 => Ok(Opcode::SUBUI),
0x28 => Ok(Opcode::ADDU2),
0x29 => Ok(Opcode::ADDU2I),
0x2A => Ok(Opcode::ADDU4),
0x2B => Ok(Opcode::ADDU4I),
0x2C => Ok(Opcode::ADDU8),
0x2D => Ok(Opcode::ADDU8I),
0x2E => Ok(Opcode::ADDU16),
0x2F => Ok(Opcode::ADDU16I),
0x30 => Ok(Opcode::CMP),
0x31 => Ok(Opcode::CMPI),
0x32 => Ok(Opcode::CMPU),
0x33 => Ok(Opcode::CMPUI),
0x34 => Ok(Opcode::NEG),
0x35 => Ok(Opcode::NEGI),
0x36 => Ok(Opcode::NEGU),
0x37 => Ok(Opcode::NEGUI),
0x38 => Ok(Opcode::SL),
0x39 => Ok(Opcode::SLI),
0x3A => Ok(Opcode::SLU),
0x3B => Ok(Opcode::SLUI),
0x3C => Ok(Opcode::SR),
0x3D => Ok(Opcode::SRI),
0x3E => Ok(Opcode::SRU),
0x3F => Ok(Opcode::SRUI),
0x40 => Ok(Opcode::BN),
0x41 => Ok(Opcode::BNB),
0x42 => Ok(Opcode::BZ),
0x43 => Ok(Opcode::BZB),
0x44 => Ok(Opcode::BP),
0x45 => Ok(Opcode::BPB),
0x46 => Ok(Opcode::BOD),
0x47 => Ok(Opcode::BODB),
0x48 => Ok(Opcode::BNN),
0x49 => Ok(Opcode::BNNB),
0x4A => Ok(Opcode::BNZ),
0x4B => Ok(Opcode::BNZB),
0x4C => Ok(Opcode::BNP),
0x4D => Ok(Opcode::BNPB),
0x4E => Ok(Opcode::BEV),
0x4F => Ok(Opcode::BEVB),
0x50 => Ok(Opcode::PBN),
0x51 => Ok(Opcode::PBNB),
0x52 => Ok(Opcode::PBZ),
0x53 => Ok(Opcode::PBZB),
0x54 => Ok(Opcode::PBP),
0x55 => Ok(Opcode::PBPB),
0x56 => Ok(Opcode::PBOD),
0x57 => Ok(Opcode::PBODB),
0x58 => Ok(Opcode::PBNN),
0x59 => Ok(Opcode::PBNNB),
0x5A => Ok(Opcode::PBNZ),
0x5B => Ok(Opcode::PBNZB),
0x5C => Ok(Opcode::PBNP),
0x5D => Ok(Opcode::PBNPB),
0x5E => Ok(Opcode::PBEV),
0x5F => Ok(Opcode::PBEVB),
0x60 => Ok(Opcode::CSN),
0x61 => Ok(Opcode::CSNI),
0x62 => Ok(Opcode::CSZ),
0x63 => Ok(Opcode::CSZI),
0x64 => Ok(Opcode::CSP),
0x65 => Ok(Opcode::CSPI),
0x66 => Ok(Opcode::CSOD),
0x67 => Ok(Opcode::CSODI),
0x68 => Ok(Opcode::CSNN),
0x69 => Ok(Opcode::CSNNI),
0x6A => Ok(Opcode::CSNZ),
0x6B => Ok(Opcode::CSNZI),
0x6C => Ok(Opcode::CSNP),
0x6D => Ok(Opcode::CSNPI),
0x6E => Ok(Opcode::CSEV),
0x6F => Ok(Opcode::CSEVI),
0x70 => Ok(Opcode::ZSN),
0x71 => Ok(Opcode::ZSNI),
0x72 => Ok(Opcode::ZSZ),
0x73 => Ok(Opcode::ZSZI),
0x74 => Ok(Opcode::ZSP),
0x75 => Ok(Opcode::ZSPI),
0x76 => Ok(Opcode::ZSOD),
0x77 => Ok(Opcode::ZSODI),
0x78 => Ok(Opcode::ZSNN),
0x79 => Ok(Opcode::ZSNNI),
0x7A => Ok(Opcode::ZSNZ),
0x7B => Ok(Opcode::ZSNZI),
0x7C => Ok(Opcode::ZSNP),
0x7D => Ok(Opcode::ZSNPI),
0x7E => Ok(Opcode::ZSEV),
0x7F => Ok(Opcode::ZSEVI),
0x80 => Ok(Opcode::LDB),
0x81 => Ok(Opcode::LDBI),
0x82 => Ok(Opcode::LDBU),
0x83 => Ok(Opcode::LDBUI),
0x84 => Ok(Opcode::LDW),
0x85 => Ok(Opcode::LDWI),
0x86 => Ok(Opcode::LDWU),
0x87 => Ok(Opcode::LDWUI),
0x88 => Ok(Opcode::LDT),
0x89 => Ok(Opcode::LDTI),
0x8A => Ok(Opcode::LDTU),
0x8B => Ok(Opcode::LDTUI),
0x8C => Ok(Opcode::LDO),
0x8D => Ok(Opcode::LDOI),
0x8E => Ok(Opcode::LDOU),
0x8F => Ok(Opcode::LDOUI),
0x90 => Ok(Opcode::LDSF),
0x91 => Ok(Opcode::LDSFI),
0x92 => Ok(Opcode::LDHT),
0x93 => Ok(Opcode::LDHTI),
0x94 => Ok(Opcode::CSWAP),
0x95 => Ok(Opcode::CSWAPI),
0x96 => Ok(Opcode::LDUNC),
0x97 => Ok(Opcode::LDUNCI),
0x98 => Ok(Opcode::LDVTS),
0x99 => Ok(Opcode::LDVTSI),
0x9A => Ok(Opcode::PRELD),
0x9B => Ok(Opcode::PRELDI),
0x9C => Ok(Opcode::PREGO),
0x9D => Ok(Opcode::PREGOI),
0x9E => Ok(Opcode::GO),
0x9F => Ok(Opcode::GOI),
0xA0 => Ok(Opcode::STB),
0xA1 => Ok(Opcode::STBI),
0xA2 => Ok(Opcode::STBU),
0xA3 => Ok(Opcode::STBUI),
0xA4 => Ok(Opcode::STW),
0xA5 => Ok(Opcode::STWI),
0xA6 => Ok(Opcode::STWU),
0xA7 => Ok(Opcode::STWUI),
0xA8 => Ok(Opcode::STT),
0xA9 => Ok(Opcode::STTI),
0xAA => Ok(Opcode::STTU),
0xAB => Ok(Opcode::STTUI),
0xAC => Ok(Opcode::STO),
0xAD => Ok(Opcode::STOI),
0xAE => Ok(Opcode::STOU),
0xAF => Ok(Opcode::STOUI),
0xB0 => Ok(Opcode::STSF),
0xB1 => Ok(Opcode::STSFI),
0xB2 => Ok(Opcode::STHT),
0xB3 => Ok(Opcode::STHTI),
0xB4 => Ok(Opcode::STCO),
0xB5 => Ok(Opcode::STCOI),
0xB6 => Ok(Opcode::STUNC),
0xB7 => Ok(Opcode::STUNCI),
0xB8 => Ok(Opcode::SYNCD),
0xB9 => Ok(Opcode::SYNCDI),
0xBA => Ok(Opcode::PREST),
0xBB => Ok(Opcode::PRESTI),
0xBC => Ok(Opcode::SYNCID),
0xBD => Ok(Opcode::SYNCIDI),
0xBE => Ok(Opcode::PUSHGO),
0xBF => Ok(Opcode::PUSHGOI),
0xC0 => Ok(Opcode::OR),
0xC1 => Ok(Opcode::ORI),
0xC2 => Ok(Opcode::ORN),
0xC3 => Ok(Opcode::ORNI),
0xC4 => Ok(Opcode::NOR),
0xC5 => Ok(Opcode::NORI),
0xC6 => Ok(Opcode::XOR),
0xC7 => Ok(Opcode::XORI),
0xC8 => Ok(Opcode::AND),
0xC9 => Ok(Opcode::ANDI),
0xCA => Ok(Opcode::ANDN),
0xCB => Ok(Opcode::ANDNI),
0xCC => Ok(Opcode::NAND),
0xCD => Ok(Opcode::NANDI),
0xCE => Ok(Opcode::NXOR),
0xCF => Ok(Opcode::NXORI),
0xD0 => Ok(Opcode::BDIF),
0xD1 => Ok(Opcode::BDIFI),
0xD2 => Ok(Opcode::WDIF),
0xD3 => Ok(Opcode::WDIFI),
0xD4 => Ok(Opcode::TDIF),
0xD5 => Ok(Opcode::TDIFI),
0xD6 => Ok(Opcode::ODIF),
0xD7 => Ok(Opcode::ODIFI),
0xD8 => Ok(Opcode::MUX),
0xD9 => Ok(Opcode::MUXI),
0xDA => Ok(Opcode::SADD),
0xDB => Ok(Opcode::SADDI),
0xDC => Ok(Opcode::MOR),
0xDD => Ok(Opcode::MORI),
0xDE => Ok(Opcode::MXOR),
0xDF => Ok(Opcode::MXORI),
0xE0 => Ok(Opcode::SETH),
0xE1 => Ok(Opcode::SETMH),
0xE2 => Ok(Opcode::SETML),
0xE3 => Ok(Opcode::SETL),
0xE4 => Ok(Opcode::INCH),
0xE5 => Ok(Opcode::INCMH),
0xE6 => Ok(Opcode::INCML),
0xE7 => Ok(Opcode::INCL),
0xE8 => Ok(Opcode::ORH),
0xE9 => Ok(Opcode::ORMH),
0xEA => Ok(Opcode::ORML),
0xEB => Ok(Opcode::ORL),
0xEC => Ok(Opcode::ANDNH),
0xED => Ok(Opcode::ANDNMH),
0xEE => Ok(Opcode::ANDNML),
0xEF => Ok(Opcode::ANDNL),
0xF0 => Ok(Opcode::JMP),
0xF1 => Ok(Opcode::JMPB),
0xF2 => Ok(Opcode::PUSHJ),
0xF3 => Ok(Opcode::PUSHJB),
0xF4 => Ok(Opcode::GETA),
0xF5 => Ok(Opcode::GETAB),
0xF6 => Ok(Opcode::PUT),
0xF7 => Ok(Opcode::PUTI),
0xF8 => Ok(Opcode::POP),
0xF9 => Ok(Opcode::RESUME),
0xFA => Ok(Opcode::SAVE),
0xFB => Ok(Opcode::UNSAVE),
0xFC => Ok(Opcode::SYNC),
0xFD => Ok(Opcode::SWYM),
0xFE => Ok(Opcode::GET),
0xFF => Ok(Opcode::TRIP),
_ => Err(format!("Invalid opcode: {:#04x}", value)),
}
}
}
pub struct MMixAssembler {
source: String,
filename: String,
pub labels: HashMap<String, u64>,
pub symbols: HashMap<String, SymbolType>, pub instructions: Vec<(u64, MMixInstruction)>,
current_addr: u64,
next_greg: u8, pub greg_inits: Vec<(u8, u64)>, }
impl MMixAssembler {
fn preprocess_debug(source: &str) -> String {
use regex::Regex;
let debug_re = Regex::new(r#"(?m)^([^\s]*\s+)?debug\s+"([^"]*)"\s*$"#).unwrap();
let mut counter = 0;
let mut result = String::new();
let mut debug_strings = Vec::new();
for line in source.lines() {
if let Some(caps) = debug_re.captures(line) {
let prefix = caps.get(1).map(|m| m.as_str()).unwrap_or("");
let text = &caps[2];
counter += 1;
let label = format!("DbgStr_{:04}", counter);
debug!(
"Preprocessing debug directive: \"{}\" -> label {}",
text, label
);
if !prefix.trim().is_empty() {
result.push_str(prefix.trim());
result.push('\n');
}
result.push_str(&format!("\tPUSHJ\t$0,{}\n", label));
debug_strings.push((label, text.to_string()));
} else {
result.push_str(line);
result.push('\n');
}
}
if !debug_strings.is_empty() {
result.push('\n');
result.push_str("; Debug subroutines generated by preprocessor\n");
for (label, text) in debug_strings {
result.push_str(&format!("{} \tSAVE\t$254,0\n", label));
result.push_str(&format!("\tGETA\t$0,{}Str\n", label));
result.push_str("\tTRAP\t0,Fputs,StdOut\n");
result.push_str("\tUNSAVE\t0,$254\n");
result.push_str("\tPOP\t0,0\n"); result.push_str(&format!("{}Str\tBYTE\t\"{}\",#a,0\n", label, text));
}
}
debug!("Preprocessed source:\n{}", result);
result
}
pub fn new(source: &str, filename: &str) -> Self {
let mut symbols = HashMap::new();
symbols.insert(
"Data_Segment".to_string(),
SymbolType::Constant(0x2000000000000000),
);
symbols.insert(
"Pool_Segment".to_string(),
SymbolType::Constant(0x4000000000000000),
);
symbols.insert(
"Stack_Segment".to_string(),
SymbolType::Constant(0x6000000000000000),
);
symbols.insert("StdIn".to_string(), SymbolType::Constant(0));
symbols.insert("StdOut".to_string(), SymbolType::Constant(1));
symbols.insert("StdErr".to_string(), SymbolType::Constant(2));
symbols.insert("Halt".to_string(), SymbolType::Constant(0));
symbols.insert("Fopen".to_string(), SymbolType::Constant(1));
symbols.insert("Fclose".to_string(), SymbolType::Constant(2));
symbols.insert("Fread".to_string(), SymbolType::Constant(3));
symbols.insert("Fgets".to_string(), SymbolType::Constant(4));
symbols.insert("Fgetws".to_string(), SymbolType::Constant(5));
symbols.insert("Fwrite".to_string(), SymbolType::Constant(6));
symbols.insert("Fputs".to_string(), SymbolType::Constant(7));
symbols.insert("Fputc".to_string(), SymbolType::Constant(8));
symbols.insert("Fputws".to_string(), SymbolType::Constant(9));
symbols.insert("Fseek".to_string(), SymbolType::Constant(10));
symbols.insert("Ftell".to_string(), SymbolType::Constant(11));
let preprocessed_source = Self::preprocess_debug(source);
Self {
source: preprocessed_source,
filename: filename.to_string(),
labels: HashMap::new(),
symbols,
instructions: Vec::new(),
current_addr: 0,
next_greg: 254, greg_inits: Vec::new(),
}
}
fn format_parse_error(error: &pest::error::Error<Rule>, filename: &str) -> String {
use pest::error::LineColLocation;
let (line, col) = match error.line_col {
LineColLocation::Pos((l, c)) => (l, c),
LineColLocation::Span((l, c), _) => (l, c),
};
let expected_msg = match &error.variant {
pest::error::ErrorVariant::ParsingError { positives, .. } => {
if positives.is_empty() {
"valid MMIX instruction or directive".to_string()
} else {
let friendly: Vec<String> = positives
.iter()
.map(|r| match r {
Rule::instruction => "instruction".to_string(),
Rule::directive => "directive".to_string(),
Rule::directive_is => "IS directive (symbol definition)".to_string(),
Rule::directive_loc => "LOC directive".to_string(),
Rule::register => "register (e.g., $0, $1, $255)".to_string(),
Rule::expr_value => "number or expression".to_string(),
Rule::identifier => "label or symbol name".to_string(),
_ => format!("{:?}", r),
})
.collect();
if friendly.len() == 1 {
friendly[0].clone()
} else {
format!("one of: {}", friendly.join(", "))
}
}
}
pest::error::ErrorVariant::CustomError { message } => message.clone(),
};
format!(
"{}:{}:{}: syntax error: expected {}",
filename, line, col, expected_msg
)
}
#[instrument(skip(self), fields(source_len = self.source.len()))]
pub fn parse(&mut self) -> Result<(), String> {
debug!("Starting MMIXAL parsing (two-pass)");
match self.parse_two_pass() {
Ok(_) => {
debug!(
instruction_count = self.instructions.len(),
label_count = self.labels.len(),
symbol_count = self.symbols.len(),
"Parsing completed successfully"
);
Ok(())
}
Err(e) => Err(e),
}
}
#[instrument(skip(self))]
fn parse_two_pass(&mut self) -> Result<(), String> {
use pest::Parser;
let source = self.source.clone();
debug!("Pass 1: Collecting labels and symbols");
let pairs = MMixalParser::parse(Rule::program, &source).map_err(|e| {
Self::format_parse_error(&e, &self.filename)
})?;
for pair in pairs {
if pair.as_rule() == Rule::program {
for line_pair in pair.into_inner() {
if line_pair.as_rule() == Rule::line {
for stmt_pair in line_pair.into_inner() {
if stmt_pair.as_rule() == Rule::statement {
self.first_pass_statement(stmt_pair)?;
}
}
}
}
}
}
debug!(
"Pass 1 complete: {} labels, {} symbols",
self.labels.len(),
self.symbols.len()
);
let saved_addr = self.current_addr;
self.current_addr = 0;
debug!("Pass 2: Generating instructions");
let pairs = MMixalParser::parse(Rule::program, &source).map_err(|e| format!("{}", e))?;
for pair in pairs {
if pair.as_rule() == Rule::program {
for line_pair in pair.into_inner() {
if line_pair.as_rule() == Rule::line {
for stmt_pair in line_pair.into_inner() {
if stmt_pair.as_rule() == Rule::statement {
self.second_pass_statement(stmt_pair)?;
}
}
}
}
}
}
self.current_addr = saved_addr;
Ok(())
}
#[instrument(skip(self, pair), fields(current_addr = format!("0x{:X}", self.current_addr)))]
fn first_pass_statement(&mut self, pair: pest::iterators::Pair<Rule>) -> Result<(), String> {
let mut label_name: Option<String> = None;
for inner_pair in pair.into_inner() {
match inner_pair.as_rule() {
Rule::label_def => {
let ident = inner_pair.into_inner().next().unwrap();
label_name = Some(ident.as_str().to_string());
}
Rule::instruction => {
let inst = self.peek_instruction_type(inner_pair)?;
let size = Self::instruction_size(&inst);
if let Some(label) = label_name.take() {
debug!(label = %label, addr = format!("0x{:X}", self.current_addr), "Collected label");
self.labels.insert(label, self.current_addr);
}
self.current_addr += size;
}
Rule::directive => {
let directive_pair = inner_pair.into_inner().next().unwrap();
match directive_pair.as_rule() {
Rule::data_directive => {
let size = self.data_directive_size(directive_pair.clone())?;
if let Some(label) = label_name.take() {
debug!(label = %label, addr = format!("0x{:X}", self.current_addr), "Collected label");
self.labels.insert(label, self.current_addr);
}
self.current_addr += size;
}
Rule::loc_directive => {
self.parse_loc_directive(directive_pair)?;
if let Some(label) = label_name.take() {
self.labels.insert(label, self.current_addr);
}
}
Rule::greg_directive => {
let allocated_reg = if self.next_greg == 0 {
return Err(
"Too many GREG directives - ran out of global registers"
.to_string(),
);
} else {
let reg = self.next_greg;
self.next_greg -= 1;
reg
};
if let Some(label) = label_name.take() {
self.symbols
.insert(label, SymbolType::Register(allocated_reg));
}
let mut greg_parts = directive_pair.clone().into_inner();
let _directive = greg_parts.next();
let value = self.parse_number(greg_parts.next().unwrap())?;
self.greg_inits.push((allocated_reg, value));
}
Rule::is_directive => {
self.parse_is_directive(directive_pair)?;
}
_ => {}
}
}
_ => {}
}
}
if let Some(label) = label_name {
debug!(label = %label, addr = format!("0x{:X}", self.current_addr), "Collected standalone label");
self.labels.insert(label, self.current_addr);
}
Ok(())
}
#[instrument(skip(self, pair), fields(current_addr = format!("0x{:X}", self.current_addr)))]
fn second_pass_statement(&mut self, pair: pest::iterators::Pair<Rule>) -> Result<(), String> {
let mut label_name: Option<String> = None;
let mut inst: Option<MMixInstruction> = None;
for inner_pair in pair.into_inner() {
match inner_pair.as_rule() {
Rule::label_def => {
let ident = inner_pair.into_inner().next().unwrap();
label_name = Some(ident.as_str().to_string());
}
Rule::instruction => {
if let Some(label) = label_name.take() {
debug!(label = %label, addr = format!("0x{:X}", self.current_addr), "Defined label");
self.labels.insert(label, self.current_addr);
}
inst = Some(self.parse_instruction(inner_pair)?);
}
Rule::directive => {
let directive_pair = inner_pair.into_inner().next().unwrap();
match directive_pair.as_rule() {
Rule::data_directive => {
if let Some(label) = label_name.take() {
debug!(label = %label, addr = format!("0x{:X}", self.current_addr), "Defined label");
self.labels.insert(label, self.current_addr);
}
let instructions = self.parse_data_directive(directive_pair)?;
for instruction in instructions {
let size = Self::instruction_size(&instruction);
debug!(inst = ?instruction, addr = format!("0x{:X}", self.current_addr), size, "Added instruction");
self.instructions.push((self.current_addr, instruction));
self.current_addr += size;
}
}
Rule::loc_directive => {
self.parse_loc_directive(directive_pair)?;
}
Rule::greg_directive => {
if let Some(label) = label_name.take()
&& !self.symbols.contains_key(&label)
{
return Err(format!(
"Internal error: GREG label '{}' not found in symbols from first pass",
label
));
}
}
Rule::is_directive => {
self.parse_is_directive(directive_pair)?;
}
_ => {}
}
}
_ => {}
}
}
if let Some(instruction) = inst {
let size = Self::instruction_size(&instruction);
debug!(inst = ?instruction, addr = format!("0x{:X}", self.current_addr), size, "Added instruction");
self.instructions.push((self.current_addr, instruction));
self.current_addr += size;
}
if let Some(label) = label_name {
debug!(label = %label, addr = format!("0x{:X}", self.current_addr), "Defined standalone label");
self.labels.insert(label, self.current_addr);
}
Ok(())
}
fn peek_instruction_type(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let inner = pair.into_inner().next().ok_or("Empty instruction")?;
match inner.as_rule() {
Rule::inst_set => Ok(MMixInstruction::SETRR(0, 0)), Rule::inst_seti => Ok(MMixInstruction::SET(0, 0)), Rule::inst_setl_ri => Ok(MMixInstruction::SETL(0, 0)),
Rule::inst_seth_ri => Ok(MMixInstruction::SETH(0, 0)),
Rule::inst_setmh_ri => Ok(MMixInstruction::SETMH(0, 0)),
Rule::inst_setml_ri => Ok(MMixInstruction::SETML(0, 0)),
Rule::inst_incl_rrr => Ok(MMixInstruction::INCL(0, 0, 0)),
Rule::inst_lda_ri | Rule::inst_lda_rri => {
let mut parts = inner.clone().into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let _x = ops.next(); let addr_pair = ops.next().unwrap();
match self.parse_number(addr_pair) {
Ok(addr) if addr <= 0xFF => Ok(MMixInstruction::LDA(0, 0, 0)), _ => Ok(MMixInstruction::SET(0, 0)), }
}
Rule::inst_halt => Ok(MMixInstruction::HALT),
_ => Ok(MMixInstruction::ADDU(0, 0, 0)),
}
}
fn data_directive_size(&self, pair: pest::iterators::Pair<Rule>) -> Result<u64, String> {
let mut parts = pair.clone().into_inner();
let directive = parts.next().ok_or("Empty data directive")?;
match directive.as_rule() {
Rule::directive_byte => {
let mut total_size = 0u64;
let byte_values = parts.next().ok_or("Missing byte values")?;
for byte_value in byte_values.into_inner() {
let mut value_parts = byte_value.into_inner();
let first = value_parts.next().unwrap();
if first.as_rule() == Rule::string_literal {
let text = first.as_str();
let content = &text[1..text.len() - 1];
total_size += content.len() as u64;
debug!("BYTE string: \"{}\" = {} bytes", content, content.len());
} else {
total_size += 1;
debug!("BYTE value: 1 byte");
}
}
debug!("Total BYTE size: {} bytes", total_size);
Ok(total_size)
}
Rule::directive_wyde => Ok(2),
Rule::directive_tetra => Ok(4),
Rule::directive_octa => Ok(8),
_ => Err(format!("Unknown data directive: {:?}", directive.as_rule())),
}
}
fn parse_instruction(
&mut self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let inner = pair.into_inner().next().ok_or("Empty instruction")?;
match inner.as_rule() {
Rule::inst_set => self.parse_inst_set(inner),
Rule::inst_seti => self.parse_inst_seti(inner),
Rule::inst_setl_ri => self.parse_inst_setl(inner),
Rule::inst_seth_ri => self.parse_inst_seth(inner),
Rule::inst_setmh_ri => self.parse_inst_setmh(inner),
Rule::inst_setml_ri => self.parse_inst_setml(inner),
Rule::inst_incl_rrr => self.parse_inst_incl(inner),
Rule::inst_inch_ri => self.parse_inst_inch(inner),
Rule::inst_incmh_ri => self.parse_inst_incmh(inner),
Rule::inst_incml_ri => self.parse_inst_incml(inner),
Rule::inst_orh_ri => self.parse_inst_orh(inner),
Rule::inst_ormh_ri => self.parse_inst_ormh(inner),
Rule::inst_orml_ri => self.parse_inst_orml(inner),
Rule::inst_orl_ri => self.parse_inst_orl(inner),
Rule::inst_andnh_ri => self.parse_inst_andnh(inner),
Rule::inst_andnmh_ri => self.parse_inst_andnmh(inner),
Rule::inst_andnml_ri => self.parse_inst_andnml(inner),
Rule::inst_andnl_ri => self.parse_inst_andnl(inner),
Rule::inst_load_store_rrr => self.parse_inst_load_store_rrr(inner),
Rule::inst_load_store_rri => self.parse_inst_load_store_rri(inner),
Rule::inst_lda_rri => self.parse_inst_lda_rri(inner),
Rule::inst_lda_ri => self.parse_inst_lda_ri(inner),
Rule::inst_arith_rrr => self.parse_inst_arith_rrr(inner),
Rule::inst_arith_rri => self.parse_inst_arith_rri(inner),
Rule::inst_float_rrr => self.parse_inst_float_rrr(inner),
Rule::inst_float_rri => self.parse_inst_float_rri(inner),
Rule::inst_neg_rrr => self.parse_inst_neg_rrr(inner),
Rule::inst_neg_rri => self.parse_inst_neg_rri(inner),
Rule::inst_bitwise_rrr => self.parse_inst_bitwise_rrr(inner),
Rule::inst_bitwise_rri => self.parse_inst_bitwise_rri(inner),
Rule::inst_bitfiddle_rrr => self.parse_inst_bitfiddle_rrr(inner),
Rule::inst_bitfiddle_rri => self.parse_inst_bitfiddle_rri(inner),
Rule::inst_shift_rrr => self.parse_inst_shift_rrr(inner),
Rule::inst_shift_rri => self.parse_inst_shift_rri(inner),
Rule::inst_conditional_set_rrr => self.parse_inst_conditional_set_rrr(inner),
Rule::inst_conditional_set_rri => self.parse_inst_conditional_set_rri(inner),
Rule::inst_zero_or_set_rrr => self.parse_inst_zero_or_set_rrr(inner),
Rule::inst_zero_or_set_rri => self.parse_inst_zero_or_set_rri(inner),
Rule::inst_pbranch => self.parse_inst_pbranch(inner),
Rule::inst_branch => self.parse_inst_branch(inner),
Rule::inst_jmp => self.parse_inst_jmp(inner),
Rule::inst_geta => self.parse_inst_geta(inner),
Rule::inst_getab => self.parse_inst_getab(inner),
Rule::inst_pushj => self.parse_inst_pushj(inner),
Rule::inst_pushjb => self.parse_inst_pushjb(inner),
Rule::inst_pushgo_rrr => self.parse_inst_pushgo_rrr(inner),
Rule::inst_pushgo_rri => self.parse_inst_pushgo_rri(inner),
Rule::inst_pop => self.parse_inst_pop(inner),
Rule::inst_go_rrr => self.parse_inst_go_rrr(inner),
Rule::inst_go_rri => self.parse_inst_go_rri(inner),
Rule::inst_get => self.parse_inst_get(inner),
Rule::inst_put => self.parse_inst_put(inner),
Rule::inst_puti => self.parse_inst_puti(inner),
Rule::inst_save => self.parse_inst_save(inner),
Rule::inst_unsave => self.parse_inst_unsave(inner),
Rule::inst_ldunc_rrr => self.parse_inst_ldunc_rrr(inner),
Rule::inst_ldunc_rri => self.parse_inst_ldunc_rri(inner),
Rule::inst_stunc_rrr => self.parse_inst_stunc_rrr(inner),
Rule::inst_stunc_rri => self.parse_inst_stunc_rri(inner),
Rule::inst_ldht_rrr => self.parse_inst_ldht_rrr(inner),
Rule::inst_ldht_rri => self.parse_inst_ldht_rri(inner),
Rule::inst_stht_rrr => self.parse_inst_stht_rrr(inner),
Rule::inst_stht_rri => self.parse_inst_stht_rri(inner),
Rule::inst_ldsf_rrr => self.parse_inst_ldsf_rrr(inner),
Rule::inst_ldsf_rri => self.parse_inst_ldsf_rri(inner),
Rule::inst_stsf_rrr => self.parse_inst_stsf_rrr(inner),
Rule::inst_stsf_rri => self.parse_inst_stsf_rri(inner),
Rule::inst_ldvts_rrr => self.parse_inst_ldvts_rrr(inner),
Rule::inst_ldvts_rri => self.parse_inst_ldvts_rri(inner),
Rule::inst_cswap_rrr => self.parse_inst_cswap_rrr(inner),
Rule::inst_cswap_rri => self.parse_inst_cswap_rri(inner),
Rule::inst_stco_rrr => self.parse_inst_stco_rrr(inner),
Rule::inst_stco_rri => self.parse_inst_stco_rri(inner),
Rule::inst_preld_rrr => self.parse_inst_preld_rrr(inner),
Rule::inst_preld_rri => self.parse_inst_preld_rri(inner),
Rule::inst_prego_rrr => self.parse_inst_prego_rrr(inner),
Rule::inst_prego_rri => self.parse_inst_prego_rri(inner),
Rule::inst_prest_rrr => self.parse_inst_prest_rrr(inner),
Rule::inst_prest_rri => self.parse_inst_prest_rri(inner),
Rule::inst_syncd_rrr => self.parse_inst_syncd_rrr(inner),
Rule::inst_syncd_rri => self.parse_inst_syncd_rri(inner),
Rule::inst_syncid_rrr => self.parse_inst_syncid_rrr(inner),
Rule::inst_syncid_rri => self.parse_inst_syncid_rri(inner),
Rule::inst_resume => self.parse_inst_resume(inner),
Rule::inst_trip => self.parse_inst_trip(inner),
Rule::inst_swym => Ok(MMixInstruction::SWYM),
Rule::inst_sync => self.parse_inst_sync(inner),
Rule::inst_trap => self.parse_inst_trap(inner),
Rule::inst_halt => Ok(MMixInstruction::HALT),
_ => Err(format!(
"Unsupported instruction rule: {:?}",
inner.as_rule()
)),
}
}
fn parse_inst_set(&self, pair: pest::iterators::Pair<Rule>) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next(); let operands = parts.next().unwrap(); let mut ops = operands.into_inner();
let dest_reg = self.parse_register(ops.next().unwrap())?;
let src_reg = self.parse_register(ops.next().unwrap())?;
Ok(MMixInstruction::SETRR(dest_reg, src_reg))
}
fn parse_inst_seti(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next(); let operands = parts.next().unwrap(); let mut ops = operands.into_inner();
let dest_reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())?;
Ok(MMixInstruction::SET(dest_reg, val))
}
fn parse_inst_setl(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::SETL(reg, val))
}
fn parse_inst_seth(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::SETH(reg, val))
}
fn parse_inst_setmh(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::SETMH(reg, val))
}
fn parse_inst_setml(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::SETML(reg, val))
}
fn parse_inst_incl(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
Ok(MMixInstruction::INCL(x, y, z))
}
fn parse_inst_inch(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::INCH(reg, val))
}
fn parse_inst_incmh(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::INCMH(reg, val))
}
fn parse_inst_incml(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::INCML(reg, val))
}
fn parse_inst_orh(&self, pair: pest::iterators::Pair<Rule>) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::ORH(reg, val))
}
fn parse_inst_ormh(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::ORMH(reg, val))
}
fn parse_inst_orml(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::ORML(reg, val))
}
fn parse_inst_orl(&self, pair: pest::iterators::Pair<Rule>) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::ORL(reg, val))
}
fn parse_inst_andnh(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::ANDNH(reg, val))
}
fn parse_inst_andnmh(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::ANDNMH(reg, val))
}
fn parse_inst_andnml(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::ANDNML(reg, val))
}
fn parse_inst_andnl(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let reg = self.parse_register(ops.next().unwrap())?;
let val = self.parse_number(ops.next().unwrap())? as u16;
Ok(MMixInstruction::ANDNL(reg, val))
}
fn parse_inst_load_store_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"LDB" => Ok(MMixInstruction::LDB(x, y, z)),
"LDBU" => Ok(MMixInstruction::LDBU(x, y, z)),
"LDW" => Ok(MMixInstruction::LDW(x, y, z)),
"LDWU" => Ok(MMixInstruction::LDWU(x, y, z)),
"LDT" => Ok(MMixInstruction::LDT(x, y, z)),
"LDTU" => Ok(MMixInstruction::LDTU(x, y, z)),
"LDO" => Ok(MMixInstruction::LDO(x, y, z)),
"LDOU" => Ok(MMixInstruction::LDOU(x, y, z)),
"LDA" => Ok(MMixInstruction::LDA(x, y, z)),
"STB" => Ok(MMixInstruction::STB(x, y, z)),
"STBU" => Ok(MMixInstruction::STBU(x, y, z)),
"STW" => Ok(MMixInstruction::STW(x, y, z)),
"STWU" => Ok(MMixInstruction::STWU(x, y, z)),
"STT" => Ok(MMixInstruction::STT(x, y, z)),
"STTU" => Ok(MMixInstruction::STTU(x, y, z)),
"STO" => Ok(MMixInstruction::STO(x, y, z)),
"STOU" => Ok(MMixInstruction::STOU(x, y, z)),
_ => Err(format!("Unknown load/store instruction: {}", mnem.as_str())),
}
}
fn parse_inst_load_store_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"LDBI" => Ok(MMixInstruction::LDBI(x, y, z)),
"LDBUI" => Ok(MMixInstruction::LDBUI(x, y, z)),
"LDWI" => Ok(MMixInstruction::LDWI(x, y, z)),
"LDWUI" => Ok(MMixInstruction::LDWUI(x, y, z)),
"LDTI" => Ok(MMixInstruction::LDTI(x, y, z)),
"LDTUI" => Ok(MMixInstruction::LDTUI(x, y, z)),
"LDOI" => Ok(MMixInstruction::LDOI(x, y, z)),
"LDOUI" => Ok(MMixInstruction::LDOUI(x, y, z)),
"STBI" => Ok(MMixInstruction::STBI(x, y, z)),
"STBUI" => Ok(MMixInstruction::STBUI(x, y, z)),
"STWI" => Ok(MMixInstruction::STWI(x, y, z)),
"STWUI" => Ok(MMixInstruction::STWUI(x, y, z)),
"STTI" => Ok(MMixInstruction::STTI(x, y, z)),
"STTUI" => Ok(MMixInstruction::STTUI(x, y, z)),
"STOI" => Ok(MMixInstruction::STOI(x, y, z)),
"STOUI" => Ok(MMixInstruction::STOUI(x, y, z)),
_ => Err(format!("Unknown load/store instruction: {}", mnem.as_str())),
}
}
fn parse_inst_lda_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"LDA" => Ok(MMixInstruction::LDA(x, y, z)),
"LDAI" => Ok(MMixInstruction::LDAI(x, y, z)),
_ => Err(format!("Unknown LDA instruction: {}", mnem.as_str())),
}
}
fn parse_inst_lda_ri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let addr_value = self.parse_number(ops.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"LDA" => {
if addr_value <= 0xFF {
Ok(MMixInstruction::LDA(x, 0, addr_value as u8))
} else {
debug!("LDA with large address {:#x} converted to SET", addr_value);
Ok(MMixInstruction::SET(x, addr_value))
}
}
"LDAI" => {
if addr_value <= 0xFF {
Ok(MMixInstruction::LDAI(x, 0, addr_value as u8))
} else {
Ok(MMixInstruction::SET(x, addr_value))
}
}
_ => Err(format!("Unknown LDA instruction: {}", mnem.as_str())),
}
}
fn parse_inst_arith_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"ADD" => Ok(MMixInstruction::ADD(x, y, z)),
"ADDU" => Ok(MMixInstruction::ADDU(x, y, z)),
"2ADDU" => Ok(MMixInstruction::ADDU2(x, y, z)),
"4ADDU" => Ok(MMixInstruction::ADDU4(x, y, z)),
"8ADDU" => Ok(MMixInstruction::ADDU8(x, y, z)),
"16ADDU" => Ok(MMixInstruction::ADDU16(x, y, z)),
"SUB" => Ok(MMixInstruction::SUB(x, y, z)),
"SUBU" => Ok(MMixInstruction::SUBU(x, y, z)),
"MUL" => Ok(MMixInstruction::MUL(x, y, z)),
"MULU" => Ok(MMixInstruction::MULU(x, y, z)),
"DIV" => Ok(MMixInstruction::DIV(x, y, z)),
"DIVU" => Ok(MMixInstruction::DIVU(x, y, z)),
"CMP" => Ok(MMixInstruction::CMP(x, y, z)),
"CMPU" => Ok(MMixInstruction::CMPU(x, y, z)),
_ => Err(format!("Unknown arithmetic instruction: {}", mnem.as_str())),
}
}
fn parse_inst_arith_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"ADDI" => Ok(MMixInstruction::ADDI(x, y, z)),
"ADDUI" => Ok(MMixInstruction::ADDUI(x, y, z)),
"2ADDUI" => Ok(MMixInstruction::ADDU2I(x, y, z)),
"4ADDUI" => Ok(MMixInstruction::ADDU4I(x, y, z)),
"8ADDUI" => Ok(MMixInstruction::ADDU8I(x, y, z)),
"16ADDUI" => Ok(MMixInstruction::ADDU16I(x, y, z)),
"SUBI" => Ok(MMixInstruction::SUBI(x, y, z)),
"SUBUI" => Ok(MMixInstruction::SUBUI(x, y, z)),
"MULI" => Ok(MMixInstruction::MULI(x, y, z)),
"MULUI" => Ok(MMixInstruction::MULUI(x, y, z)),
"DIVI" => Ok(MMixInstruction::DIVI(x, y, z)),
"DIVUI" => Ok(MMixInstruction::DIVUI(x, y, z)),
"CMPI" => Ok(MMixInstruction::CMPI(x, y, z)),
"CMPUI" => Ok(MMixInstruction::CMPUI(x, y, z)),
_ => Err(format!("Unknown arithmetic instruction: {}", mnem.as_str())),
}
}
fn parse_inst_neg_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let x = self.parse_register(parts.next().unwrap())?;
let y = self.parse_number(parts.next().unwrap())? as u8;
let z = self.parse_register(parts.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"NEG" => Ok(MMixInstruction::NEG(x, y, z)),
"NEGU" => Ok(MMixInstruction::NEGU(x, y, z)),
_ => Err(format!("Unknown NEG instruction: {}", mnem.as_str())),
}
}
fn parse_inst_neg_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let x = self.parse_register(parts.next().unwrap())?;
let y = self.parse_number(parts.next().unwrap())? as u8;
let z = self.parse_number(parts.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"NEGI" => Ok(MMixInstruction::NEGI(x, y, z)),
"NEGUI" => Ok(MMixInstruction::NEGUI(x, y, z)),
_ => Err(format!("Unknown NEG instruction: {}", mnem.as_str())),
}
}
fn parse_inst_float_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"FCMP" => Ok(MMixInstruction::FCMP(x, y, z)),
"FUN" => Ok(MMixInstruction::FUN(x, y, z)),
"FEQL" => Ok(MMixInstruction::FEQL(x, y, z)),
"FADD" => Ok(MMixInstruction::FADD(x, y, z)),
"FSUB" => Ok(MMixInstruction::FSUB(x, y, z)),
"FMUL" => Ok(MMixInstruction::FMUL(x, y, z)),
"FDIV" => Ok(MMixInstruction::FDIV(x, y, z)),
"FREM" => Ok(MMixInstruction::FREM(x, y, z)),
"FSQRT" => Ok(MMixInstruction::FSQRT(x, y, z)),
"FINT" => Ok(MMixInstruction::FINT(x, y, z)),
"FIX" => Ok(MMixInstruction::FIX(x, y, z)),
"FIXU" => Ok(MMixInstruction::FIXU(x, y, z)),
"FLOT" => Ok(MMixInstruction::FLOT(x, y, z)),
"FLOTU" => Ok(MMixInstruction::FLOTU(x, y, z)),
"SFLOT" => Ok(MMixInstruction::SFLOT(x, y, z)),
"SFLOTU" => Ok(MMixInstruction::SFLOTU(x, y, z)),
_ => Err(format!(
"Unknown floating point instruction: {}",
mnem.as_str()
)),
}
}
fn parse_inst_float_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"FLOTI" => Ok(MMixInstruction::FLOTI(x, y, z)),
"FLOTUI" => Ok(MMixInstruction::FLOTUI(x, y, z)),
"SFLOTI" => Ok(MMixInstruction::SFLOTI(x, y, z)),
"SFLOTUI" => Ok(MMixInstruction::SFLOTUI(x, y, z)),
_ => Err(format!(
"Unknown floating point immediate instruction: {}",
mnem.as_str()
)),
}
}
fn parse_inst_bitwise_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"AND" => Ok(MMixInstruction::AND(x, y, z)),
"OR" => Ok(MMixInstruction::OR(x, y, z)),
"XOR" => Ok(MMixInstruction::XOR(x, y, z)),
"ANDN" => Ok(MMixInstruction::ANDN(x, y, z)),
"ORN" => Ok(MMixInstruction::ORN(x, y, z)),
"NAND" => Ok(MMixInstruction::NAND(x, y, z)),
"NOR" => Ok(MMixInstruction::NOR(x, y, z)),
"NXOR" => Ok(MMixInstruction::NXOR(x, y, z)),
"MUX" => Ok(MMixInstruction::MUX(x, y, z)),
_ => Err(format!("Unknown bitwise instruction: {}", mnem.as_str())),
}
}
fn parse_inst_bitwise_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"ANDI" => Ok(MMixInstruction::ANDI(x, y, z)),
"ORI" => Ok(MMixInstruction::ORI(x, y, z)),
"XORI" => Ok(MMixInstruction::XORI(x, y, z)),
"ANDNI" => Ok(MMixInstruction::ANDNI(x, y, z)),
"ORNI" => Ok(MMixInstruction::ORNI(x, y, z)),
"NANDI" => Ok(MMixInstruction::NANDI(x, y, z)),
"NORI" => Ok(MMixInstruction::NORI(x, y, z)),
"NXORI" => Ok(MMixInstruction::NXORI(x, y, z)),
"MUXI" => Ok(MMixInstruction::MUXI(x, y, z)),
_ => Err(format!("Unknown bitwise instruction: {}", mnem.as_str())),
}
}
fn parse_inst_bitfiddle_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"BDIF" => Ok(MMixInstruction::BDIF(x, y, z)),
"WDIF" => Ok(MMixInstruction::WDIF(x, y, z)),
"TDIF" => Ok(MMixInstruction::TDIF(x, y, z)),
"ODIF" => Ok(MMixInstruction::ODIF(x, y, z)),
"SADD" => Ok(MMixInstruction::SADD(x, y, z)),
"MOR" => Ok(MMixInstruction::MOR(x, y, z)),
"MXOR" => Ok(MMixInstruction::MXOR(x, y, z)),
_ => Err(format!(
"Unknown bit fiddling instruction: {}",
mnem.as_str()
)),
}
}
fn parse_inst_bitfiddle_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"BDIFI" => Ok(MMixInstruction::BDIFI(x, y, z)),
"WDIFI" => Ok(MMixInstruction::WDIFI(x, y, z)),
"TDIFI" => Ok(MMixInstruction::TDIFI(x, y, z)),
"ODIFI" => Ok(MMixInstruction::ODIFI(x, y, z)),
"SADDI" => Ok(MMixInstruction::SADDI(x, y, z)),
"MORI" => Ok(MMixInstruction::MORI(x, y, z)),
"MXORI" => Ok(MMixInstruction::MXORI(x, y, z)),
_ => Err(format!(
"Unknown bit fiddling instruction: {}",
mnem.as_str()
)),
}
}
fn parse_inst_shift_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"SL" => Ok(MMixInstruction::SL(x, y, z)),
"SLU" => Ok(MMixInstruction::SLU(x, y, z)),
"SR" => Ok(MMixInstruction::SR(x, y, z)),
"SRU" => Ok(MMixInstruction::SRU(x, y, z)),
_ => Err(format!("Unknown shift instruction: {}", mnem.as_str())),
}
}
fn parse_inst_shift_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"SLI" => Ok(MMixInstruction::SLI(x, y, z)),
"SLUI" => Ok(MMixInstruction::SLUI(x, y, z)),
"SRI" => Ok(MMixInstruction::SRI(x, y, z)),
"SRUI" => Ok(MMixInstruction::SRUI(x, y, z)),
_ => Err(format!("Unknown shift instruction: {}", mnem.as_str())),
}
}
fn parse_inst_conditional_set_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"CSN" => Ok(MMixInstruction::CSN(x, y, z)),
"CSZ" => Ok(MMixInstruction::CSZ(x, y, z)),
"CSP" => Ok(MMixInstruction::CSP(x, y, z)),
"CSOD" => Ok(MMixInstruction::CSOD(x, y, z)),
"CSNN" => Ok(MMixInstruction::CSNN(x, y, z)),
"CSNZ" => Ok(MMixInstruction::CSNZ(x, y, z)),
"CSNP" => Ok(MMixInstruction::CSNP(x, y, z)),
"CSEV" => Ok(MMixInstruction::CSEV(x, y, z)),
_ => Err(format!(
"Unknown conditional set instruction: {}",
mnem.as_str()
)),
}
}
fn parse_inst_conditional_set_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"CSNI" => Ok(MMixInstruction::CSNI(x, y, z)),
"CSZI" => Ok(MMixInstruction::CSZI(x, y, z)),
"CSPI" => Ok(MMixInstruction::CSPI(x, y, z)),
"CSODI" => Ok(MMixInstruction::CSODI(x, y, z)),
"CSNNI" => Ok(MMixInstruction::CSNNI(x, y, z)),
"CSNZI" => Ok(MMixInstruction::CSNZI(x, y, z)),
"CSNPI" => Ok(MMixInstruction::CSNPI(x, y, z)),
"CSEVI" => Ok(MMixInstruction::CSEVI(x, y, z)),
_ => Err(format!(
"Unknown conditional set immediate instruction: {}",
mnem.as_str()
)),
}
}
fn parse_inst_zero_or_set_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
match mnem.as_str().to_uppercase().as_str() {
"ZSN" => Ok(MMixInstruction::ZSN(x, y, z)),
"ZSZ" => Ok(MMixInstruction::ZSZ(x, y, z)),
"ZSP" => Ok(MMixInstruction::ZSP(x, y, z)),
"ZSOD" => Ok(MMixInstruction::ZSOD(x, y, z)),
"ZSNN" => Ok(MMixInstruction::ZSNN(x, y, z)),
"ZSNZ" => Ok(MMixInstruction::ZSNZ(x, y, z)),
"ZSNP" => Ok(MMixInstruction::ZSNP(x, y, z)),
"ZSEV" => Ok(MMixInstruction::ZSEV(x, y, z)),
_ => Err(format!(
"Unknown zero or set instruction: {}",
mnem.as_str()
)),
}
}
fn parse_inst_zero_or_set_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
match mnem.as_str().to_uppercase().as_str() {
"ZSNI" => Ok(MMixInstruction::ZSNI(x, y, z)),
"ZSZI" => Ok(MMixInstruction::ZSZI(x, y, z)),
"ZSPI" => Ok(MMixInstruction::ZSPI(x, y, z)),
"ZSODI" => Ok(MMixInstruction::ZSODI(x, y, z)),
"ZSNNI" => Ok(MMixInstruction::ZSNNI(x, y, z)),
"ZSNZI" => Ok(MMixInstruction::ZSNZI(x, y, z)),
"ZSNPI" => Ok(MMixInstruction::ZSNPI(x, y, z)),
"ZSEVI" => Ok(MMixInstruction::ZSEVI(x, y, z)),
_ => Err(format!(
"Unknown zero or set immediate instruction: {}",
mnem.as_str()
)),
}
}
fn parse_inst_branch(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let target = self.parse_number(ops.next().unwrap())?;
let pc = self.current_addr;
let offset_bytes = (target as i64 - pc as i64) as i16;
let offset = (offset_bytes / 4) as u16;
match mnem.as_str().to_uppercase().as_str() {
"JE" => Ok(MMixInstruction::JE(x, offset)),
"JNE" => Ok(MMixInstruction::JNE(x, offset)),
"JL" => Ok(MMixInstruction::JL(x, offset)),
"JG" => Ok(MMixInstruction::JG(x, offset)),
"BN" => Ok(MMixInstruction::BN(x, offset)),
"BNB" => Ok(MMixInstruction::BNB(x, offset)),
"BZ" => Ok(MMixInstruction::BZ(x, offset)),
"BZB" => Ok(MMixInstruction::BZB(x, offset)),
"BP" => Ok(MMixInstruction::BP(x, offset)),
"BPB" => Ok(MMixInstruction::BPB(x, offset)),
"BOD" => Ok(MMixInstruction::BOD(x, offset)),
"BODB" => Ok(MMixInstruction::BODB(x, offset)),
"BNN" => Ok(MMixInstruction::BNN(x, offset)),
"BNNB" => Ok(MMixInstruction::BNNB(x, offset)),
"BNZ" => Ok(MMixInstruction::BNZ(x, offset)),
"BNZB" => Ok(MMixInstruction::BNZB(x, offset)),
"BNP" => Ok(MMixInstruction::BNP(x, offset)),
"BNPB" => Ok(MMixInstruction::BNPB(x, offset)),
"BEV" => Ok(MMixInstruction::BEV(x, offset)),
"BEVB" => Ok(MMixInstruction::BEVB(x, offset)),
_ => Err(format!("Unknown branch instruction: {}", mnem.as_str())),
}
}
fn parse_inst_jmp(&self, pair: pest::iterators::Pair<Rule>) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let target = self.parse_number(ops.next().unwrap())?;
let pc = self.current_addr;
let offset = ((target as i64 - pc as i64) / 4) as i32;
let offset_24 = (offset & 0xFFFFFF) as u32;
Ok(MMixInstruction::JMP(offset_24))
}
fn parse_inst_pbranch(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let mnem = parts.next().unwrap();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let target = self.parse_number(ops.next().unwrap())?;
let pc = self.current_addr;
let offset = ((target as i64 - pc as i64) / 4) as i16;
let offset_u16 = offset as u16;
let y = ((offset_u16 >> 8) & 0xFF) as u8;
let z = (offset_u16 & 0xFF) as u8;
match mnem.as_str().to_uppercase().as_str() {
"PBN" => Ok(MMixInstruction::PBN(x, y, z)),
"PBZ" => Ok(MMixInstruction::PBZ(x, y, z)),
"PBP" => Ok(MMixInstruction::PBP(x, y, z)),
"PBOD" => Ok(MMixInstruction::PBOD(x, y, z)),
"PBNN" => Ok(MMixInstruction::PBNN(x, y, z)),
"PBNZ" => Ok(MMixInstruction::PBNZ(x, y, z)),
"PBNP" => Ok(MMixInstruction::PBNP(x, y, z)),
"PBEV" => Ok(MMixInstruction::PBEV(x, y, z)),
"PBNB" => Ok(MMixInstruction::PBNB(x, y, z)),
"PBZB" => Ok(MMixInstruction::PBZB(x, y, z)),
"PBPB" => Ok(MMixInstruction::PBPB(x, y, z)),
"PBODB" => Ok(MMixInstruction::PBODB(x, y, z)),
"PBNNB" => Ok(MMixInstruction::PBNNB(x, y, z)),
"PBNZB" => Ok(MMixInstruction::PBNZB(x, y, z)),
"PBNPB" => Ok(MMixInstruction::PBNPB(x, y, z)),
"PBEVB" => Ok(MMixInstruction::PBEVB(x, y, z)),
_ => Err(format!(
"Unknown probable branch instruction: {}",
mnem.as_str()
)),
}
}
fn parse_inst_geta(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next(); let operand = parts.next().unwrap();
let mut operand_parts = operand.into_inner();
let reg_pair = operand_parts.next().unwrap();
let addr_pair = operand_parts.next().unwrap();
let x = self.parse_register(reg_pair)?;
let addr = self.parse_number(addr_pair)?;
debug!(
"GETA: current_addr=0x{:X}, target_addr=0x{:X}",
self.current_addr, addr
);
let offset = addr.wrapping_sub(self.current_addr) as i64;
let offset_16 = ((offset >> 2) & 0xFFFF) as u16; let y = ((offset_16 >> 8) & 0xFF) as u8;
let z = (offset_16 & 0xFF) as u8;
debug!(
"GETA: offset={}, offset_16=0x{:X}, y=0x{:X}, z=0x{:X}",
offset, offset_16, y, z
);
Ok(MMixInstruction::GETA(x, y, z))
}
fn parse_inst_getab(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next(); let operand = parts.next().unwrap();
let mut operand_parts = operand.into_inner();
let reg_pair = operand_parts.next().unwrap();
let addr_pair = operand_parts.next().unwrap();
let x = self.parse_register(reg_pair)?;
let addr = self.parse_number(addr_pair)?;
let offset = addr.wrapping_sub(self.current_addr) as i64;
let offset_16 = ((offset >> 2) & 0xFFFF) as u16;
let y = ((offset_16 >> 8) & 0xFF) as u8;
let z = (offset_16 & 0xFF) as u8;
Ok(MMixInstruction::GETAB(x, y, z))
}
fn parse_inst_trap(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x = self.parse_number(parts.next().unwrap())? as u8;
let y = self.parse_number(parts.next().unwrap())? as u8;
let z = self.parse_number(parts.next().unwrap())? as u8;
Ok(MMixInstruction::TRAP(x, y, z))
}
fn parse_rrr<F>(
&self,
pair: pest::iterators::Pair<Rule>,
f: F,
) -> Result<MMixInstruction, String>
where
F: FnOnce(u8, u8, u8) -> MMixInstruction,
{
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_register(ops.next().unwrap())?;
Ok(f(x, y, z))
}
fn parse_rri<F>(
&self,
pair: pest::iterators::Pair<Rule>,
f: F,
) -> Result<MMixInstruction, String>
where
F: FnOnce(u8, u8, u8) -> MMixInstruction,
{
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operands = parts.next().unwrap();
let mut ops = operands.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let y = self.parse_register(ops.next().unwrap())?;
let z = self.parse_number(ops.next().unwrap())? as u8;
Ok(f(x, y, z))
}
fn parse_inst_pushj(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operand = parts.next().unwrap();
let mut ops = operand.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let addr = self.parse_number(ops.next().unwrap())?;
let offset = addr.wrapping_sub(self.current_addr) as i64;
let offset_16 = ((offset >> 2) & 0xFFFF) as u16;
let y = ((offset_16 >> 8) & 0xFF) as u8;
let z = (offset_16 & 0xFF) as u8;
Ok(MMixInstruction::PUSHJ(x, y, z))
}
fn parse_inst_pushjb(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let operand = parts.next().unwrap();
let mut ops = operand.into_inner();
let x = self.parse_register(ops.next().unwrap())?;
let addr = self.parse_number(ops.next().unwrap())?;
let offset = addr.wrapping_sub(self.current_addr) as i64;
let offset_16 = ((offset >> 2) & 0xFFFF) as u16;
let y = ((offset_16 >> 8) & 0xFF) as u8;
let z = (offset_16 & 0xFF) as u8;
Ok(MMixInstruction::PUSHJB(x, y, z))
}
fn parse_inst_pushgo_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::PUSHGO)
}
fn parse_inst_pushgo_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::PUSHGOI)
}
fn parse_inst_pop(&self, pair: pest::iterators::Pair<Rule>) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x = self.parse_number(parts.next().unwrap())? as u8;
let yz = self.parse_number(parts.next().unwrap())? as u16;
let y = ((yz >> 8) & 0xFF) as u8;
let z = (yz & 0xFF) as u8;
Ok(MMixInstruction::POP(x, y | z))
}
fn parse_inst_go_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::GO)
}
fn parse_inst_go_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::GOI)
}
fn parse_inst_get(&self, pair: pest::iterators::Pair<Rule>) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x = self.parse_register(parts.next().unwrap())?;
let z = self.parse_number(parts.next().unwrap())? as u8;
Ok(MMixInstruction::GET(x, z))
}
fn parse_inst_put(&self, pair: pest::iterators::Pair<Rule>) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x = self.parse_number(parts.next().unwrap())? as u8;
let z = self.parse_register(parts.next().unwrap())?;
Ok(MMixInstruction::PUT(x, z))
}
fn parse_inst_puti(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x = self.parse_number(parts.next().unwrap())? as u8;
let z = self.parse_number(parts.next().unwrap())? as u8;
Ok(MMixInstruction::PUTI(x, z))
}
fn parse_inst_save(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x_pair = parts.next().ok_or("Missing X register in SAVE")?;
let x = self.parse_register(x_pair)?;
let z_pair = parts.next().ok_or("Missing Z value in SAVE")?;
let z = self.parse_number(z_pair)? as u8;
Ok(MMixInstruction::SAVE(x, z))
}
fn parse_inst_unsave(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x_pair = parts.next().ok_or("Missing X value in UNSAVE")?;
let x = self.parse_number(x_pair)? as u8;
let z_pair = parts.next().ok_or("Missing Z register in UNSAVE")?;
let z = self.parse_register(z_pair)?;
Ok(MMixInstruction::UNSAVE(x, z))
}
fn parse_inst_ldunc_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::LDUNC)
}
fn parse_inst_ldunc_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::LDUNCI)
}
fn parse_inst_stunc_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::STUNC)
}
fn parse_inst_stunc_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::STUNCI)
}
fn parse_inst_ldht_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::LDHT)
}
fn parse_inst_ldht_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::LDHTI)
}
fn parse_inst_stht_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::STHT)
}
fn parse_inst_stht_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::STHTI)
}
fn parse_inst_ldsf_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::LDSF)
}
fn parse_inst_ldsf_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::LDSFI)
}
fn parse_inst_stsf_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::STSF)
}
fn parse_inst_stsf_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::STSFI)
}
fn parse_inst_ldvts_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::LDVTS)
}
fn parse_inst_ldvts_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::LDVTSI)
}
fn parse_inst_cswap_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::CSWAP)
}
fn parse_inst_cswap_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::CSWAPI)
}
fn parse_inst_stco_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x = self.parse_number(parts.next().unwrap())? as u8;
let _comma1 = parts.next();
let y = self.parse_register(parts.next().unwrap())?;
let _comma2 = parts.next();
let z = self.parse_register(parts.next().unwrap())?;
Ok(MMixInstruction::STCO(x, y, z))
}
fn parse_inst_stco_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x = self.parse_number(parts.next().unwrap())? as u8;
let _comma1 = parts.next();
let y = self.parse_register(parts.next().unwrap())?;
let _comma2 = parts.next();
let z = self.parse_number(parts.next().unwrap())? as u8;
Ok(MMixInstruction::STCOI(x, y, z))
}
fn parse_inst_preld_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::PRELD)
}
fn parse_inst_preld_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::PRELDI)
}
fn parse_inst_prego_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::PREGO)
}
fn parse_inst_prego_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::PREGOI)
}
fn parse_inst_prest_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::PREST)
}
fn parse_inst_prest_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::PRESTI)
}
fn parse_inst_syncd_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::SYNCD)
}
fn parse_inst_syncd_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::SYNCDI)
}
fn parse_inst_syncid_rrr(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rrr(pair, MMixInstruction::SYNCID)
}
fn parse_inst_syncid_rri(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
self.parse_rri(pair, MMixInstruction::SYNCIDI)
}
fn parse_inst_resume(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let xyz = self.parse_number(parts.next().unwrap())? as u8;
Ok(MMixInstruction::RESUME(xyz))
}
fn parse_inst_trip(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let x = self.parse_number(parts.next().unwrap())? as u8;
let y = self.parse_number(parts.next().unwrap())? as u8;
let z = self.parse_number(parts.next().unwrap())? as u8;
Ok(MMixInstruction::TRIP(x, y, z))
}
fn parse_inst_sync(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<MMixInstruction, String> {
let mut parts = pair.into_inner();
let _mnem = parts.next();
let xyz = self.parse_number(parts.next().unwrap())? as u8;
Ok(MMixInstruction::SYNC(xyz))
}
fn parse_data_directive(
&mut self,
pair: pest::iterators::Pair<Rule>,
) -> Result<Vec<MMixInstruction>, String> {
let mut parts = pair.into_inner();
let directive = parts.next().unwrap();
match directive.as_rule() {
Rule::directive_byte => {
let mut result = Vec::new();
let values_pair = parts.next().unwrap();
let values: Vec<_> = values_pair.into_inner().collect();
let num_values = values.len();
for (idx, byte_value) in values.into_iter().enumerate() {
let actual_value = byte_value.into_inner().next().unwrap();
if actual_value.as_rule() == Rule::string_literal {
let s = actual_value.as_str();
let s = &s[1..s.len() - 1]; for ch in s.chars() {
result.push(MMixInstruction::BYTE(ch as u8));
}
if idx == num_values - 1 {
result.push(MMixInstruction::BYTE(0));
}
} else {
let val = self.parse_number(actual_value)? as u8;
result.push(MMixInstruction::BYTE(val));
}
}
Ok(result)
}
Rule::directive_wyde => {
let val = self.parse_number(parts.next().unwrap())? as u16;
Ok(vec![MMixInstruction::WYDE(val)])
}
Rule::directive_tetra => {
let val = self.parse_number(parts.next().unwrap())? as u32;
Ok(vec![MMixInstruction::TETRA(val)])
}
Rule::directive_octa => {
let value_pair = parts.next().unwrap();
let val = self.parse_number(value_pair)?;
Ok(vec![MMixInstruction::OCTA(val)])
}
_ => Err(format!("Unknown data directive: {:?}", directive.as_rule())),
}
}
fn parse_loc_directive(&mut self, pair: pest::iterators::Pair<Rule>) -> Result<(), String> {
let mut parts = pair.into_inner();
let _directive = parts.next(); let addr = self.parse_number(parts.next().unwrap())?;
self.current_addr = addr;
Ok(())
}
fn parse_is_directive(&mut self, pair: pest::iterators::Pair<Rule>) -> Result<(), String> {
let mut parts = pair.into_inner();
let symbol_name = parts.next().unwrap().as_str().to_string();
let _is_keyword = parts.next(); let value_pair = parts.next().unwrap();
let symbol_type = match value_pair.as_rule() {
Rule::register => {
let reg = self.parse_register(value_pair)?;
SymbolType::Register(reg)
}
Rule::expr_value => {
let value = self.parse_number(value_pair)?;
SymbolType::Constant(value)
}
_ => return Err("IS directive requires register or expr_value".to_string()),
};
self.symbols.insert(symbol_name, symbol_type);
Ok(())
}
fn parse_register(&self, pair: pest::iterators::Pair<Rule>) -> Result<u8, String> {
let (line, col) = pair.line_col();
let text = if pair.as_rule() == Rule::register {
if let Some(inner) = pair.clone().into_inner().next() {
inner.as_str()
} else {
pair.as_str()
}
} else {
pair.as_str()
};
if !text.starts_with('$') {
if let Some(&symbol_type) = self.symbols.get(text) {
match symbol_type {
SymbolType::Register(reg) => return Ok(reg),
SymbolType::Constant(value) => {
return Err(format!(
"Line {}:{}: Symbol '{}' is a numeric constant ({}), cannot use as register",
line, col, text, value
));
}
}
}
return Err(format!(
"Line {}:{}: Undefined symbol '{}' (expected register like $0 or register alias)",
line, col, text
));
}
text[1..]
.parse::<u8>()
.map_err(|e| format!("Line {}:{}: Invalid register number: {}", line, col, e))
}
fn parse_number(&self, pair: pest::iterators::Pair<Rule>) -> Result<u64, String> {
let rule = pair.as_rule();
let (line, col) = pair.line_col();
if rule == Rule::expr_value || rule == Rule::number_literal || rule == Rule::operand_imm {
let inner = pair.into_inner().next().unwrap();
return self.parse_number(inner);
}
let text = pair.as_str();
match rule {
Rule::at_symbol => {
Ok(self.current_addr)
}
Rule::signed_number_literal => {
let mut inner = pair.into_inner();
let unsigned = self.parse_number(inner.next().unwrap())?;
let signed = -(unsigned as i128);
Ok(signed as u64)
}
Rule::char_literal => {
let inner = &text[1..text.len() - 1];
let ch = if let Some(rest) = inner.strip_prefix('\\') {
match rest {
"n" => '\n',
"r" => '\r',
"t" => '\t',
"0" => '\0',
"\\" => '\\',
"'" => '\'',
_ => {
return Err(format!(
"Line {}:{}: Unsupported escape in char literal: \\{}",
line, col, rest
));
}
}
} else {
if inner.chars().count() != 1 {
return Err(format!(
"Line {}:{}: Invalid char literal (must be one byte): '{}'",
line, col, inner
));
}
inner.chars().next().unwrap()
};
if !ch.is_ascii() {
return Err(format!(
"Line {}:{}: Char literal must be ASCII byte, got {:?}",
line, col, ch
));
}
Ok(ch as u8 as u64)
}
Rule::hex_literal => {
let hex_str = if let Some(stripped) = text.strip_prefix('#') {
stripped
} else if let Some(stripped) =
text.strip_prefix("0x").or_else(|| text.strip_prefix("0X"))
{
stripped
} else {
text
};
u64::from_str_radix(hex_str, 16)
.map_err(|e| format!("Line {}:{}: Invalid hex number: {}", line, col, e))
}
Rule::oct_literal => u64::from_str_radix(&text[1..], 8)
.map_err(|e| format!("Line {}:{}: Invalid octal number: {}", line, col, e)),
Rule::dec_literal => text
.parse::<u64>()
.map_err(|e| format!("Line {}:{}: Invalid decimal number: {}", line, col, e)),
Rule::symbol => {
if let Some(&symbol_type) = self.symbols.get(text) {
match symbol_type {
SymbolType::Constant(value) => Ok(value),
SymbolType::Register(reg) => Err(format!(
"Line {}:{}: Symbol '{}' is a register alias (${}), cannot use as immediate value",
line, col, text, reg
)),
}
} else if let Some(&label_addr) = self.labels.get(text) {
Ok(label_addr)
} else {
Err(format!("Line {}:{}: Undefined symbol: {}", line, col, text))
}
}
Rule::identifier => {
if let Some(&symbol_type) = self.symbols.get(text) {
match symbol_type {
SymbolType::Constant(value) => Ok(value),
SymbolType::Register(reg) => Err(format!(
"Line {}:{}: Symbol '{}' is a register alias (${}), cannot use as immediate value",
line, col, text, reg
)),
}
} else if let Some(&label_addr) = self.labels.get(text) {
Ok(label_addr)
} else {
Err(format!("Line {}:{}: Undefined symbol: {}", line, col, text))
}
}
_ => Err(format!(
"Line {}:{}: Expected number, got: {:?}",
line, col, rule
)),
}
}
fn instruction_size(inst: &MMixInstruction) -> u64 {
match inst {
MMixInstruction::SET(_, _) => 16,
MMixInstruction::SETRR(_, _) => 4, MMixInstruction::BYTE(_) => 1,
MMixInstruction::WYDE(_) => 2,
MMixInstruction::TETRA(_) => 4,
MMixInstruction::OCTA(_) => 8,
_ => 4,
}
}
pub fn encode_instruction_bytes(&self, instruction: &MMixInstruction) -> Vec<u8> {
crate::encode::encode_instruction_bytes(instruction)
}
pub fn generate_object_code(&self) -> Vec<u8> {
crate::mmo::MmoGenerator::new(self.instructions.clone(), self.labels.clone()).generate()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_simple_label() {
let mut asm = MMixAssembler::new("LOOP: HALT", "<test>");
asm.parse().unwrap();
assert_eq!(asm.labels.get("LOOP"), Some(&0));
assert_eq!(asm.instructions.len(), 1);
}
#[test]
fn test_parse_octa_directive() {
let mut asm = MMixAssembler::new("OCTA #123456789ABCDEF0", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions.len(), 1);
assert_eq!(
asm.instructions[0].1,
MMixInstruction::OCTA(0x123456789ABCDEF0)
);
}
#[test]
fn test_parse_node_structure() {
let mut asm = MMixAssembler::new("NODE: OCTA 42\n OCTA 0", "<test>");
asm.parse().unwrap();
assert_eq!(asm.labels.get("NODE"), Some(&0));
assert_eq!(asm.instructions.len(), 2);
}
#[test]
fn test_parse_seti() {
let mut asm = MMixAssembler::new("SETI $2, 10", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SET(2, 10));
}
#[test]
fn test_parse_set_register() {
let mut asm = MMixAssembler::new("SET $1, $7", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SETRR(1, 7));
}
#[test]
fn test_parse_negative_literal_seti() {
let mut asm = MMixAssembler::new("SETI $1, -1", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SET(1, u64::MAX));
}
#[test]
fn test_parse_negative_literal_8bit_wrap() {
let mut asm = MMixAssembler::new("ADDI $1, $2, -1", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::ADDI(1, 2, 0xFF));
}
#[test]
fn test_parse_char_literal_immediate() {
let mut asm = MMixAssembler::new("ANDI $1, $2, 'A'", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::ANDI(1, 2, 65));
}
#[test]
fn test_parse_char_literal_escape_newline() {
let mut asm = MMixAssembler::new("ANDI $1, $2, '\\n'", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::ANDI(1, 2, 10));
}
#[test]
fn test_parse_char_literal_non_ascii_error() {
let mut asm = MMixAssembler::new("ANDI $1, $2, 'ฤ'", "<test>");
assert!(asm.parse().is_err());
}
#[test]
fn test_parse_char_literal_multi_char_error() {
let mut asm = MMixAssembler::new("ANDI $1, $2, 'AB'", "<test>");
assert!(asm.parse().is_err());
}
#[test]
fn test_parse_and() {
let mut asm = MMixAssembler::new("AND $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::AND(1, 2, 3));
}
#[test]
fn test_parse_andi() {
let mut asm = MMixAssembler::new("ANDI $1, $2, #FF", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::ANDI(1, 2, 0xFF));
}
#[test]
fn test_parse_or() {
let mut asm = MMixAssembler::new("OR $10, $20, $30", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::OR(10, 20, 30));
}
#[test]
fn test_parse_xor() {
let mut asm = MMixAssembler::new("XOR $5, $6, $7", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::XOR(5, 6, 7));
}
#[test]
fn test_parse_andn() {
let mut asm = MMixAssembler::new("ANDN $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::ANDN(1, 2, 3));
}
#[test]
fn test_parse_nand() {
let mut asm = MMixAssembler::new("NAND $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::NAND(1, 2, 3));
}
#[test]
fn test_parse_nor() {
let mut asm = MMixAssembler::new("NOR $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::NOR(1, 2, 3));
}
#[test]
fn test_parse_nxor() {
let mut asm = MMixAssembler::new("NXOR $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::NXOR(1, 2, 3));
}
#[test]
fn test_parse_mux() {
let mut asm = MMixAssembler::new("MUX $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::MUX(1, 2, 3));
}
#[test]
fn test_parse_bdif() {
let mut asm = MMixAssembler::new("BDIF $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::BDIF(1, 2, 3));
}
#[test]
fn test_parse_bdifi() {
let mut asm = MMixAssembler::new("BDIFI $1, $2, #10", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::BDIFI(1, 2, 0x10));
}
#[test]
fn test_parse_wdif() {
let mut asm = MMixAssembler::new("WDIF $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::WDIF(1, 2, 3));
}
#[test]
fn test_parse_wdifi() {
let mut asm = MMixAssembler::new("WDIFI $1, $2, 100", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::WDIFI(1, 2, 100));
}
#[test]
fn test_parse_tdif() {
let mut asm = MMixAssembler::new("TDIF $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::TDIF(1, 2, 3));
}
#[test]
fn test_parse_tdifi() {
let mut asm = MMixAssembler::new("TDIFI $1, $2, 50", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::TDIFI(1, 2, 50));
}
#[test]
fn test_parse_odif() {
let mut asm = MMixAssembler::new("ODIF $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::ODIF(1, 2, 3));
}
#[test]
fn test_parse_odifi() {
let mut asm = MMixAssembler::new("ODIFI $1, $2, 255", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::ODIFI(1, 2, 255));
}
#[test]
fn test_parse_sadd() {
let mut asm = MMixAssembler::new("SADD $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SADD(1, 2, 3));
}
#[test]
fn test_parse_saddi() {
let mut asm = MMixAssembler::new("SADDI $1, $2, 0", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SADDI(1, 2, 0));
}
#[test]
fn test_parse_mor() {
let mut asm = MMixAssembler::new("MOR $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::MOR(1, 2, 3));
}
#[test]
fn test_parse_mori() {
let mut asm = MMixAssembler::new("MORI $1, $2, 128", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::MORI(1, 2, 128));
}
#[test]
fn test_parse_mxor() {
let mut asm = MMixAssembler::new("MXOR $1, $2, $3", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::MXOR(1, 2, 3));
}
#[test]
fn test_parse_mxori() {
let mut asm = MMixAssembler::new("MXORI $1, $2, 64", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::MXORI(1, 2, 64));
}
#[test]
fn test_parse_sl() {
let mut asm = MMixAssembler::new("SL $3, $1, $2", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SL(3, 1, 2));
}
#[test]
fn test_parse_sli() {
let mut asm = MMixAssembler::new("SLI $3, $1, 8", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SLI(3, 1, 8));
}
#[test]
fn test_parse_slu() {
let mut asm = MMixAssembler::new("SLU $10, $20, $30", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SLU(10, 20, 30));
}
#[test]
fn test_parse_slui() {
let mut asm = MMixAssembler::new("SLUI $1, $2, 16", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SLUI(1, 2, 16));
}
#[test]
fn test_parse_sr() {
let mut asm = MMixAssembler::new("SR $5, $6, $7", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SR(5, 6, 7));
}
#[test]
fn test_parse_sri() {
let mut asm = MMixAssembler::new("SRI $3, $1, 4", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SRI(3, 1, 4));
}
#[test]
fn test_parse_sru() {
let mut asm = MMixAssembler::new("SRU $8, $9, $10", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SRU(8, 9, 10));
}
#[test]
fn test_parse_srui() {
let mut asm = MMixAssembler::new("SRUI $3, $1, 1", "<test>");
asm.parse().unwrap();
assert_eq!(asm.instructions[0].1, MMixInstruction::SRUI(3, 1, 1));
}
}