diff --git a/src/chip.rs b/src/chip.rs index cf1847b..40c96da 100644 --- a/src/chip.rs +++ b/src/chip.rs @@ -1,21 +1,33 @@ -/* use cpu::CPU; +use slog; + pub struct Chip { - cpu: CPU, - rom: Box<[u8]>, - ram: Box<[u8]>, + log: slog::Logger, + pub cpu: CPU, + pub rom: Box<[u8]>, + pub ram: Box<[u8]>, // TODO: List of devices } impl Chip { - pub fn new(rom: Box<[u8]>) -> Chip { + pub fn new(log: slog::Logger, rom: Box<[u8]>) -> Chip { Self { - cpu: CPU::new(), + log: log.clone(), + cpu: CPU::new(log.clone()), rom: rom, ram: Box::new([0u8; 8 * 1024 + 1024]), } } + + pub fn step(&mut self) -> bool { + match self.cpu.step(&mut self.rom, &mut self.ram) { + Ok(_) => true, + Err(ref e) => { + warn!(self.log, "Error occured: {:?}", e); + false + } + } + } } -*/ \ No newline at end of file diff --git a/src/cpu.rs b/src/cpu.rs index ce0515d..65b7fd9 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -17,6 +17,7 @@ pub enum CPUError { OutOfBoundsException, UnsupportedAddress, DecodingError(decoder::DecodingError), + Breakpoint, Exit, } @@ -69,7 +70,7 @@ impl CPU { } } - fn get_sp(&self, mem: &[u8]) -> u16 { + pub fn get_sp(&self, mem: &[u8]) -> u16 { mem[chip_definitions::IOAdress::SPL as usize] as u16 | ((mem[chip_definitions::IOAdress::SPH as usize] as u16) << 8) } @@ -242,7 +243,7 @@ impl CPU { } else if addr == 0x320 || addr == 0x321 || addr == 0x322 { // DAC? @DD2E - } else if addr == 0x230 || addr == 0x231 || addr == 0x234 || addr == 0x235 { // ADC + } else if addr == 0x230 || addr == 0x231 || addr == 0x234 || addr == 0x235 { // ADC } else if addr >= 0x1f00 { } else { @@ -738,6 +739,9 @@ impl CPU { let rv = self.get_register(r); self.set_register(r, rv >> 4 | ((rv << 4) & 0xF0)); }, + Instruction::BREAK => { + return Err(CPUError::Breakpoint); + } Instruction::NOP => {}, _ => return Err(CPUError::UnimplementedInstruction) } diff --git a/src/gdbstub.rs b/src/gdbstub.rs index b903198..6c7304c 100644 --- a/src/gdbstub.rs +++ b/src/gdbstub.rs @@ -1,9 +1,11 @@ +use std::collections::HashSet; use std; use std::net::TcpListener; use std::io::{Read, Write}; use slog; +use chip; use cpu; #[derive(Debug)] @@ -14,7 +16,7 @@ pub struct GDBPacket { /* At a minimum, a stub is required to support the ‘g’ and ‘G’ commands for register access, and the ‘m’ and ‘M’ commands for memory access. Stubs that only control single-threaded targets can implement run control with the ‘c’ (continue), and ‘s’ (step) commands. -Stubs that support multi-threading targets should support the ‘vCont’ command. All other commands are optional. +Stubs that support multi-threading targets should support the ‘vCont’ command. All other commands are optional. */ #[derive(Debug)] @@ -55,7 +57,8 @@ impl GDBPacket { } let checksum = u8::from_str_radix( - std::str::from_utf8(&checksum).map_err(|_| (GDBPacketError::InvalidFormat))?, 16) + std::str::from_utf8(&checksum) + .map_err(|_| (GDBPacketError::InvalidFormat))?, 16) .map_err(|_| (GDBPacketError::InvalidFormat))?; let p = GDBPacket { raw_data: decoded_data.clone() @@ -93,28 +96,36 @@ impl GDBPacket { } } -fn step(log: &slog::Logger, cpu: &mut cpu::CPU, rom: &mut [u8], ram: &mut [u8]) -> bool{ - let r = cpu.step(rom, ram); - match r { - Ok(_) => {true} - Err(ref e) => { - warn!(log, "Error occured: {:?}", e); - false - } +fn split_definitions(pkg: &str) -> Vec<(String, String)> { + let mut res = Vec::new(); + for definition in pkg.split(";") { + let mut v = definition.split(":"); + let first = v.nth(0).unwrap().to_string(); + res.push((first, v.collect::())); } + res } -fn stop_reply(cpu: &cpu::CPU) -> Vec { +/* +struct GDBStub { + chip: &mut chip::Chip, +} +*/ + +fn stop_reply(chip: &chip::Chip) -> Vec { + // Send SIGTRAP along with SREG/SP/PC. format!("T0520:{:02X};21:{:04X};22:{:08X};", - cpu.sreg, 1337u16.swap_bytes(), (2 * cpu.pc).swap_bytes() + chip.cpu.sreg, chip.cpu.get_sp(&chip.ram).swap_bytes(), + (2 * chip.cpu.pc).swap_bytes() ).as_bytes().to_vec() } -pub fn run(log: slog::Logger, cpu: &mut cpu::CPU, rom: &mut [u8], ram: &mut [u8]) { +pub fn run(log: slog::Logger, chip: &mut chip::Chip) { let listener = TcpListener::bind("0.0.0.0:1234").unwrap(); let mut conn = listener.incoming().nth(0).unwrap().unwrap(); info!(log, "Debugger? attached."); + let mut breakpoints: HashSet = HashSet::new(); loop { let mut buf = Vec::new(); @@ -128,16 +139,13 @@ pub fn run(log: slog::Logger, cpu: &mut cpu::CPU, rom: &mut [u8], ram: &mut [u8] continue; } buf.push(s[0]); - // warn!(log, "{:?}", std::str::from_utf8(&buf)); match GDBPacket::from_packet(&buf) { Ok(_) => break 'a, Err(_) => {} } } let pkg = GDBPacket::from_packet(&buf).unwrap(); - // warn!(log, "Got pkg: {:?}", pkg); warn!(log, "<- {:?}", std::str::from_utf8(&pkg.raw_data)); - // Send ACK { let s = [b'+']; if conn.write(&s).unwrap() != 1 { @@ -146,39 +154,68 @@ pub fn run(log: slog::Logger, cpu: &mut cpu::CPU, rom: &mut [u8], ram: &mut [u8] } let mut response = Vec::new(); - if pkg.raw_data[0] == b'q' { let query = std::str::from_utf8(&pkg.raw_data).unwrap(); - info!(log, "Got query: {}", query); + let query_data = split_definitions(&query[1..]); // TODO: Let's do this right :( - if query.chars().skip(1).take(9).collect::() == "Supported" { - response = "PacketSize=1024".as_bytes().to_vec(); - } else if pkg.raw_data[1] == b'C' { - response = "0".as_bytes().to_vec(); + if query_data[0].0 == "Supported" { + response = "qXfer:memory-map:read+;PacketSize=1024".as_bytes().to_vec(); + } else if query_data[0].0 == "Attached" { + response = "1".as_bytes().to_vec(); + } else if query_data[0].0 == "Xfer" {// && query_data[0].1.find("memory-map:read").is_some() { + response = format!( + "l\n\n\ + \n\ + 0x80\n", chip.rom.len(), chip.ram.len() + ).as_bytes().to_vec(); + } else if query_data[0].0 == "C" { + response = "01".as_bytes().to_vec(); } else { - // panic!("Unknown query"); + info!(log, "Unknown query: {}", query_data[0].0); } } else if pkg.raw_data[0] == b'H' { - // TODO: Make sure we have the right stuff here. + // Set thread - we only support one thread thus we ignore this. response = "OK".as_bytes().to_vec(); } else if pkg.raw_data[0] == b'?' { + // S05 = Stopped cause of SIGTRAP. response = "S05".as_bytes().to_vec(); } else if pkg.raw_data[0] == b'g' { + // Read registers. for i in 0..32 { - response.extend(format!("{:02X}", cpu.registers[i]).as_bytes().to_vec()); + response.extend(format!("{:02X}", chip.cpu.registers[i]) + .as_bytes().to_vec()); } - response.extend(format!("{:02X}", cpu.sreg).as_bytes().to_vec()); - response.extend(format!("{:04X}", 0x1337u16.swap_bytes()).as_bytes().to_vec()); // TODO: SP - response.extend(format!("{:08X}", (2*cpu.pc).swap_bytes()).as_bytes().to_vec()); + response.extend(format!("{:02X}", chip.cpu.sreg) + .as_bytes().to_vec()); + response.extend(format!("{:04X}", chip.cpu.get_sp(&chip.ram).swap_bytes()) + .as_bytes().to_vec()); + response.extend(format!("{:08X}", (2 * chip.cpu.pc).swap_bytes()) + .as_bytes().to_vec()); } else if pkg.raw_data[0] == b's' { - // TODO: Optional PC argument? - if !step(&log, cpu, rom, ram) { + let resume_from = std::str::from_utf8(&pkg.raw_data[1..]) + .unwrap_or(""); + let resume_from = u32::from_str_radix(resume_from, 16); + if let Ok(pc) = resume_from { + chip.cpu.pc = pc; + } + if !chip.step() { // ERR } - response = stop_reply(cpu); + response = stop_reply(&chip); } else if pkg.raw_data[0] == b'c' { - while step(&log, cpu, rom, ram) {} - response = stop_reply(cpu); + let resume_from = std::str::from_utf8(&pkg.raw_data[1..]) + .unwrap_or(""); + let resume_from = u32::from_str_radix(resume_from, 16); + if let Ok(pc) = resume_from { + chip.cpu.pc = pc; + } + while chip.step() { + // Maximum efficiency. + if breakpoints.contains(&chip.cpu.pc) { + break; + } + } + response = stop_reply(&chip); } else if pkg.raw_data[0] == b'm' { // Read memory let full_cmd = std::str::from_utf8(&pkg.raw_data).unwrap().chars().skip(1).collect::(); @@ -196,12 +233,56 @@ pub fn run(log: slog::Logger, cpu: &mut cpu::CPU, rom: &mut [u8], ram: &mut [u8] // TODO: Overflow checks. if addr & 0x00f00000 > 0 { // RAM - response.extend(format!("{:02X}", ram[addr & 0xFFFFF]).as_bytes().to_vec()); + response.extend(format!("{:02X}", chip.ram[addr & 0xFFFFF]).as_bytes().to_vec()); } else { // ROM - response.extend(format!("{:02X}", rom[addr]).as_bytes().to_vec()); + response.extend(format!("{:02X}", chip.rom[addr]).as_bytes().to_vec()); } } + } else if pkg.raw_data[0] == b'M' { + // Write memory + let full_cmd = std::str::from_utf8(&pkg.raw_data).unwrap().chars().skip(1).collect::(); + let addrlen_content = full_cmd.split(":").collect::>(); + let parts = addrlen_content[0].split(",").collect::>(); + let value = addrlen_content[1]; + let mem_addr = usize::from_str_radix(parts[0], 16).unwrap(); + let len = usize::from_str_radix(parts[1], 16).unwrap(); + for i in 0..len { + // Copied from megumi again <3 + // GDB uses a special addressing to allow addressing of flash, SRAM, etc. + // - flash starts at 0x00000000 + // - SRAM starts at 0x00800000 + // - mask for memory space is 0x00f00000 + // Constants used below are retrieved from GDB sources, in avr-tdep.c. + let addr = mem_addr.wrapping_add(i); + // Very hax wow + if addr & 0x00f00000 > 0 { + chip.ram[addr & 0xFFFFF] = u8::from_str_radix(&value[2 * i..2 * (i + 1)], 16).unwrap(); + } else { + // ROM + chip.rom[addr & 0xFFFFF] = u8::from_str_radix(&value[2 * i..2 * (i + 1)], 16).unwrap(); + } + } + } else if pkg.raw_data[0] == b'z' || pkg.raw_data[0] == b'Z' { + // insert(Z)/remove(z) breakpoint. + let d = std::str::from_utf8(&pkg.raw_data[1..]).unwrap(); + let values = d.split(",").collect::>(); + let bp_type = values[0]; + let bp_addr = u32::from_str_radix(&values[1], 16).unwrap(); + let bp_kind = values[2]; + if pkg.raw_data[0] == b'z' { + breakpoints.remove(&bp_addr); + } else { + breakpoints.insert(bp_addr); + } + + } else if pkg.raw_data[0] == b'v' { + let word = std::str::from_utf8(&pkg.raw_data[1..]).unwrap(); + let word_payload = word.split(";").collect::>(); + let word = word_payload[0]; + if word == "MustReplyEmpty" { + // Empty reply ;) + } } else { info!(log, "Unknown cmd: {}", pkg.raw_data[0]); // Unknown @@ -228,8 +309,8 @@ fn main() { println!("Sample pkg: {:?}", String::from_utf8_lossy(&GDBPacket { raw_data: vec![]}.to_packet_format())); let pkg = GDBPacket::from_packet(&"$#00".as_bytes().iter().map(|v| *v).collect::>()).expect("Could not parse pkg"); - // Handler for g: Read general registers. - // Each byte of register data is described by two hex digits. The bytes with the register are transmitted in target byte order. The size of each register and their position within the ‘g’ packet are determined by the GDB internal gdbarch functions DEPRECATED_REGISTER_RAW_SIZE and gdbarch_register_name. + // Handler for g: Read general registers. + // Each byte of register data is described by two hex digits. The bytes with the register are transmitted in target byte order. The size of each register and their position within the ‘g’ packet are determined by the GDB internal gdbarch functions DEPRECATED_REGISTER_RAW_SIZE and gdbarch_register_name. // From megumi: 35 value,s total 39 bytes // 0-31: Regular regs // 33 SREG @@ -241,4 +322,4 @@ fn main() { // m: addr:hex,len:hex // s c } -*/ \ No newline at end of file +*/ diff --git a/src/main.rs b/src/main.rs index 3261498..7bc4e06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,14 +29,15 @@ fn main() { ); info!(log, "AVREmu starting up"); - let mut rom = read_file(std::env::args().nth(1).unwrap_or("rom.bin".to_string())).unwrap(); - let mut ram = [0u8; 8 * 1024 + 8 * 1024]; - let mut cpu = cpu::CPU::new(log.clone()); + let rom = read_file(std::env::args().nth(1).unwrap_or("rom.bin".to_string())).unwrap(); + let mut chip = chip::Chip::new(log.clone(), rom); + + // Use GDBStub info!(log, "Enabling GDB backend"); - gdbstub::run(log.clone(), &mut cpu, &mut rom, &mut ram); - warn!(log, "{}", cpu); - write_file("ram.dmp", &ram).unwrap(); + gdbstub::run(log.clone(), &mut chip); + warn!(log, "{}", &chip.cpu); + write_file("ram.dmp", &chip.ram).unwrap(); } pub fn read_file>(rom_path: P) -> Result, io::Error> {