use amplify::num::u3;
#[cfg(feature = "log")]
use baid64::DisplayBaid64;
use super::{Lib, Marshaller};
use crate::isa::{Bytecode, BytecodeRead, ExecStep, Instruction};
use crate::{Core, LibId, Site, SiteId};
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
pub enum Jump<Id: SiteId> {
#[display("halt")]
Halt,
#[display("={0}")]
Instr(Site<Id>),
#[display(">{0}")]
Next(Site<Id>),
}
impl Lib {
pub fn exec<Instr>(
&self,
entrypoint: u16,
skip_first: bool,
core: &mut Core<LibId, Instr::Core>,
context: &Instr::Context<'_>,
) -> Jump<LibId>
where
Instr: Instruction<LibId> + Bytecode<LibId>,
{
#[cfg(feature = "log")]
let (m, w, d, g, r, y, z) = (
"\x1B[0;35m",
"\x1B[1;1m",
"\x1B[0;37;2m",
"\x1B[0;32m",
"\x1B[0;31m",
"\x1B[0;33m",
"\x1B[0m",
);
let mut marshaller = Marshaller::with(&self.code, &self.data, &self.libs);
let lib_id = self.lib_id();
#[cfg(feature = "log")]
let lib_mnemonic = lib_id.to_baid64_mnemonic();
#[cfg(feature = "log")]
let lib_ref = lib_mnemonic.split_at(5).0;
if marshaller.seek(entrypoint).is_err() {
let _ = core.fail_ck();
#[cfg(feature = "log")]
eprintln!("jump to non-existing offset; halting, {y}CK{z} is set to {r}false{z}");
return Jump::Halt;
}
#[cfg(feature = "log")]
let mut ck0 = core.ck();
#[cfg(feature = "log")]
let mut co0 = core.co();
if marshaller.is_eof() {
return Jump::Halt;
}
if skip_first {
if Instr::decode_instr(&mut marshaller).is_err() {
#[cfg(feature = "log")]
{
let (byte, bit) = marshaller.offset();
eprintln!(
"; unable to decode instruction at byte pos {byte:06X}.h, bit pos {bit}",
);
}
return Jump::Halt;
};
let next_pos = marshaller.offset();
debug_assert_eq!(next_pos.1, u3::ZERO);
#[cfg(feature = "log")]
eprintln!("; return to the caller offset {:06X}.h", next_pos.0);
}
while !marshaller.is_eof() {
let pos = marshaller.pos();
let Ok(instr) = Instr::decode_instr(&mut marshaller) else {
#[cfg(feature = "log")]
{
let (byte, bit) = marshaller.offset();
eprintln!(
"unable to decode instruction at byte pos {byte:06X}.h, bit pos {bit}",
);
}
return Jump::Halt;
};
#[cfg(feature = "log")]
let mut prev = bmap![];
#[cfg(feature = "log")]
#[allow(unused_assignments)]
let mut src_empty = true;
#[cfg(feature = "log")]
{
for reg in instr.dst_regs() {
prev.insert(reg, core.get(reg));
}
eprint!("site {m}{}@{pos:06}:{z} {: <32}; ", lib_ref, instr.to_string());
let src_regs = instr.src_regs();
src_empty = src_regs.is_empty();
let mut iter = src_regs.into_iter().peekable();
while let Some(reg) = iter.next() {
eprint!("{d}{reg}{z} ");
if let Some(val) = core.get(reg) {
eprint!("{w}{}{z}", val);
} else {
eprint!("{d}~{z}");
}
if iter.peek().is_some() {
eprint!(", ");
}
}
}
let next = instr.exec(Site::new(lib_id, pos), core, context);
#[cfg(feature = "log")]
{
if !src_empty {
if !prev.is_empty() {
eprint!(" => ");
} else if ck0 != core.ck() || co0 != core.co() || next != ExecStep::Next {
eprint!("; ");
}
}
let mut iter = instr.dst_regs().into_iter().peekable();
while let Some(reg) = iter.next() {
eprint!("{g}{reg}{z} ");
if let Some(val) = prev.get(®).unwrap() {
eprint!("{y}{}{z}", val);
} else {
eprint!("{d}~{z}");
}
eprint!(" -> ");
if let Some(val) = core.get(reg) {
eprint!("{y}{}{z}", val);
} else {
eprint!("{d}~{z}");
}
if iter.peek().is_some() {
eprint!(", ");
}
}
if !prev.is_empty() && (ck0 != core.ck() || co0 != core.co()) {
eprint!(", ");
}
if ck0 != core.ck() {
let p = if ck0.is_ok() { g } else { r };
let c = if core.ck().is_ok() { g } else { r };
eprint!("{y}CK{z} {p}{ck0}{z} -> {c}{}{z}", core.ck());
}
if ck0 != core.ck() && co0 != core.co() {
eprint!(", ");
}
if co0 != core.co() {
let p = if co0.is_ok() { g } else { r };
let c = if core.co().is_ok() { g } else { r };
eprint!("{y}CO{z} {p}{co0}{z} -> {c}{}{z}", core.co());
}
if (!prev.is_empty() || ck0 != core.ck() || co0 != core.co())
&& next != ExecStep::Next
{
eprint!(", ");
}
ck0 = core.ck();
co0 = core.co();
}
if !core.acc_complexity(instr.complexity()) {
let _ = core.fail_ck();
#[cfg(feature = "log")]
{
if !src_empty || !prev.is_empty() {
eprint!(", ");
}
eprintln!("halting, complexity overflow");
}
return Jump::Halt;
}
match next {
ExecStep::Stop => {
return Jump::Halt;
}
ExecStep::Fail => {
#[cfg(feature = "log")]
eprint!("{y}CK{z} {g}success{z} -> {r}fail{z}");
if core.fail_ck() {
#[cfg(feature = "log")]
eprintln!(", {y}CH{z} is {g}true{z}: halting");
return Jump::Halt;
}
#[cfg(feature = "log")]
eprintln!(", {y}CH{z} is {r}false{z}: continuing");
continue;
}
ExecStep::Next => {
#[cfg(feature = "log")]
eprintln!();
continue;
}
ExecStep::Jump(pos) => {
#[cfg(feature = "log")]
eprintln!("{d}jumping to{z} {m}{pos:06}{z}");
if marshaller.seek(pos).is_err() {
let _ = core.fail_ck();
#[cfg(feature = "log")]
eprintln!(
"jump to non-existing offset: unconditionally halting; {y}CK{z} is \
set to {r}fail{z}"
);
return Jump::Halt;
}
}
ExecStep::Call(site) => {
#[cfg(feature = "log")]
eprintln!("{d}calling{z} {m}{site}{z}");
return Jump::Instr(site);
}
ExecStep::Ret(site) => {
#[cfg(feature = "log")]
eprintln!("{d}returning to{z} {m}{site}{z}");
return Jump::Next(site);
}
}
}
Jump::Halt
}
}