use byteorder::{ReadBytesExt, LE}; use std::io::Cursor; use std::convert::{TryInto, TryFrom}; use std::io::Read; struct Machine { RX: u16, RY: u16, RZ: u16, RTRGT: u16, RSTAT: Flags, RCALL: u16, RSK: u16, RSR: u16, SSK: Vec, SIN: Vec, SMAIN: Vec, SCODE: Vec, IP: u16, } impl Machine { fn push_stack(&mut self, val: u16) { self.SSK[self.RSK as usize] = val; self.RSK -= 1; } fn pop_stack(&mut self) -> u16 { let v = self.SSK[self.RSK as usize]; self.RSK += 1; v } } impl Machine { fn write_reg(&mut self, reg: Register, val: u16) { match reg { Register::RX => self.RX = val, Register::RY => self.RY = val, Register::RZ => self.RZ = val, Register::RTRGT => self.RTRGT = val, Register::RSTAT => {}, // writes have no effect Register::RCALL => {}, Register::NULL => {}, } } fn read_reg(&mut self, reg: Register) -> u16 { match reg { Register::RX => self.RX, Register::RY => self.RY, Register::RZ => self.RZ, Register::RTRGT => 0, Register::RSTAT => self.RSTAT.bits(), Register::RCALL => self.RCALL, Register::NULL => 0, } } fn read(&mut self, a: Arg) -> u16 { match a { Arg::Register(r) => self.read_reg(r), Arg::SIN(v) => self.SIN[v as usize], Arg::SMAIN(v) => self.SMAIN[v as usize], } } fn write(&mut self, a: Arg, val: u16) { match a { Arg::Register(r) => self.write_reg(r, val), Arg::SIN(v) => self.SIN[v as usize] = val, Arg::SMAIN(v) => self.SMAIN[v as usize] = val, } } } #[derive(Debug, Eq, PartialEq, Copy, Clone)] enum Register { RX, RY, RZ, RTRGT, RSTAT, RCALL, NULL, } impl Register { fn into_register(x: u16) -> Self { match x { 1 => Self::RX, 2 => Self::RY, 3 => Self::RZ, 4 => Self::RTRGT, 5 => Self::RSTAT, 6 => Self::RCALL, _ => Self::NULL, } } } bitflags::bitflags! { struct Flags: u16 { const FZERO = 0b0000000000000001; const FEQUL = 0b0000000000000010; const FLT = 0b0000000000000100; const FGT = 0b0000000000001000; const FCRRY = 0b0000000000010000; const FINF = 0b0000000000100000; const FSE = 0b0000000001000000; const FSF = 0b0000000010000000; } } #[derive(Debug, Eq, PartialEq)] enum OP { HALT = 0x000 , NOOP = 0x001 , INC = 0x002 , DEC = 0x003 , ADD = 0x004 , SUB = 0x005 , MUL = 0x006 , DIV = 0x007 , ADDC = 0x008 , SUBC = 0x009 , READ = 0x00A , WRIT = 0x00B , CPY = 0x00C , MCPY = 0x00D , ICPY = 0x00E , CMP = 0x00F , AND = 0x010 , OR = 0x011 , CMPL = 0x012 , LSHF = 0x013 , RSHF = 0x014 , PUSH = 0x015 , POP = 0x016 , CFLG = 0x017 , CALL = 0x018 , RTRN = 0x019 , RTRV = 0x01A , RTL = 0x01B , RTR = 0x01C , CIP = 0x01D , BSWP = 0x01E , JUMP = 0x01F , JZRO = 0x020 , JEQU = 0x021 , JLT = 0x022 , JGT = 0x023 , JCRY = 0x024 , JINF = 0x025 , JSE = 0x026 , JSF = 0x027 , CZRO = 0x030 , CCRY = 0x034 , XOR = 0x040 , SWAP = 0x041 , RCPT = 0x042 , RCPF = 0x043 , } impl std::convert::Into for u16 { fn into(self) -> OP { match self { 0x000 => OP::HALT, 0x001 => OP::NOOP, 0x002 => OP::INC, 0x003 => OP::DEC, 0x004 => OP::ADD, 0x005 => OP::SUB, 0x006 => OP::MUL, 0x007 => OP::DIV, 0x008 => OP::ADDC, 0x009 => OP::SUBC, 0x00A => OP::READ, 0x00B => OP::WRIT, 0x00C => OP::CPY, 0x00D => OP::MCPY, 0x00E => OP::ICPY, 0x00F => OP::CMP, 0x010 => OP::AND, 0x011 => OP::OR, 0x012 => OP::CMPL, 0x013 => OP::LSHF, 0x014 => OP::RSHF, 0x015 => OP::PUSH, 0x016 => OP::POP, 0x017 => OP::CFLG, 0x018 => OP::CALL, 0x019 => OP::RTRN, 0x01A => OP::RTRV, 0x01B => OP::RTL, 0x01C => OP::RTR, 0x01D => OP::CIP, 0x01E => OP::BSWP, 0x01F => OP::JUMP, 0x020 => OP::JZRO, 0x021 => OP::JEQU, 0x022 => OP::JLT, 0x023 => OP::JGT, 0x024 => OP::JCRY, 0x025 => OP::JINF, 0x026 => OP::JSE, 0x027 => OP::JSF, 0x030 => OP::CZRO, 0x034 => OP::CCRY, 0x040 => OP::XOR, 0x041 => OP::SWAP, 0x042 => OP::RCPT, 0x043 => OP::RCPF, _ => panic!(), } } } #[derive(Debug, PartialEq, Eq)] enum AFLG { Register, SIN, SMAIN, } impl From for AFLG { fn from(x: u16) -> AFLG { match x { 0b00 => AFLG::Register, 0b01 => AFLG::Register, 0b10 => AFLG::SIN, 0b11 => AFLG::SMAIN, _ => panic!(), } } } bitfield::bitfield! { struct DefinitionWord(u16); impl Debug; into OP, opcode, set_opcode: 8, 0; sign, set_sign: 9, 9; into AFLG, aflg2, set_aflg2: 11, 10; into AFLG, aflg1, set_aflg1: 13, 12; into AFLG, aflg0, set_aflg0: 15, 14; } #[derive(Debug)] struct Instruction { def: DefinitionWord, args: Vec, } impl Default for Machine { fn default() -> Self { Self { SSK: vec![0_u16; 65536_usize], SIN: vec![0_u16; 65536_usize], SMAIN: vec![0_u16; 65536_usize], SCODE: vec![0_u16; 65536_usize], RSTAT: Flags::FSE, IP: 1, RCALL: 0, RSK: 0xffff, RSR: 0, RTRGT: 0, RX: 0, RY: 0, RZ: 0, } } } #[derive(Debug)] enum Chunk { SIN(Vec), SCODE(Vec), SMAIN(Vec), } impl Chunk { fn inner(&self) -> &[u16] { match self { Chunk::SIN(x) => x, Chunk::SCODE(x) => x, Chunk::SMAIN(x) => x, } } } fn parse_chunk(mut input: impl std::io::Read) -> Chunk { let space_index = input.read_u8().unwrap(); let length = input.read_u16::().unwrap(); let mut data = Vec::new(); for _ in 0..length { data.push(input.read_u16::().unwrap()); } match space_index { 1 => Chunk::SIN(data), 2 => Chunk::SCODE(data), 3 => Chunk::SMAIN(data), _ => panic!("unknown space index {}", space_index), } } fn arg_count(opcode: OP) -> usize { match opcode { OP::HALT => 0, OP::NOOP => 0, OP::INC => 1, OP::DEC => 1, OP::ADD => 2, OP::SUB => 2, OP::MUL => 2, OP::DIV => 2, OP::ADDC => 2, OP::SUBC => 2, OP::READ => 1, OP::WRIT => 1, OP::CPY => 2, OP::MCPY => 3, OP::ICPY => 2, OP::CMP => 2, OP::AND => 2, OP::OR => 2, OP::CMPL => 1, OP::LSHF => 2, OP::RSHF => 2, OP::PUSH => 1, OP::POP => 1, OP::CFLG => 0, OP::CALL => 3, OP::RTRN => 0, OP::RTRV => 0, OP::RTL => 2, OP::RTR => 2, OP::CIP => 1, OP::BSWP => 1, OP::JUMP => 0, OP::JZRO => 0, OP::JEQU => 0, OP::JLT => 0, OP::JGT => 0, OP::JCRY => 0, OP::JINF => 0, OP::JSE => 0, OP::JSF => 0, OP::CZRO => 0, OP::CCRY => 0, OP::XOR => 2, OP::SWAP => 2, OP::RCPT => 2, OP::RCPF => 2, } } fn parse_instruction(mut bytes: &[u16]) -> (Instruction, usize) { //println!("{:#02x?}", &bytes[0..8]); let mut consumed = 0; let def = DefinitionWord(bytes[0]); bytes = &bytes[1..]; consumed += 1; let ac = arg_count(def.opcode()); let mut args = Vec::new(); let mut register_count = 0; if ac > 0 && def.aflg0() == AFLG::Register { register_count += 1; } if ac > 1 && def.aflg1() == AFLG::Register { register_count += 1; } if ac > 2 && def.aflg2() == AFLG::Register { register_count += 1; } let mut registers = Vec::new(); if register_count > 0 { let mut aw = bytes[0]; //dbg!(aw); bytes = &bytes[1..]; consumed += 1; //println!("{:016b}", aw); aw >>= 4; for i in 0..register_count { registers.insert(0_usize, Register::into_register(aw & 0b1111)); aw >>= 4; } } for idx in 0..ac { let aflg = match idx { 0 => def.aflg0(), 1 => def.aflg1(), 2 => def.aflg2(), _ => unreachable!(), }; match aflg { AFLG::Register => { args.push(Arg::Register(registers.remove(0))) }, AFLG::SIN => { args.push(Arg::SIN(bytes[0])); bytes = &bytes[1..]; consumed += 1; } AFLG::SMAIN => { args.push(Arg::SMAIN(bytes[0])); bytes = &bytes[1..]; consumed += 1; } } } (Instruction { def, args }, consumed) } #[derive(Debug, Copy, Clone)] enum Arg { Register(Register), SIN(u16), SMAIN(u16), } impl Arg { fn as_immediate(self) -> u16 { match self { Arg::Register(_) => panic!("a register is not an immediate"), Arg::SIN(x) => x, Arg::SMAIN(x) => x, } } fn offset(self, by: i32) -> Self { match self { Arg::Register(_) => panic!("offset must be memory space address"), Arg::SIN(x) => Arg::SIN((x as i32 + by) as u16), Arg::SMAIN(x) => Arg::SMAIN((x as i32 + by) as u16), } } } impl Machine { fn step(&mut self) -> bool { let (ci, inc_by) = parse_instruction(&self.SCODE[(self.IP as usize)..]); //dbg!(&ci); let oc = ci.def.opcode(); let mut should_inc_ip = true; match oc { OP::HALT => return false, OP::NOOP => {}, OP::XOR => { let arg0 = self.read(ci.args[0]); let arg1 = self.read(ci.args[1]); let xored = arg0 ^ arg1; self.write(ci.args[0], xored); self.RSTAT.set(Flags::FZERO, xored == 0); } OP::CALL => { if self.RTRGT == 0 { } else { self.push_stack(self.RCALL); self.push_stack(self.RSTAT.bits()); self.push_stack(self.RX); self.push_stack(self.RY); self.push_stack(self.RZ); self.push_stack(u16::try_from(inc_by).unwrap()); self.RSR = self.RSK; self.RX = self.read(ci.args[0]); self.RY = self.read(ci.args[1]); self.RZ = self.read(ci.args[2]); self.RCALL = self.IP; self.IP = self.RTRGT; should_inc_ip = false; self.RTRGT = 0; self.RSTAT = Flags::empty(); } } OP::POP => { let val = self.pop_stack(); self.write(ci.args[0], val); self.RSTAT.set(Flags::FZERO, val == 0); self.RSTAT.set(Flags::FSE, self.RSK == 0xffff); } OP::PUSH => { let val = self.read(ci.args[0]); self.push_stack(val); self.RSTAT.set(Flags::FZERO, val == 0); self.RSTAT.set(Flags::FSF, self.RSK == 0); } OP::ICPY => { let arg0 = ci.args[0].as_immediate(); self.write(ci.args[1], arg0); self.RSTAT.set(Flags::FZERO, arg0 == 0); //eprintln!("copying {} to {:?}", arg0, ci.args[1]); } OP::CMP => { let a = self.read(ci.args[0]); let b = self.read(ci.args[1]); self.RSTAT.set(Flags::FZERO, a == 0 || b == 0); self.RSTAT.set(Flags::FEQUL, a == b); self.RSTAT.set(Flags::FLT, a < b); self.RSTAT.set(Flags::FGT, a > b); } OP::JEQU => { if self.RSTAT.contains(Flags::FEQUL) { if self.RTRGT == 0 { return false; } self.IP = self.RTRGT; should_inc_ip = false; } } OP::JUMP => { if self.RTRGT == 0 { return false; } self.IP = self.RTRGT; should_inc_ip = false; } OP::INC => { let mut val = self.read(ci.args[0]); val = val.wrapping_add(1); self.write(ci.args[0], val); self.RSTAT.set(Flags::FZERO, val == 0); } OP::READ => { let input = 0; std::io::stdin().read(&mut [input]).unwrap(); self.write(ci.args[0], input as u16); self.RSTAT.set(Flags::FZERO, input == 0); } OP::CPY => { let val = self.read(ci.args[0]); self.write(ci.args[1], val); self.RSTAT.set(Flags::FZERO, val == 0); } OP::RTRN => { self.RSK = self.RSR; self.RX = self.pop_stack(); self.IP = self.RCALL + self.RX; should_inc_ip = false; self.RZ = self.pop_stack(); self.RY = self.pop_stack(); self.RX = self.pop_stack(); self.RSTAT = Flags::from_bits(self.pop_stack()).unwrap(); self.RCALL = self.pop_stack(); self.push_stack(self.RTRGT); self.RTRGT = 0; } OP::RCPT => { let by: i32 = if ci.def.sign() == 1 { self.RTRGT as i32 } else { -(self.RTRGT as i32) }; let from = self.read(ci.args[0]); let to = ci.args[1].offset(by); self.write(to, from); self.RSTAT.set(Flags::FZERO, from == 0); } OP::RCPF => { let by: i32 = if ci.def.sign() == 1 { self.RTRGT as i32 } else { -(self.RTRGT as i32) }; let from = ci.args[0].offset(by); let from_val = self.read(from); self.RSTAT.set(Flags::FZERO, from_val == 0); self.write(ci.args[1], from_val); } OP::CMPL => { let mut val = self.read(ci.args[0]); val = !val; self.write(ci.args[0], val); self.RSTAT.set(Flags::FZERO, val == 0); } OP::AND => { let arg0 = self.read(ci.args[0]); let arg1 = self.read(ci.args[1]); let anded = arg0 & arg1; self.write(ci.args[0], anded); self.RSTAT.set(Flags::FZERO, anded == 0); } OP::OR => { let arg0 = self.read(ci.args[0]); let arg1 = self.read(ci.args[1]); let ored = arg0 | arg1; self.write(ci.args[0], ored); self.RSTAT.set(Flags::FZERO, ored == 0); } OP::WRIT => { let arg = self.read(ci.args[0]); println!("ch: `{}`", arg); } _ => { eprintln!("unsupported opcode {:?}, continuing", oc); return false; } } if should_inc_ip { self.IP += inc_by as u16; } true } } fn dump_instructions(mut x: &[u16]) { while !x.is_empty() { let (i, inc) = parse_instruction(x); x = &x[inc..]; //println!("{:?}", i); } } fn main() { let bin = include_bytes!("../challenge.mctf"); let (header, body) = bin.split_at(4); assert_eq!(header, b"mCTZ"); let decompressed = zstd::decode_all(body).unwrap(); let mut csr = Cursor::new(decompressed); let code = parse_chunk(&mut csr); let sin = parse_chunk(&mut csr); let mut codeinner = code.inner(); let mut m = Machine::default(); m.SIN[1..=sin.inner().len()].copy_from_slice(sin.inner()); m.SCODE[1..=code.inner().len()].copy_from_slice(code.inner()); // dump_instructions(&m.SCODE[1..=code.inner().len()]); while m.step() {} }