diff --git a/Cargo.lock b/Cargo.lock index b696ec7..d60846b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,54 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "bitflags" version = "2.4.0" @@ -31,11 +79,45 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "copycat" version = "0.1.0" dependencies = [ "bitflags", + "clap", "hashbrown", ] @@ -55,8 +137,86 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index 970c365..5832985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] bitflags = "2.4.0" +clap = { version = "4.4.6", features = ["cargo"] } hashbrown = "0.14.0" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6eed03f --- /dev/null +++ b/build.rs @@ -0,0 +1,13 @@ +use std::process::Command; + +fn main() { + // Tell Cargo that if the given file changes, to rerun this build script. + println!("cargo:rerun-if-changed=crabbios/crabbios.asm"); + Command::new("nasm") + .arg("crabbios/crabbios.asm") + .args(["-f", "bin"]) + .args(["-o", "roms/crabbios.bin"]) + .args(["-I", "crabbios"]) + .spawn() + .unwrap(); +} diff --git a/crabbios/README.md b/crabbios/README.md new file mode 100644 index 0000000..8d16014 --- /dev/null +++ b/crabbios/README.md @@ -0,0 +1,3 @@ +# CrabBIOS + +BIOS implementation in x86 real mode assembly diff --git a/crabbios/crabbios.asm b/crabbios/crabbios.asm new file mode 100644 index 0000000..f1b133c --- /dev/null +++ b/crabbios/crabbios.asm @@ -0,0 +1,27 @@ +bits 16 ; we're in 16 bit real mode +org 0xfe000 ; that's where we're loaded + +start: ; we jump here after the entry point +mov ax, 0xFF +push ax +pop bx +hlt + +; fill until the reset vector +times 4080 - ($ - start) db 0 + +; entry point +; we shouldn't assume any initial state here +cli + +; set up stack at 0:0x7c00 +; that's just below where we'll load the boot sector +mov ax, 0 +mov ss, ax +mov sp, 0x7c00 + +; jump over to start +jmp start + +; fill the last bytes +times 4096 - ($ - start) db 0 diff --git a/src/main.rs b/src/main.rs index ec0c8a2..4f7961d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,16 +4,40 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::fs; + +use clap::{command, arg}; use copycat::memory::{Memory, KiB}; use copycat::processor::{Instruction, Processor}; use copycat::io::IoBus; -static BIOS_ROM: &[u8; 8 * KiB as usize] = include_bytes!("../roms/BIOS_IBM5150_24APR81_5700051_U33.BIN"); +static BIOS_ROM: &[u8; 4 * KiB as usize] = include_bytes!("../roms/crabbios.bin"); fn main() { + let matches = command!() + .arg(arg!(-b --bios "Firmware image to load")) + .get_matches(); + + let maybe_bios; + let bios: &[u8] = match matches.get_one::("bios") { + Some(bios) => { + maybe_bios = fs::read(bios).unwrap(); + &maybe_bios + }, + None => BIOS_ROM, + }; + if bios.len() == 0 { + panic!("firmware image empty"); + } + if bios.len() % 4 * KiB as usize != 0 { + panic!("firmware image length not a multiple of a page"); + } + let bios_len = bios.len() / (4 * KiB) as usize; + let mut mem = Memory::new(64 * KiB); - mem.add_tlb_entry(0xfe, BIOS_ROM.as_ptr().cast_mut().cast()); - mem.add_tlb_entry(0xff, unsafe { BIOS_ROM.as_ptr().add(4 * KiB as usize) }.cast_mut().cast()); + for i in 0xff - bios_len + 1..=0xff { + mem.add_tlb_entry(i as u64, unsafe { bios.as_ptr().add((i - (0xff - bios_len + 1)) * 4 * KiB as usize) }.cast_mut().cast()); + } // mem.write(0xffff0, [0x90, 0x04, 0x69, 0xF4]); let mut io = IoBus::default(); let mut cpu = Processor::default(); diff --git a/src/processor/instruction.rs b/src/processor/instruction.rs index 3ff0387..c4c8fb7 100644 --- a/src/processor/instruction.rs +++ b/src/processor/instruction.rs @@ -98,6 +98,22 @@ impl Instruction { 0x4D => (Dec, BP, NoOperand), 0x4E => (Dec, SI, NoOperand), 0x4F => (Dec, DI, NoOperand), + 0x50 => (Push, AX, NoOperand), + 0x51 => (Push, CX, NoOperand), + 0x52 => (Push, DX, NoOperand), + 0x53 => (Push, BX, NoOperand), + 0x54 => (Push, SP, NoOperand), + 0x55 => (Push, BP, NoOperand), + 0x56 => (Push, SI, NoOperand), + 0x57 => (Push, DI, NoOperand), + 0x58 => (Pop, AX, NoOperand), + 0x59 => (Pop, CX, NoOperand), + 0x5A => (Pop, DX, NoOperand), + 0x5B => (Pop, BX, NoOperand), + 0x5C => (Pop, SP, NoOperand), + 0x5D => (Pop, BP, NoOperand), + 0x5E => (Pop, SI, NoOperand), + 0x5F => (Pop, DI, NoOperand), 0x70 => (Jo, Imm8, NoOperand), 0x71 => (Jno, Imm8, NoOperand), 0x72 => (Jc, Imm8, NoOperand), @@ -139,6 +155,10 @@ impl Instruction { 0xBD => (Mov, BP, Imm16), 0xBE => (Mov, SI, Imm16), 0xBF => (Mov, DI, Imm16), + 0xC2 => (Retn, Imm16, NoOperand), + 0xC3 => (Retn, NoOperand, NoOperand), + 0xCA => (Retf, Imm16, NoOperand), + 0xCB => (Retf, NoOperand, NoOperand), 0xD0 => { modrm = ModRM::from_bits(memory.read_u8(address + size as u64)); size += 1; @@ -448,6 +468,10 @@ pub enum Mnemonic { Nop, Or, Out, + Pop, + Push, + Retf, + Retn, Sahf, Sbb, Shl, diff --git a/src/processor/mod.rs b/src/processor/mod.rs index 8b94895..0c336e3 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -195,6 +195,14 @@ impl Processor { self.set_operand16(value, memory, op1, instruction.segment_override); }, + (Push, Operand(op1, Word), _) => { + let value = self.operand16(memory, op1, instruction.segment_override); + self.push16(value, memory); + }, + (Pop, Operand(op1, Word), _) => { + let value = self.pop16(memory); + self.set_operand16(value, memory, op1, instruction.segment_override); + }, (Jo, Operand(Imm8(op1), Byte), _) => if self.registers.of() { self.registers.add_ip(op1 as i8 as i16) }, (Jno, Operand(Imm8(op1), Byte), _) => @@ -238,6 +246,28 @@ impl Processor { self.set_operand8(self.operand8(memory, op2, instruction.segment_override), memory, op1, instruction.segment_override), (Mov, Operand(op1, Word), Operand(op2, Word)) => self.set_operand16(self.operand16(memory, op2, instruction.segment_override), memory, op1, instruction.segment_override), + (Retn, Operand(Imm16(parameters), _), _) => { + let ip = self.pop16(memory); + self.registers.set_ip(ip); + self.registers.set_sp(self.registers.sp() + parameters); + }, + (Retn, _, _) => { + let ip = self.pop16(memory); + self.registers.set_ip(ip); + }, + (Retf, Operand(Imm16(parameters), _), _) => { + let ip = self.pop16(memory); + let cs = self.pop16(memory); + self.registers.set_ip(ip); + self.registers.set_cs(cs); + self.registers.set_sp(self.registers.sp() + parameters); + }, + (Retf, _, _) => { + let ip = self.pop16(memory); + let cs = self.pop16(memory); + self.registers.set_ip(ip); + self.registers.set_cs(cs); + }, (Shl, Operand(op1, Byte), Operand(op2, Byte)) => 'shl: { let dst = self.operand8(memory, op1, instruction.segment_override); let shift = self.operand8(memory, op2, instruction.segment_override); @@ -500,4 +530,16 @@ impl Processor { SegmentOverride::DS | SegmentOverride::None => self.registers.ds(), } as u64) << 4) + offset as u64 } + + fn push16(&mut self, value: u16, memory: &mut Memory) { + self.registers.set_sp(self.registers.sp() - 2); + let address = ((self.registers.ss() as u64) << 4) + self.registers.sp() as u64; + memory.write_u16(address, value); + } + + fn pop16(&mut self, memory: &Memory) -> u16 { + let address = ((self.registers.ss() as u64) << 4) + self.registers.sp() as u64; + self.registers.set_sp(self.registers.sp() + 2); + memory.read_u16(address) + } }