Skip to content

Instantly share code, notes, and snippets.

@yuriks
Created November 11, 2018 00:22
Show Gist options
  • Save yuriks/51380e2b75773425329e3f11b8838a25 to your computer and use it in GitHub Desktop.
Save yuriks/51380e2b75773425329e3f11b8838a25 to your computer and use it in GitHub Desktop.
mod decode;
use self::decode::DecodeInstruction;
use self::decode::DecodedArmInstruction;
use scheduler::GeneratorTask;
use scheduler::Task;
use system::AccessWidth;
use system::Bus;
use system::MemoryRequest;
use system::OperationType;
// Named constants for common registers
const LR: usize = 14;
const PC: usize = 15;
struct ArmCpu {
regs: [u32; 16],
cpsr: u32,
}
impl ArmCpu {
fn new() -> ArmCpu {
ArmCpu {
regs: [0; 16],
cpsr: 0,
}
}
}
fn run_cpu<'a>(cpu: &'a mut ArmCpu, bus: &'a Bus) -> impl Task<'a, Return = ()> {
GeneratorTask::new(move || {
let mut sequential_fetch = false;
let mut refill_steps = 2;
let mut instr_decoding = 0xFFFFFFFF;
let mut instr_executing = 0xFFFFFFFF;
loop {
println!("F[${:X}], D[{:08X}], E[{:08X}]", cpu.regs[PC], instr_decoding, instr_executing);
bus.make_request(MemoryRequest {
address: cpu.regs[PC],
width: AccessWidth::Bit32,
op: OperationType::Read {
is_instruction: true,
},
seq: sequential_fetch,
});
println!("Fetching from PC={:X}", cpu.regs[PC]);
sequential_fetch = true;
if refill_steps > 0 {
println!("Skipping execute");
refill_steps -= 1;
cpu.regs[PC] = cpu.regs[PC].wrapping_add(4);
} else {
println!("Executing {:08X}", instr_executing);
let decoded_instr = DecodedArmInstruction::decode_arm_instruction(instr_executing);
match decoded_instr {
DecodedArmInstruction::BranchImm { cond, link, offset } => {
sequential_fetch = false;
refill_steps = 2;
if link {
cpu.regs[LR] = cpu.regs[PC].wrapping_sub(4);
}
// TODO: Handle faulting on bad address
cpu.regs[PC] = cpu.regs[PC].wrapping_add((offset * 4) as u32);
println!("Branching to PC={:0X}", cpu.regs[PC]);
}
instr => unimplemented!("Unimplemented instruction execute: {:?}", instr),
}
if refill_steps == 0 {
cpu.regs[PC] = cpu.regs[PC].wrapping_add(4);
}
}
wait_cycles!(1);
while bus.should_cpu_wait() {
println!("waitstate");
wait_cycles!(1);
}
instr_executing = instr_decoding;
instr_decoding = bus.data.get();
}
})
}
#[cfg(test)]
mod test {
use super::*;
use std::pin::Pin;
#[test]
fn test_branch() {
let bus = Default::default();
let mut cpu = ArmCpu::new();
let mut cpu_task = Box::pinned(run_cpu(&mut cpu, &bus));
cpu_task.as_mut().step();
assert_eq!(
bus.request.get(),
Some(MemoryRequest {
address: 0,
width: AccessWidth::Bit32,
op: OperationType::Read {
is_instruction: true
},
seq: false,
})
);
bus.data.set(0xEA000006);
cpu_task.as_mut().step();
assert_eq!(
bus.request.get(),
Some(MemoryRequest {
address: 4,
width: AccessWidth::Bit32,
op: OperationType::Read {
is_instruction: true
},
seq: true,
})
);
bus.data.set(0xFFFFFFFF);
cpu_task.as_mut().step();
assert_eq!(
bus.request.get(),
Some(MemoryRequest {
address: 8,
width: AccessWidth::Bit32,
op: OperationType::Read {
is_instruction: true
},
seq: true,
})
);
bus.data.set(0xFFFFFFFF);
cpu_task.as_mut().step();
assert_eq!(
bus.request.get(),
Some(MemoryRequest {
address: 0x20,
width: AccessWidth::Bit32,
op: OperationType::Read {
is_instruction: true
},
seq: false,
})
);
bus.busy.set(true);
cpu_task.as_mut().step();
bus.busy.set(false);
bus.data.set(0xE3A00302);
cpu_task.as_mut().step();
assert_eq!(
bus.request.get(),
Some(MemoryRequest {
address: 0x24,
width: AccessWidth::Bit32,
op: OperationType::Read {
is_instruction: true
},
seq: true,
})
);
}
}
test cpu::test::test_branch ...
F[$0], D[FFFFFFFF], E[FFFFFFFF]
Fetching from PC=0
Skipping execute
F[$4], D[EA000006], E[FFFFFFFF]
Fetching from PC=4
Skipping execute
F[$8], D[FFFFFFFF], E[EA000006]
Fetching from PC=8
Executing EA000006
Branching to PC=20
F[$20], D[FFFFFFFF], E[FFFFFFFF]
Fetching from PC=20
Skipping execute
waitstate
F[$24], D[E3A00302], E[FFFFFFFF]
Fetching from PC=24
Skipping execute
ok
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment