Added github actions for PRs (#3)

* Added github actions for PRs

* Added some rustfmt::skip attributes

* Applied formatting

* Added rustfmt component in action

* Configured to use rustfmt version 2 which fixes some comment formatting

* Removed ready_for_review condition for github actions

Since it has the synchronize condition, it will update after each
commit, whether in draft or not, so I think this should be alright
This commit is contained in:
transistor fet 2024-03-17 11:03:52 -07:00 committed by GitHub
parent a2372d1355
commit 7dac32d844
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
99 changed files with 2394 additions and 1572 deletions

View File

@ -1 +1 @@
msrv = "1.60.0"
msrv = "1.70.0"

32
.github/workflows/clippy.yaml vendored Normal file
View File

@ -0,0 +1,32 @@
name: clippy
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install dependencies
run: |
sudo apt-get install -y alsa-base libasound2-dev libxkbcommon-dev
- name: Select rust version
run: |
rustup toolchain install 1.70 --profile minimal --no-self-update
rustup default 1.70
rustup component add clippy
- name: Check clippy
run: |
cargo clippy

32
.github/workflows/rustdoc.yaml vendored Normal file
View File

@ -0,0 +1,32 @@
name: rustdoc
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
jobs:
rustdocs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install dependencies
run: |
sudo apt-get install -y alsa-base libasound2-dev libxkbcommon-dev
- name: Select rust version
run: |
rm Cargo.lock
rustup toolchain install nightly --profile minimal --no-self-update
rustup default nightly
- name: Build rustdoc
run: |
RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features

32
.github/workflows/rustfmt.yaml vendored Normal file
View File

@ -0,0 +1,32 @@
name: rustfmt
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
jobs:
rustfmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install dependencies
run: |
sudo apt-get install -y alsa-base libasound2-dev libxkbcommon-dev
- name: Select rust version
run: |
rustup toolchain install nightly --profile minimal --no-self-update
rustup default nightly
rustup component add rustfmt
- name: Check rustfmt
run: |
cargo +nightly fmt --check

44
.github/workflows/test.yaml vendored Normal file
View File

@ -0,0 +1,44 @@
name: test
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
env:
RUSTFLAGS: '--deny warnings'
jobs:
test-ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install dependencies
run: |
sudo apt-get install -y alsa-base libasound2-dev libxkbcommon-dev
- name: Select rust version
run: |
rustup toolchain install 1.70 --profile minimal --no-self-update
rustup default 1.70
- name: Run tests with default features
run: |
cargo test
- name: Run tests with all features
run: |
cargo test #--features=std,fugit,femtos
- name: Run test with no_std
run: |
cargo test --no-default-features

24
.rustfmt.toml Normal file
View File

@ -0,0 +1,24 @@
edition = "2021"
version = "Two"
max_width = 132
struct_lit_width = 0 # default 18 (24)
fn_call_width=100 # default 60 (80)
array_width=132 # default 60 (80)
#chain_width=100 # default 60 (80)
#attr_fn_like_width=100 # default 70 (92)
#single_line_if_else_max_width=100 # default 50 (66)
#struct_variant_width = 0 # default 35 (46)
newline_style = "Unix"
reorder_imports = false
match_block_trailing_comma = true
## Experimental Features
unstable_features = true
blank_lines_upper_bound = 3
overflow_delimited_expr = true
# it would be nice to allow a newline at the top and bottom of file
# it would be nice to not erase the whitespace between the end of the code line and start of a comment on the same line

19
Cargo.lock generated
View File

@ -420,10 +420,6 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "emulator-hal"
version = "0.1.0"
dependencies = [
"femtos",
"fugit",
]
[[package]]
name = "emulator-hal-memory"
@ -491,21 +487,6 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "fugit"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
dependencies = [
"gcd",
]
[[package]]
name = "gcd"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]]
name = "glob"
version = "0.3.1"

View File

@ -1,4 +1,3 @@
use std::rc::Rc;
use std::cell::{RefCell, RefMut, BorrowMutError};
use std::sync::atomic::{AtomicUsize, Ordering};
@ -17,7 +16,7 @@ pub type Address = u64;
/// information that might be helpful for debugging.
pub trait Steppable {
fn step(&mut self, system: &System) -> Result<Duration, Error>;
fn on_error(&mut self, _system: &System) { }
fn on_error(&mut self, _system: &System) {}
}
/// A device that can receive an interrupt. The `interrupt_state_change()` method
@ -104,30 +103,22 @@ pub trait Addressable {
#[inline]
pub fn read_beu16(data: &[u8]) -> u16 {
(data[0] as u16) << 8 |
(data[1] as u16)
(data[0] as u16) << 8 | (data[1] as u16)
}
#[inline]
pub fn read_leu16(data: &[u8]) -> u16 {
(data[1] as u16) << 8 |
(data[0] as u16)
(data[1] as u16) << 8 | (data[0] as u16)
}
#[inline]
pub fn read_beu32(data: &[u8]) -> u32 {
(data[0] as u32) << 24 |
(data[1] as u32) << 16 |
(data[2] as u32) << 8 |
(data[3] as u32)
(data[0] as u32) << 24 | (data[1] as u32) << 16 | (data[2] as u32) << 8 | (data[3] as u32)
}
#[inline]
pub fn read_leu32(data: &[u8]) -> u32 {
(data[3] as u32) << 24 |
(data[2] as u32) << 16 |
(data[1] as u32) << 8 |
(data[0] as u32)
(data[3] as u32) << 24 | (data[2] as u32) << 16 | (data[1] as u32) << 8 | (data[0] as u32)
}
@ -239,7 +230,7 @@ pub struct Device(DeviceId, TransmutableBox);
impl Device {
pub fn new<T>(value: T) -> Self
where
T: Transmutable + 'static
T: Transmutable + 'static,
{
Self(DeviceId::new(), wrap_transmutable(value))
}

View File

@ -1,4 +1,3 @@
use std::fmt;
use moa_host::HostError;
@ -52,10 +51,7 @@ impl Error {
pub fn msg(&self) -> &str {
match self {
Error::Assertion(msg) |
Error::Breakpoint(msg) |
Error::Other(msg) |
Error::Emulator(_, msg) => msg.as_str(),
Error::Assertion(msg) | Error::Breakpoint(msg) | Error::Other(msg) | Error::Emulator(_, msg) => msg.as_str(),
Error::Processor(_) => "native exception",
}
}
@ -64,10 +60,7 @@ impl Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Assertion(msg) |
Error::Breakpoint(msg) |
Error::Other(msg) |
Error::Emulator(_, msg) => write!(f, "{}", msg),
Error::Assertion(msg) | Error::Breakpoint(msg) | Error::Other(msg) | Error::Emulator(_, msg) => write!(f, "{}", msg),
Error::Processor(_) => write!(f, "native exception"),
}
}
@ -78,4 +71,3 @@ impl<E> From<HostError<E>> for Error {
Self::Other("other".to_string())
}
}

View File

@ -1,4 +1,3 @@
use crate::error::Error;
@ -43,4 +42,3 @@ impl InterruptController {
Ok(acknowledge)
}
}

View File

@ -1,4 +1,3 @@
#[macro_use]
mod error;
@ -7,12 +6,15 @@ mod interrupts;
mod memory;
mod system;
pub use crate::devices::{Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device};
pub use crate::devices::{read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable};
pub use crate::devices::{
Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device,
};
pub use crate::devices::{
read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable,
};
pub use crate::error::Error;
pub use crate::interrupts::InterruptController;
pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory};
pub use crate::system::System;
pub use emulator_hal::bus::{BusAccess};

View File

@ -1,4 +1,3 @@
use std::fs;
use std::cmp;
use std::rc::Rc;
@ -20,7 +19,7 @@ impl MemoryBlock {
pub fn new(contents: Vec<u8>) -> MemoryBlock {
MemoryBlock {
read_only: false,
contents
contents,
}
}
@ -62,10 +61,13 @@ impl Addressable for MemoryBlock {
fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
if self.read_only {
return Err(Error::breakpoint(format!("Attempt to write to read-only memory at {:x} with data {:?}", addr, data)));
return Err(Error::breakpoint(format!(
"Attempt to write to read-only memory at {:x} with data {:?}",
addr, data
)));
}
self.contents[(addr as usize) .. (addr as usize) + data.len()].copy_from_slice(data);
self.contents[(addr as usize)..(addr as usize) + data.len()].copy_from_slice(data);
Ok(())
}
}
@ -99,12 +101,20 @@ impl Addressable for AddressRepeater {
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
let size = self.subdevice.borrow_mut().as_addressable().unwrap().size() as Address;
self.subdevice.borrow_mut().as_addressable().unwrap().read(clock, addr % size, data)
self.subdevice
.borrow_mut()
.as_addressable()
.unwrap()
.read(clock, addr % size, data)
}
fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
let size = self.subdevice.borrow_mut().as_addressable().unwrap().size() as Address;
self.subdevice.borrow_mut().as_addressable().unwrap().write(clock, addr % size, data)
self.subdevice
.borrow_mut()
.as_addressable()
.unwrap()
.write(clock, addr % size, data)
}
}
@ -125,7 +135,7 @@ pub struct AddressTranslator {
impl AddressTranslator {
pub fn new<F>(subdevice: Device, size: usize, func: F) -> Self
where
F: Fn(Address) -> Address + 'static
F: Fn(Address) -> Address + 'static,
{
Self {
subdevice,
@ -141,11 +151,19 @@ impl Addressable for AddressTranslator {
}
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
self.subdevice.borrow_mut().as_addressable().unwrap().read(clock, (self.func)(addr), data)
self.subdevice
.borrow_mut()
.as_addressable()
.unwrap()
.read(clock, (self.func)(addr), data)
}
fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
self.subdevice.borrow_mut().as_addressable().unwrap().write(clock, (self.func)(addr), data)
self.subdevice
.borrow_mut()
.as_addressable()
.unwrap()
.write(clock, (self.func)(addr), data)
}
}
@ -185,8 +203,16 @@ impl Bus {
pub fn insert(&mut self, base: Address, dev: Device) {
let size = dev.borrow_mut().as_addressable().unwrap().size();
let block = Block { base, size, dev };
let i = self.blocks.iter().position(|cur| cur.base > block.base).unwrap_or(self.blocks.len());
let block = Block {
base,
size,
dev,
};
let i = self
.blocks
.iter()
.position(|cur| cur.base > block.base)
.unwrap_or(self.blocks.len());
self.blocks.insert(i, block);
}
@ -252,7 +278,7 @@ impl Addressable for Bus {
Ok(result) => result,
Err(err) if self.ignore_unmapped => {
log::info!("{:?}", err);
return Ok(())
return Ok(());
},
Err(err) => return Err(err),
};
@ -270,7 +296,7 @@ impl Addressable for Bus {
Ok(result) => result,
Err(err) if self.ignore_unmapped => {
log::info!("{:?}", err);
return Ok(())
return Ok(());
},
Err(err) => return Err(err),
};
@ -300,7 +326,9 @@ impl BusPort {
}
pub fn dump_memory(&mut self, clock: Instant, addr: Address, count: Address) {
self.subdevice.borrow_mut().dump_memory(clock, self.offset + (addr & self.address_mask), count)
self.subdevice
.borrow_mut()
.dump_memory(clock, self.offset + (addr & self.address_mask), count)
}
#[inline]
@ -401,4 +429,3 @@ impl BusAccess<u64, Instant> for &mut dyn Addressable {
Ok(data.len())
}
}

View File

@ -1,4 +1,3 @@
use std::rc::Rc;
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
@ -45,7 +44,10 @@ impl System {
}
pub fn get_device(&self, name: &str) -> Result<Device, Error> {
self.devices.get(name).cloned().ok_or_else(|| Error::new(format!("system: no device named {}", name)))
self.devices
.get(name)
.cloned()
.ok_or_else(|| Error::new(format!("system: no device named {}", name)))
}
pub fn add_device(&mut self, name: &str, device: Device) -> Result<(), Error> {

View File

@ -1,4 +1,3 @@
use std::collections::HashMap;
use moa_parsing::{self as parser, AssemblyLine, AssemblyOperand, AssemblyParser, ParserError};
@ -24,6 +23,7 @@ impl From<ParserError> for Error {
#[repr(usize)]
#[derive(Copy, Clone)]
#[rustfmt::skip]
pub enum Disallow {
None = 0x0000,
NoDReg = 0x0001,
@ -136,7 +136,10 @@ impl M68kAssembler {
match reloc.rtype {
RelocationType::Displacement => {
// TODO this doesn't yet take into accound the origin
let location = *self.labels.get(&reloc.label).ok_or_else(|| Error::new(format!("error during relocation, label undefined {:?}", reloc.label)))?;
let location = *self
.labels
.get(&reloc.label)
.ok_or_else(|| Error::new(format!("error during relocation, label undefined {:?}", reloc.label)))?;
self.output[reloc.index] |= ((self.output[reloc.index] as i8 * 2 + 2) - (location as i8 * 2)) as u16 & 0x00ff;
},
_ => panic!("relocation type unimplemented"),
@ -166,12 +169,22 @@ impl M68kAssembler {
"bra" => {
let label = parser::expect_label(lineno, args)?;
self.output.push(0x6000);
self.relocations.push(Relocation::new(RelocationType::Displacement, label, self.output.len() - 1, self.current_origin));
self.relocations.push(Relocation::new(
RelocationType::Displacement,
label,
self.output.len() - 1,
self.current_origin,
));
},
"bsr" => {
let label = parser::expect_label(lineno, args)?;
self.output.push(0x6100);
self.relocations.push(Relocation::new(RelocationType::Displacement, label, self.output.len() - 1, self.current_origin));
self.relocations.push(Relocation::new(
RelocationType::Displacement,
label,
self.output.len() - 1,
self.current_origin,
));
},
"illegal" => {
self.output.push(0x4AFC);
@ -180,7 +193,8 @@ impl M68kAssembler {
"lea" => {
parser::expect_args(lineno, args, 2)?;
let reg = expect_address_register(lineno, &args[0])?;
let (effective_address, additional_words) = convert_target(lineno, &args[1], Size::Long, Disallow::NoRegsPrePostOrImmediate)?;
let (effective_address, additional_words) =
convert_target(lineno, &args[1], Size::Long, Disallow::NoRegsPrePostOrImmediate)?;
self.output.push(0x41C0 | (reg << 9) | effective_address);
self.output.extend(additional_words);
},
@ -213,7 +227,8 @@ impl M68kAssembler {
}
fn convert_sized_instruction(&mut self, lineno: usize, mneumonic: &str, args: &[AssemblyOperand]) -> Result<(), Error> {
let operation_size = get_size_from_mneumonic(mneumonic).ok_or_else(|| Error::new(format!("error at line {}: expected a size specifier (b/w/l)", lineno)));
let operation_size = get_size_from_mneumonic(mneumonic)
.ok_or_else(|| Error::new(format!("error at line {}: expected a size specifier (b/w/l)", lineno)));
match &mneumonic[..mneumonic.len() - 1] {
"addi" => {
self.convert_common_immediate_instruction(lineno, 0x0600, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
@ -229,7 +244,13 @@ impl M68kAssembler {
},
"andi" => {
if !self.check_convert_flags_instruction(lineno, 0x23C, 0x27C, args)? {
self.convert_common_immediate_instruction(lineno, 0x0200, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
self.convert_common_immediate_instruction(
lineno,
0x0200,
args,
operation_size?,
Disallow::NoARegImmediateOrPC,
)?;
}
},
"and" => {
@ -240,7 +261,13 @@ impl M68kAssembler {
},
"clr" => {
self.convert_common_single_operand_instruction(lineno, 0x4200, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
self.convert_common_single_operand_instruction(
lineno,
0x4200,
args,
operation_size?,
Disallow::NoARegImmediateOrPC,
)?;
},
"cmpi" => {
self.convert_common_immediate_instruction(lineno, 0x0C00, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
@ -251,7 +278,13 @@ impl M68kAssembler {
"eori" => {
if !self.check_convert_flags_instruction(lineno, 0x0A3C, 0x0A7C, args)? {
self.convert_common_immediate_instruction(lineno, 0x0A00, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
self.convert_common_immediate_instruction(
lineno,
0x0A00,
args,
operation_size?,
Disallow::NoARegImmediateOrPC,
)?;
}
},
"eor" => {
@ -265,27 +298,54 @@ impl M68kAssembler {
"move" | "movea" => {
let operation_size = operation_size?;
parser::expect_args(lineno, args, 2)?;
let (effective_address_left, additional_words_left) = convert_target(lineno, &args[0], operation_size, Disallow::None)?;
let (effective_address_right, additional_words_right) = convert_target(lineno, &args[1], operation_size, Disallow::None)?;
let (effective_address_left, additional_words_left) =
convert_target(lineno, &args[0], operation_size, Disallow::None)?;
let (effective_address_right, additional_words_right) =
convert_target(lineno, &args[1], operation_size, Disallow::None)?;
let effective_address_left = (effective_address_left >> 3) | (effective_address_left << 3);
self.output.push(encode_size_for_move(operation_size) | effective_address_left | effective_address_right);
self.output
.push(encode_size_for_move(operation_size) | effective_address_left | effective_address_right);
self.output.extend(additional_words_left);
self.output.extend(additional_words_right);
},
"neg" => {
self.convert_common_single_operand_instruction(lineno, 0x4400, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
self.convert_common_single_operand_instruction(
lineno,
0x4400,
args,
operation_size?,
Disallow::NoARegImmediateOrPC,
)?;
},
"negx" => {
self.convert_common_single_operand_instruction(lineno, 0x4000, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
self.convert_common_single_operand_instruction(
lineno,
0x4000,
args,
operation_size?,
Disallow::NoARegImmediateOrPC,
)?;
},
"not" => {
self.convert_common_single_operand_instruction(lineno, 0x4600, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
self.convert_common_single_operand_instruction(
lineno,
0x4600,
args,
operation_size?,
Disallow::NoARegImmediateOrPC,
)?;
},
"ori" => {
if !self.check_convert_flags_instruction(lineno, 0x003C, 0x007C, args)? {
self.convert_common_immediate_instruction(lineno, 0x0000, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
self.convert_common_immediate_instruction(
lineno,
0x0000,
args,
operation_size?,
Disallow::NoARegImmediateOrPC,
)?;
}
},
"or" => {
@ -319,7 +379,14 @@ impl M68kAssembler {
Ok(())
}
fn convert_common_immediate_instruction(&mut self, lineno: usize, opcode: u16, args: &[AssemblyOperand], operation_size: Size, disallow: Disallow) -> Result<(), Error> {
fn convert_common_immediate_instruction(
&mut self,
lineno: usize,
opcode: u16,
args: &[AssemblyOperand],
operation_size: Size,
disallow: Disallow,
) -> Result<(), Error> {
parser::expect_args(lineno, args, 2)?;
let immediate = parser::expect_immediate(lineno, &args[0])?;
let (effective_address, additional_words) = convert_target(lineno, &args[1], operation_size, disallow)?;
@ -329,27 +396,50 @@ impl M68kAssembler {
Ok(())
}
fn convert_common_dreg_instruction(&mut self, lineno: usize, opcode: u16, args: &[AssemblyOperand], operation_size: Size, disallow: Disallow) -> Result<(), Error> {
fn convert_common_dreg_instruction(
&mut self,
lineno: usize,
opcode: u16,
args: &[AssemblyOperand],
operation_size: Size,
disallow: Disallow,
) -> Result<(), Error> {
parser::expect_args(lineno, args, 2)?;
let (direction, reg, operand) = convert_reg_and_other(lineno, args, Disallow::NoAReg)?;
let (effective_address, additional_words) = convert_target(lineno, operand, operation_size, disallow)?;
self.output.push(opcode | encode_size(operation_size) | direction | (reg << 9) | effective_address);
self.output
.push(opcode | encode_size(operation_size) | direction | (reg << 9) | effective_address);
self.output.extend(additional_words);
Ok(())
}
fn convert_common_areg_instruction(&mut self, lineno: usize, opcode: u16, args: &[AssemblyOperand], operation_size: Size, disallow: Disallow) -> Result<(), Error> {
fn convert_common_areg_instruction(
&mut self,
lineno: usize,
opcode: u16,
args: &[AssemblyOperand],
operation_size: Size,
disallow: Disallow,
) -> Result<(), Error> {
let size_bit = expect_a_instruction_size(lineno, operation_size)?;
parser::expect_args(lineno, args, 2)?;
//let (_direction, reg, operand) = convert_reg_and_other(lineno, args, Disallow::NoDReg)?;
let reg = expect_address_register(lineno, &args[1])?;
let (effective_address, additional_words) = convert_target(lineno, &args[0], operation_size, disallow)?;
self.output.push(opcode | size_bit | (0b11 << 6) | (reg << 9) | effective_address);
self.output
.push(opcode | size_bit | (0b11 << 6) | (reg << 9) | effective_address);
self.output.extend(additional_words);
Ok(())
}
fn convert_common_single_operand_instruction(&mut self, lineno: usize, opcode: u16, args: &[AssemblyOperand], operation_size: Size, disallow: Disallow) -> Result<(), Error> {
fn convert_common_single_operand_instruction(
&mut self,
lineno: usize,
opcode: u16,
args: &[AssemblyOperand],
operation_size: Size,
disallow: Disallow,
) -> Result<(), Error> {
parser::expect_args(lineno, args, 1)?;
let (effective_address, additional_words) = convert_target(lineno, &args[0], operation_size, disallow)?;
self.output.push(opcode | encode_size(operation_size) | effective_address);
@ -357,7 +447,13 @@ impl M68kAssembler {
Ok(())
}
fn check_convert_flags_instruction(&mut self, lineno: usize, opcode_ccr: u16, opcode_sr: u16, args: &[AssemblyOperand]) -> Result<bool, Error> {
fn check_convert_flags_instruction(
&mut self,
lineno: usize,
opcode_ccr: u16,
opcode_sr: u16,
args: &[AssemblyOperand],
) -> Result<bool, Error> {
if let AssemblyOperand::Register(name) = &args[1] {
let opcode = match name.as_str() {
"ccr" => Some(opcode_ccr),
@ -376,32 +472,47 @@ impl M68kAssembler {
Ok(false)
}
fn convert_common_shift_instruction(&mut self, lineno: usize, mneumonic: &str, opcode: u16, args: &[AssemblyOperand], operation_size: Size) -> Result<(), Error> {
fn convert_common_shift_instruction(
&mut self,
lineno: usize,
mneumonic: &str,
opcode: u16,
args: &[AssemblyOperand],
operation_size: Size,
) -> Result<(), Error> {
let dirstr = &mneumonic[mneumonic.len() - 2..mneumonic.len() - 1];
let direction = if dirstr == "r" {
0
} else if dirstr == "l" {
1 << 8
} else {
return Err(Error::new(format!("error at line {}: expected direction of (l)eft or (r)ight, but found {:?}", lineno, dirstr)));
return Err(Error::new(format!(
"error at line {}: expected direction of (l)eft or (r)ight, but found {:?}",
lineno, dirstr
)));
};
match &args {
[AssemblyOperand::Immediate(_), AssemblyOperand::Register(_)] => {
let mut immediate = parser::expect_immediate(lineno, &args[0])?;
if !(1..=8).contains(&immediate) {
return Err(Error::new(format!("error at line {}: immediate value must be between 1 and 8, found {:?}", lineno, args)));
return Err(Error::new(format!(
"error at line {}: immediate value must be between 1 and 8, found {:?}",
lineno, args
)));
} else if immediate == 8 {
immediate = 0;
}
let reg = expect_data_register(lineno, &args[1])?;
self.output.push(opcode | ((immediate as u16) << 9) | direction | encode_size(operation_size) /*(0b0 << 5)*/ | reg);
self.output
.push(opcode | ((immediate as u16) << 9) | direction | encode_size(operation_size) /*(0b0 << 5)*/ | reg);
},
[AssemblyOperand::Register(_), AssemblyOperand::Register(_)] => {
let bit_reg = expect_data_register(lineno, &args[0])?;
let reg = expect_data_register(lineno, &args[1])?;
self.output.push(opcode | (bit_reg << 9) | direction | encode_size(operation_size) | (0b1 << 5) | reg);
self.output
.push(opcode | (bit_reg << 9) | direction | encode_size(operation_size) | (0b1 << 5) | reg);
},
//[_] => {
// let (effective_address, additional_words) = convert_target(lineno, &args[0], Size::Word, Disallow::NoRegsImmediateOrPC)?;
@ -416,16 +527,12 @@ impl M68kAssembler {
fn convert_target(lineno: usize, operand: &AssemblyOperand, size: Size, disallow: Disallow) -> Result<(u16, Vec<u16>), Error> {
match operand {
AssemblyOperand::Register(name) => {
convert_register(lineno, name, disallow)
},
AssemblyOperand::Register(name) => convert_register(lineno, name, disallow),
AssemblyOperand::Immediate(value) => {
disallow.check(lineno, Disallow::NoImmediate)?;
Ok((0b111100, convert_immediate(lineno, *value, size)?))
},
AssemblyOperand::Indirect(args) => {
convert_indirect(lineno, args, disallow)
},
AssemblyOperand::Indirect(args) => convert_indirect(lineno, args, disallow),
AssemblyOperand::IndirectPost(args, operator) => {
disallow.check(lineno, Disallow::NoIndirectPost)?;
if args.len() == 1 && operator == "+" {
@ -436,7 +543,10 @@ fn convert_target(lineno: usize, operand: &AssemblyOperand, size: Size, disallow
}
}
}
Err(Error::new(format!("error at line {}: post-increment operator can only be used with a single address register", lineno)))
Err(Error::new(format!(
"error at line {}: post-increment operator can only be used with a single address register",
lineno
)))
},
AssemblyOperand::IndirectPre(operator, args) => {
disallow.check(lineno, Disallow::NoIndirectPre)?;
@ -450,7 +560,10 @@ fn convert_target(lineno: usize, operand: &AssemblyOperand, size: Size, disallow
}
}
}
Err(Error::new(format!("error at line {}: pre-decrement operator can only be used with a single address register", lineno)))
Err(Error::new(format!(
"error at line {}: pre-decrement operator can only be used with a single address register",
lineno
)))
},
_ => Err(Error::new(format!("not implemented: {:?}", operand))),
}
@ -514,23 +627,25 @@ fn convert_indirect(lineno: usize, args: &[AssemblyOperand], disallow: Disallow)
}
},
// TODO add the MC68020 address options
_ => {
Err(Error::new(format!("error at line {}: expected valid indirect addressing mode, but found {:?}", lineno, args)))
}
_ => Err(Error::new(format!(
"error at line {}: expected valid indirect addressing mode, but found {:?}",
lineno, args
))),
}
}
fn convert_reg_and_other(lineno: usize, args: &[AssemblyOperand], _disallow: Disallow) -> Result<(u16, u16, &AssemblyOperand), Error> {
fn convert_reg_and_other(
lineno: usize,
args: &[AssemblyOperand],
_disallow: Disallow,
) -> Result<(u16, u16, &AssemblyOperand), Error> {
match &args {
[AssemblyOperand::Register(reg), effective_address] => {
Ok(((0b1 << 8), expect_reg_num(lineno, reg)?, effective_address))
},
[effective_address, AssemblyOperand::Register(reg)] => {
Ok(((0b0 << 8), expect_reg_num(lineno, reg)?, effective_address))
},
_ => {
Err(Error::new(format!("error at line {}: expected register and effective address, but found {:?}", lineno, args)))
}
[AssemblyOperand::Register(reg), effective_address] => Ok(((0b1 << 8), expect_reg_num(lineno, reg)?, effective_address)),
[effective_address, AssemblyOperand::Register(reg)] => Ok(((0b0 << 8), expect_reg_num(lineno, reg)?, effective_address)),
_ => Err(Error::new(format!(
"error at line {}: expected register and effective address, but found {:?}",
lineno, args
))),
}
}
@ -540,14 +655,24 @@ fn convert_immediate(lineno: usize, value: usize, size: Size) -> Result<Vec<u16>
if value <= u8::MAX as usize {
Ok(vec![value as u16])
} else {
Err(Error::new(format!("error at line {}: immediate number is out of range; must be less than {}, but number is {:?}", lineno, u8::MAX, value)))
Err(Error::new(format!(
"error at line {}: immediate number is out of range; must be less than {}, but number is {:?}",
lineno,
u8::MAX,
value
)))
}
},
Size::Word => {
if value <= u16::MAX as usize {
Ok(vec![value as u16])
} else {
Err(Error::new(format!("error at line {}: immediate number is out of range; must be less than {}, but number is {:?}", lineno, u16::MAX, value)))
Err(Error::new(format!(
"error at line {}: immediate number is out of range; must be less than {}, but number is {:?}",
lineno,
u16::MAX,
value
)))
}
},
Size::Long => Ok(vec![(value >> 16) as u16, value as u16]),
@ -569,7 +694,10 @@ fn expect_address_register(lineno: usize, operand: &AssemblyOperand) -> Result<u
return expect_reg_num(lineno, name);
}
}
Err(Error::new(format!("error at line {}: expected an address register, but found {:?}", lineno, operand)))
Err(Error::new(format!(
"error at line {}: expected an address register, but found {:?}",
lineno, operand
)))
}
fn expect_address_reg_num(lineno: usize, name: &str) -> Result<u16, Error> {
@ -603,7 +731,7 @@ fn get_size_from_mneumonic(s: &str) -> Option<Size> {
'b' => Some(Size::Byte),
'w' => Some(Size::Word),
'l' => Some(Size::Long),
_ => None
_ => None,
}
}
@ -628,7 +756,6 @@ fn encode_size_bit(size: Size) -> Result<u16, Error> {
match size {
Size::Word => Ok(0b01 << 6),
Size::Long => Ok(0b10 << 6),
_ => Err(Error::new(format!("invalid size for this operation: {:?}", size)))
_ => Err(Error::new(format!("invalid size for this operation: {:?}", size))),
}
}

View File

@ -1,4 +1,3 @@
use std::fs;
use std::env;
@ -24,4 +23,3 @@ fn main() {
},
};
}

View File

@ -1,4 +1,3 @@
use femtos::Instant;
use emulator_hal::bus::BusAccess;
@ -50,4 +49,3 @@ where
Ok(())
}
}

View File

@ -1,21 +1,10 @@
use femtos::Instant;
use emulator_hal::bus::BusAccess;
use crate::state::{M68kType, M68kError, Exceptions};
use crate::memory::{M68kBusPort, M68kAddress};
use crate::instructions::{
Size,
Sign,
Direction,
XRegister,
BaseRegister,
IndexRegister,
RegOrImmediate,
ControlRegister,
Condition,
Target,
Instruction,
Size, Sign, Direction, XRegister, BaseRegister, IndexRegister, RegOrImmediate, ControlRegister, Condition, Target, Instruction,
sign_extend_to_long,
};
@ -78,7 +67,13 @@ impl M68kDecoder {
}
#[inline]
pub fn decode_at<Bus>(&mut self, bus: &mut Bus, memory: &mut M68kBusPort, is_supervisor: bool, start: u32) -> Result<(), M68kError<Bus::Error>>
pub fn decode_at<Bus>(
&mut self,
bus: &mut Bus,
memory: &mut M68kBusPort,
is_supervisor: bool,
start: u32,
) -> Result<(), M68kError<Bus::Error>>
where
Bus: BusAccess<M68kAddress, Instant>,
{
@ -119,10 +114,9 @@ impl M68kDecoder {
where
Bus: BusAccess<M68kAddress, Instant>,
{
let ins_data: Result<String, M68kError<Bus::Error>> =
(0..((self.end - self.start) / 2)).map(|offset|
Ok(format!("{:04x} ", bus.read_beu16(clock, self.start + (offset * 2)).unwrap()))
).collect();
let ins_data: Result<String, M68kError<Bus::Error>> = (0..((self.end - self.start) / 2))
.map(|offset| Ok(format!("{:04x} ", bus.read_beu16(clock, self.start + (offset * 2)).unwrap())))
.collect();
println!("{:#010x}: {}\n\t{}\n", self.start, ins_data.unwrap(), self.instruction);
}
}
@ -186,7 +180,11 @@ where
} else if (ins & 0x138) == 0x108 {
let dreg = get_high_reg(ins);
let areg = get_low_reg(ins);
let dir = if (ins & 0x0080) == 0 { Direction::FromTarget } else { Direction::ToTarget };
let dir = if (ins & 0x0080) == 0 {
Direction::FromTarget
} else {
Direction::ToTarget
};
let size = if (ins & 0x0040) == 0 { Size::Word } else { Size::Long };
let offset = self.read_instruction_word()? as i16;
Ok(Instruction::MOVEP(dreg, areg, offset, size, dir))
@ -288,11 +286,19 @@ where
let size = if (ins & 0x0040) == 0 { Size::Word } else { Size::Long };
let data = self.read_instruction_word()?;
let target = self.decode_lower_effective_address(ins, None)?;
let dir = if (ins & 0x0400) == 0 { Direction::ToTarget } else { Direction::FromTarget };
let dir = if (ins & 0x0400) == 0 {
Direction::ToTarget
} else {
Direction::FromTarget
};
Ok(Instruction::MOVEM(target, size, dir, data))
} else if (ins & 0xF80) == 0xC00 && self.decoder.cputype >= M68kType::MC68020 {
let extension = self.read_instruction_word()?;
let reg_h = if (extension & 0x0400) != 0 { Some(get_low_reg(ins)) } else { None };
let reg_h = if (extension & 0x0400) != 0 {
Some(get_low_reg(ins))
} else {
None
};
let reg_l = ((extension & 0x7000) >> 12) as u8;
let target = self.decode_lower_effective_address(ins, Some(Size::Long))?;
let sign = if (ins & 0x0800) == 0 { Sign::Unsigned } else { Sign::Signed };
@ -303,30 +309,22 @@ where
} else if (ins & 0x800) == 0 {
let target = self.decode_lower_effective_address(ins, Some(Size::Word))?;
match (ins & 0x0700) >> 8 {
0b000 => {
match get_size(ins) {
Some(size) => Ok(Instruction::NEGX(target, size)),
None => Ok(Instruction::MOVEfromSR(target)),
}
0b000 => match get_size(ins) {
Some(size) => Ok(Instruction::NEGX(target, size)),
None => Ok(Instruction::MOVEfromSR(target)),
},
0b010 => {
match get_size(ins) {
Some(size) => Ok(Instruction::CLR(target, size)),
None if self.decoder.cputype >= M68kType::MC68010 => Ok(Instruction::MOVEfromCCR(target)),
None => Err(M68kError::Exception(Exceptions::IllegalInstruction)),
}
0b010 => match get_size(ins) {
Some(size) => Ok(Instruction::CLR(target, size)),
None if self.decoder.cputype >= M68kType::MC68010 => Ok(Instruction::MOVEfromCCR(target)),
None => Err(M68kError::Exception(Exceptions::IllegalInstruction)),
},
0b100 => {
match get_size(ins) {
Some(size) => Ok(Instruction::NEG(target, size)),
None => Ok(Instruction::MOVEtoCCR(target)),
}
0b100 => match get_size(ins) {
Some(size) => Ok(Instruction::NEG(target, size)),
None => Ok(Instruction::MOVEtoCCR(target)),
},
0b110 => {
match get_size(ins) {
Some(size) => Ok(Instruction::NOT(target, size)),
None => Ok(Instruction::MOVEtoSR(target)),
}
0b110 => match get_size(ins) {
Some(size) => Ok(Instruction::NOT(target, size)),
None => Ok(Instruction::MOVEtoSR(target)),
},
_ => Err(M68kError::Exception(Exceptions::IllegalInstruction)),
}
@ -342,25 +340,15 @@ where
let target = self.decode_lower_effective_address(ins, Some(Size::Byte))?;
Ok(Instruction::NBCD(target))
},
(0b001, 0b000) => {
Ok(Instruction::SWAP(get_low_reg(ins)))
},
(0b001, 0b001) => {
Ok(Instruction::BKPT(get_low_reg(ins)))
},
(0b001, 0b000) => Ok(Instruction::SWAP(get_low_reg(ins))),
(0b001, 0b001) => Ok(Instruction::BKPT(get_low_reg(ins))),
(0b001, _) => {
let target = self.decode_lower_effective_address(ins, None)?;
Ok(Instruction::PEA(target))
},
(0b010, 0b000) => {
Ok(Instruction::EXT(get_low_reg(ins), Size::Byte, Size::Word))
},
(0b011, 0b000) => {
Ok(Instruction::EXT(get_low_reg(ins), Size::Word, Size::Long))
},
(0b111, 0b000) => {
Ok(Instruction::EXT(get_low_reg(ins), Size::Byte, Size::Long))
},
(0b010, 0b000) => Ok(Instruction::EXT(get_low_reg(ins), Size::Byte, Size::Word)),
(0b011, 0b000) => Ok(Instruction::EXT(get_low_reg(ins), Size::Word, Size::Long)),
(0b111, 0b000) => Ok(Instruction::EXT(get_low_reg(ins), Size::Byte, Size::Long)),
_ => Err(M68kError::Exception(Exceptions::IllegalInstruction)),
}
} else if ins_0f00 == 0xA00 {
@ -393,7 +381,11 @@ where
}
} else if ins_00f0 == 0x60 {
let reg = get_low_reg(ins);
let dir = if (ins & 0b1000) == 0 { Direction::FromTarget } else { Direction::ToTarget };
let dir = if (ins & 0b1000) == 0 {
Direction::FromTarget
} else {
Direction::ToTarget
};
Ok(Instruction::MOVEUSP(Target::DirectAReg(reg), dir))
} else {
match ins & 0x00FF {
@ -412,7 +404,11 @@ where
0x76 => Ok(Instruction::TRAPV),
0x77 => Ok(Instruction::RTR),
0x7A | 0x7B if self.decoder.cputype >= M68kType::MC68010 => {
let dir = if ins & 0x01 == 0 { Direction::ToTarget } else { Direction::FromTarget };
let dir = if ins & 0x01 == 0 {
Direction::ToTarget
} else {
Direction::FromTarget
};
let ins2 = self.read_instruction_word()?;
let target = match ins2 & 0x8000 {
0 => Target::DirectDReg(((ins2 & 0x7000) >> 12) as u8),
@ -511,7 +507,11 @@ where
} else if let Some(size) = size {
let data_reg = Target::DirectDReg(get_high_reg(ins));
let effective_addr = self.decode_lower_effective_address(ins, Some(size))?;
let (from, to) = if (ins & 0x0100) == 0 { (effective_addr, data_reg) } else { (data_reg, effective_addr) };
let (from, to) = if (ins & 0x0100) == 0 {
(effective_addr, data_reg)
} else {
(data_reg, effective_addr)
};
Ok(Instruction::OR(from, to, size))
} else {
let sign = if (ins & 0x0100) == 0 { Sign::Unsigned } else { Sign::Signed };
@ -602,7 +602,11 @@ where
} else if let Some(size) = size {
let data_reg = Target::DirectDReg(get_high_reg(ins));
let effective_addr = self.decode_lower_effective_address(ins, Some(size))?;
let (from, to) = if (ins & 0x0100) == 0 { (effective_addr, data_reg) } else { (data_reg, effective_addr) };
let (from, to) = if (ins & 0x0100) == 0 {
(effective_addr, data_reg)
} else {
(data_reg, effective_addr)
};
Ok(Instruction::AND(from, to, size))
} else {
let sign = if (ins & 0x0100) == 0 { Sign::Unsigned } else { Sign::Signed };
@ -729,13 +733,17 @@ where
}
fn read_instruction_word(&mut self) -> Result<u16, M68kError<Bus::Error>> {
let word = self.memory.read_instruction_word(self.bus, self.decoder.is_supervisor, self.decoder.end)?;
let word = self
.memory
.read_instruction_word(self.bus, self.decoder.is_supervisor, self.decoder.end)?;
self.decoder.end += 2;
Ok(word)
}
fn read_instruction_long(&mut self) -> Result<u32, M68kError<Bus::Error>> {
let word = self.memory.read_instruction_long(self.bus, self.decoder.is_supervisor, self.decoder.end)?;
let word = self
.memory
.read_instruction_long(self.bus, self.decoder.is_supervisor, self.decoder.end)?;
self.decoder.end += 4;
Ok(word)
}
@ -769,11 +777,23 @@ where
// Decode Index Register
let xreg_num = ((brief_extension & 0x7000) >> 12) as u8;
let xreg = if (brief_extension & 0x8000) == 0 { XRegister::DReg(xreg_num) } else { XRegister::AReg(xreg_num) };
let size = if (brief_extension & 0x0800) == 0 { Size::Word } else { Size::Long };
let xreg = if (brief_extension & 0x8000) == 0 {
XRegister::DReg(xreg_num)
} else {
XRegister::AReg(xreg_num)
};
let size = if (brief_extension & 0x0800) == 0 {
Size::Word
} else {
Size::Long
};
if self.decoder.cputype <= M68kType::MC68010 {
let index_reg = IndexRegister { xreg, scale: 0, size };
let index_reg = IndexRegister {
xreg,
scale: 0,
size,
};
let displacement = sign_extend_to_long((brief_extension & 0x00FF) as u32, Size::Byte);
match areg {
@ -782,7 +802,11 @@ where
}
} else if use_brief {
let scale = ((brief_extension & 0x0600) >> 9) as u8;
let index_reg = IndexRegister { xreg, scale, size };
let index_reg = IndexRegister {
xreg,
scale,
size,
};
let displacement = sign_extend_to_long((brief_extension & 0x00FF) as u32, Size::Byte);
match areg {
@ -791,7 +815,11 @@ where
}
} else {
let scale = ((brief_extension & 0x0600) >> 9) as u8;
let index_reg = IndexRegister { xreg, scale, size };
let index_reg = IndexRegister {
xreg,
scale,
size,
};
let use_base_reg = (brief_extension & 0x0080) == 0;
let use_index = (brief_extension & 0x0040) == 0;
@ -826,43 +854,35 @@ where
let displacement = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word);
Target::IndirectRegOffset(BaseRegister::AReg(reg), None, displacement)
},
0b110 => {
self.decode_extension_word(Some(reg))?
},
0b111 => {
match reg {
0b000 => {
let value = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word) as u32;
Target::IndirectMemory(value, Size::Word)
},
0b001 => {
let value = self.read_instruction_long()?;
Target::IndirectMemory(value, Size::Long)
},
0b010 => {
let displacement = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word);
Target::IndirectRegOffset(BaseRegister::PC, None, displacement)
},
0b011 => {
self.decode_extension_word(None)?
},
0b100 => {
let data = match size {
Some(Size::Byte) | Some(Size::Word) => self.read_instruction_word()? as u32,
Some(Size::Long) => self.read_instruction_long()?,
None => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
};
Target::Immediate(data)
},
_ => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
}
0b110 => self.decode_extension_word(Some(reg))?,
0b111 => match reg {
0b000 => {
let value = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word) as u32;
Target::IndirectMemory(value, Size::Word)
},
0b001 => {
let value = self.read_instruction_long()?;
Target::IndirectMemory(value, Size::Long)
},
0b010 => {
let displacement = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word);
Target::IndirectRegOffset(BaseRegister::PC, None, displacement)
},
0b011 => self.decode_extension_word(None)?,
0b100 => {
let data = match size {
Some(Size::Byte) | Some(Size::Word) => self.read_instruction_word()? as u32,
Some(Size::Long) => self.read_instruction_long()?,
None => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
};
Target::Immediate(data)
},
_ => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
},
_ => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
};
Ok(value)
}
}
#[inline(always)]
@ -917,4 +937,3 @@ fn get_condition(ins: u16) -> Condition {
_ => Condition::True,
}
}

View File

@ -1,4 +1,3 @@
use femtos::Instant;
use emulator_hal::bus::{self, BusAccess};
use emulator_hal::step::Step;
@ -9,19 +8,8 @@ use crate::decode::M68kDecoder;
use crate::debugger::M68kDebugger;
use crate::timing::M68kInstructionTiming;
use crate::instructions::{
Register,
Size,
Sign,
Direction,
XRegister,
BaseRegister,
IndexRegister,
RegOrImmediate,
ControlRegister,
Condition,
Target,
Instruction,
sign_extend_to_long,
Register, Size, Sign, Direction, XRegister, BaseRegister, IndexRegister, RegOrImmediate, ControlRegister, Condition, Target,
Instruction, sign_extend_to_long,
};
@ -55,7 +43,7 @@ impl M68kCycle {
#[inline]
pub fn new(cpu: &M68k, clock: Instant) -> Self {
let is_supervisor = cpu.state.sr & (Flags:: Supervisor as u16) != 0;
let is_supervisor = cpu.state.sr & (Flags::Supervisor as u16) != 0;
Self {
decoder: M68kDecoder::new(cpu.info.chip, is_supervisor, cpu.state.pc),
timing: M68kInstructionTiming::new(cpu.info.chip, cpu.info.data_width as u8),
@ -174,7 +162,10 @@ where
}
#[inline]
pub fn check_pending_interrupts(&mut self, interrupt: (bool, u8, u8)) -> Result<(InterruptPriority, Option<u8>), M68kError<Bus::Error>> {
pub fn check_pending_interrupts(
&mut self,
interrupt: (bool, u8, u8),
) -> Result<(InterruptPriority, Option<u8>), M68kError<Bus::Error>> {
let ack_num;
(self.state.pending_ipl, ack_num) = match interrupt {
(true, priority, ack) => (InterruptPriority::from_u8(priority), ack),
@ -300,7 +291,9 @@ where
#[inline]
pub fn decode_next(&mut self) -> Result<(), M68kError<Bus::Error>> {
let is_supervisor = self.is_supervisor();
self.cycle.decoder.decode_at(&mut self.bus, &mut self.cycle.memory, is_supervisor, self.state.pc)?;
self.cycle
.decoder
.decode_at(&mut self.bus, &mut self.cycle.memory, is_supervisor, self.state.pc)?;
self.cycle.timing.add_instruction(&self.cycle.decoder.instruction);
@ -400,7 +393,9 @@ where
Instruction::UNLK(reg) => self.execute_unlk(reg),
Instruction::UnimplementedA(value) => self.execute_unimplemented_a(value),
Instruction::UnimplementedF(value) => self.execute_unimplemented_f(value),
_ => { return Err(M68kError::Other("Unsupported instruction".to_string())); },
_ => {
return Err(M68kError::Other("Unsupported instruction".to_string()));
},
}?;
Ok(())
@ -416,9 +411,13 @@ where
let binary_result = src_val.wrapping_add(dest_val).wrapping_add(extend_flag);
let mut result = src_parts.1.wrapping_add(dest_parts.1).wrapping_add(extend_flag);
if result > 0x09 { result = result.wrapping_add(0x06) };
if result > 0x09 {
result = result.wrapping_add(0x06)
};
result += src_parts.0 + dest_parts.0;
if result > 0x99 { result = result.wrapping_add(0x60) };
if result > 0x99 {
result = result.wrapping_add(0x60)
};
let carry = (result & 0xFFFFFF00) != 0;
self.set_target_value(dest, result, Size::Byte, Used::Twice)?;
@ -607,7 +606,12 @@ where
Ok(())
}
fn execute_bfchg(&mut self, target: Target, offset: RegOrImmediate, width: RegOrImmediate) -> Result<(), M68kError<Bus::Error>> {
fn execute_bfchg(
&mut self,
target: Target,
offset: RegOrImmediate,
width: RegOrImmediate,
) -> Result<(), M68kError<Bus::Error>> {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(target, Size::Long, Used::Twice)?;
@ -617,7 +621,12 @@ where
Ok(())
}
fn execute_bfclr(&mut self, target: Target, offset: RegOrImmediate, width: RegOrImmediate) -> Result<(), M68kError<Bus::Error>> {
fn execute_bfclr(
&mut self,
target: Target,
offset: RegOrImmediate,
width: RegOrImmediate,
) -> Result<(), M68kError<Bus::Error>> {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(target, Size::Long, Used::Twice)?;
@ -627,7 +636,13 @@ where
Ok(())
}
fn execute_bfexts(&mut self, target: Target, offset: RegOrImmediate, width: RegOrImmediate, reg: Register) -> Result<(), M68kError<Bus::Error>> {
fn execute_bfexts(
&mut self,
target: Target,
offset: RegOrImmediate,
width: RegOrImmediate,
reg: Register,
) -> Result<(), M68kError<Bus::Error>> {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(target, Size::Long, Used::Once)?;
@ -643,7 +658,13 @@ where
Ok(())
}
fn execute_bfextu(&mut self, target: Target, offset: RegOrImmediate, width: RegOrImmediate, reg: Register) -> Result<(), M68kError<Bus::Error>> {
fn execute_bfextu(
&mut self,
target: Target,
offset: RegOrImmediate,
width: RegOrImmediate,
reg: Register,
) -> Result<(), M68kError<Bus::Error>> {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(target, Size::Long, Used::Once)?;
@ -653,7 +674,12 @@ where
Ok(())
}
fn execute_bfset(&mut self, target: Target, offset: RegOrImmediate, width: RegOrImmediate) -> Result<(), M68kError<Bus::Error>> {
fn execute_bfset(
&mut self,
target: Target,
offset: RegOrImmediate,
width: RegOrImmediate,
) -> Result<(), M68kError<Bus::Error>> {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(target, Size::Long, Used::Twice)?;
@ -663,7 +689,12 @@ where
Ok(())
}
fn execute_bftst(&mut self, target: Target, offset: RegOrImmediate, width: RegOrImmediate) -> Result<(), M68kError<Bus::Error>> {
fn execute_bftst(
&mut self,
target: Target,
offset: RegOrImmediate,
width: RegOrImmediate,
) -> Result<(), M68kError<Bus::Error>> {
let (offset, width) = self.get_bit_field_args(offset, width);
let mask = get_bit_field_mask(offset, width);
let value = self.get_target_value(target, Size::Long, Used::Once)?;
@ -749,16 +780,12 @@ where
(
(dest_val % src_val) as u32,
quotient as u32,
quotient > i16::MAX as i32 || quotient < i16::MIN as i32
quotient > i16::MAX as i32 || quotient < i16::MIN as i32,
)
},
Sign::Unsigned => {
let quotient = dest_val / src_val;
(
dest_val % src_val,
quotient,
(quotient & 0xFFFF0000) != 0
)
(dest_val % src_val, quotient, (quotient & 0xFFFF0000) != 0)
},
};
@ -773,7 +800,13 @@ where
Ok(())
}
fn execute_divl(&mut self, src: Target, dest_h: Option<Register>, dest_l: Register, sign: Sign) -> Result<(), M68kError<Bus::Error>> {
fn execute_divl(
&mut self,
src: Target,
dest_h: Option<Register>,
dest_l: Register,
sign: Sign,
) -> Result<(), M68kError<Bus::Error>> {
let src_val = self.get_target_value(src, Size::Long, Used::Once)?;
if src_val == 0 {
self.exception(Exceptions::ZeroDivide as u8, false)?;
@ -988,8 +1021,8 @@ where
Target::IndirectARegInc(reg) | Target::IndirectARegDec(reg) => {
let a_reg_mut = self.get_a_reg_mut(reg);
*a_reg_mut = addr + (mask.count_ones() * size.in_bytes());
}
_ => { },
},
_ => {},
}
}
@ -1006,11 +1039,9 @@ where
}
self.move_registers_to_memory_reverse(addr, size, mask)?
},
_ => {
match dir {
Direction::ToTarget => self.move_registers_to_memory(addr, size, mask)?,
Direction::FromTarget => self.move_memory_to_registers(addr, size, mask)?,
}
_ => match dir {
Direction::ToTarget => self.move_registers_to_memory(addr, size, mask)?,
Direction::FromTarget => self.move_memory_to_registers(addr, size, mask)?,
},
};
@ -1019,8 +1050,8 @@ where
Target::IndirectARegInc(reg) | Target::IndirectARegDec(reg) => {
let a_reg_mut = self.get_a_reg_mut(reg);
*a_reg_mut = post_addr;
}
_ => { },
},
_ => {},
}
Ok(())
@ -1082,7 +1113,14 @@ where
Ok(addr)
}
fn execute_movep(&mut self, dreg: Register, areg: Register, offset: i16, size: Size, dir: Direction) -> Result<(), M68kError<Bus::Error>> {
fn execute_movep(
&mut self,
dreg: Register,
areg: Register,
offset: i16,
size: Size,
dir: Direction,
) -> Result<(), M68kError<Bus::Error>> {
match dir {
Direction::ToTarget => {
let mut shift = (size.in_bits() as i32) - 8;
@ -1119,7 +1157,9 @@ where
self.require_supervisor()?;
match dir {
Direction::ToTarget => self.set_target_value(target, self.state.usp, Size::Long, Used::Once)?,
Direction::FromTarget => { self.state.usp = self.get_target_value(target, Size::Long, Used::Once)?; },
Direction::FromTarget => {
self.state.usp = self.get_target_value(target, Size::Long, Used::Once)?;
},
}
Ok(())
}
@ -1137,7 +1177,13 @@ where
Ok(())
}
fn execute_mull(&mut self, src: Target, dest_h: Option<Register>, dest_l: Register, sign: Sign) -> Result<(), M68kError<Bus::Error>> {
fn execute_mull(
&mut self,
src: Target,
dest_h: Option<Register>,
dest_l: Register,
sign: Sign,
) -> Result<(), M68kError<Bus::Error>> {
let src_val = self.get_target_value(src, Size::Long, Used::Once)?;
let dest_val = get_value_sized(self.state.d_reg[dest_l as usize], Size::Long);
let result = match sign {
@ -1353,10 +1399,14 @@ where
let binary_result = dest_val.wrapping_sub(src_val).wrapping_sub(extend_flag);
let mut result = dest_parts.1.wrapping_sub(src_parts.1).wrapping_sub(extend_flag);
if (result & 0x1F) > 0x09 { result -= 0x06 };
if (result & 0x1F) > 0x09 {
result -= 0x06
};
result = result.wrapping_add(dest_parts.0.wrapping_sub(src_parts.0));
let carry = (result & 0x1FF) > 0x99;
if carry { result -= 0x60 };
if carry {
result -= 0x60
};
self.set_flag(Flags::Negative, get_msb(result, Size::Byte));
self.set_flag(Flags::Zero, (result & 0xFF) == 0);
@ -1489,7 +1539,8 @@ where
Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => {
let base_value = self.get_base_reg_value(base_reg);
let index_value = self.get_index_reg_value(&index_reg);
let intermediate = self.get_address_sized(base_value.wrapping_add(base_disp as u32).wrapping_add(index_value as u32), Size::Long)?;
let intermediate =
self.get_address_sized(base_value.wrapping_add(base_disp as u32).wrapping_add(index_value as u32), Size::Long)?;
self.get_address_sized(intermediate.wrapping_add(outer_disp as u32), size)
},
Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => {
@ -1498,13 +1549,17 @@ where
let intermediate = self.get_address_sized(base_value.wrapping_add(base_disp as u32), Size::Long)?;
self.get_address_sized(intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32), size)
},
Target::IndirectMemory(addr, _) => {
self.get_address_sized(addr, size)
},
Target::IndirectMemory(addr, _) => self.get_address_sized(addr, size),
}
}
pub(super) fn set_target_value(&mut self, target: Target, value: u32, size: Size, used: Used) -> Result<(), M68kError<Bus::Error>> {
pub(super) fn set_target_value(
&mut self,
target: Target,
value: u32,
size: Size,
used: Used,
) -> Result<(), M68kError<Bus::Error>> {
match target {
Target::DirectDReg(reg) => {
set_value_sized(&mut self.state.d_reg[reg as usize], value, size);
@ -1532,7 +1587,8 @@ where
Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => {
let base_value = self.get_base_reg_value(base_reg);
let index_value = self.get_index_reg_value(&index_reg);
let intermediate = self.get_address_sized(base_value.wrapping_add(base_disp as u32).wrapping_add(index_value as u32), Size::Long)?;
let intermediate =
self.get_address_sized(base_value.wrapping_add(base_disp as u32).wrapping_add(index_value as u32), Size::Long)?;
self.set_address_sized(intermediate.wrapping_add(outer_disp as u32), value, size)?;
},
Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => {
@ -1560,7 +1616,8 @@ where
Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => {
let base_value = self.get_base_reg_value(base_reg);
let index_value = self.get_index_reg_value(&index_reg);
let intermediate = self.get_address_sized(base_value.wrapping_add(base_disp as u32).wrapping_add(index_value as u32), Size::Long)?;
let intermediate =
self.get_address_sized(base_value.wrapping_add(base_disp as u32).wrapping_add(index_value as u32), Size::Long)?;
intermediate.wrapping_add(outer_disp as u32)
},
Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => {
@ -1569,9 +1626,7 @@ where
let intermediate = self.get_address_sized(base_value.wrapping_add(base_disp as u32), Size::Long)?;
intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32)
},
Target::IndirectMemory(addr, _) => {
addr
},
Target::IndirectMemory(addr, _) => addr,
_ => return Err(M68kError::InvalidTarget(target)),
};
Ok(addr)
@ -1611,21 +1666,28 @@ where
fn set_address_sized(&mut self, addr: M68kAddress, value: u32, size: Size) -> Result<(), M68kError<Bus::Error>> {
let is_supervisor = self.is_supervisor();
self.cycle.memory.write_data_sized(&mut self.bus, is_supervisor, addr, size, value)
self.cycle
.memory
.write_data_sized(&mut self.bus, is_supervisor, addr, size, value)
}
fn push_word(&mut self, value: u16) -> Result<(), M68kError<Bus::Error>> {
let is_supervisor = self.is_supervisor();
*self.get_stack_pointer_mut() -= 2;
let addr = *self.get_stack_pointer_mut();
self.cycle.memory.write_data_sized(&mut self.bus, is_supervisor, addr, Size::Word, value as u32)?;
self.cycle
.memory
.write_data_sized(&mut self.bus, is_supervisor, addr, Size::Word, value as u32)?;
Ok(())
}
fn pop_word(&mut self) -> Result<u16, M68kError<Bus::Error>> {
let is_supervisor = self.is_supervisor();
let addr = *self.get_stack_pointer_mut();
let value = self.cycle.memory.read_data_sized(&mut self.bus, is_supervisor, addr, Size::Word)?;
let value = self
.cycle
.memory
.read_data_sized(&mut self.bus, is_supervisor, addr, Size::Word)?;
*self.get_stack_pointer_mut() += 2;
Ok(value as u16)
}
@ -1634,21 +1696,33 @@ where
let is_supervisor = self.is_supervisor();
*self.get_stack_pointer_mut() -= 4;
let addr = *self.get_stack_pointer_mut();
self.cycle.memory.write_data_sized(&mut self.bus, is_supervisor, addr, Size::Long, value)?;
self.cycle
.memory
.write_data_sized(&mut self.bus, is_supervisor, addr, Size::Long, value)?;
Ok(())
}
fn pop_long(&mut self) -> Result<u32, M68kError<Bus::Error>> {
let is_supervisor = self.is_supervisor();
let addr = *self.get_stack_pointer_mut();
let value = self.cycle.memory.read_data_sized(&mut self.bus, is_supervisor, addr, Size::Long)?;
let value = self
.cycle
.memory
.read_data_sized(&mut self.bus, is_supervisor, addr, Size::Long)?;
*self.get_stack_pointer_mut() += 4;
Ok(value)
}
fn set_pc(&mut self, value: u32) -> Result<(), M68kError<Bus::Error>> {
self.state.pc = value;
self.cycle.memory.start_request(self.is_supervisor(), self.state.pc, Size::Word, MemAccess::Read, MemType::Program, true)?;
self.cycle.memory.start_request(
self.is_supervisor(),
self.state.pc,
Size::Word,
MemAccess::Read,
MemType::Program,
true,
)?;
Ok(())
}
@ -1679,7 +1753,13 @@ where
match base_reg {
BaseRegister::None => 0,
BaseRegister::PC => self.cycle.decoder.start + 2,
BaseRegister::AReg(7) => if self.is_supervisor() { self.state.ssp } else { self.state.usp },
BaseRegister::AReg(7) => {
if self.is_supervisor() {
self.state.ssp
} else {
self.state.usp
}
},
BaseRegister::AReg(reg) => self.state.a_reg[reg as usize],
}
}
@ -1687,9 +1767,11 @@ where
fn get_index_reg_value(&self, index_reg: &Option<IndexRegister>) -> i32 {
match index_reg {
None => 0,
Some(IndexRegister { xreg, scale, size }) => {
sign_extend_to_long(self.get_x_reg_value(*xreg), *size) << scale
}
Some(IndexRegister {
xreg,
scale,
size,
}) => sign_extend_to_long(self.get_x_reg_value(*xreg), *size) << scale,
}
}
@ -1700,12 +1782,20 @@ where
}
fn get_stack_pointer_mut(&mut self) -> &mut u32 {
if self.is_supervisor() { &mut self.state.ssp } else { &mut self.state.usp }
if self.is_supervisor() {
&mut self.state.ssp
} else {
&mut self.state.usp
}
}
fn get_a_reg(&self, reg: Register) -> u32 {
if reg == 7 {
if self.is_supervisor() { self.state.ssp } else { self.state.usp }
if self.is_supervisor() {
self.state.ssp
} else {
self.state.usp
}
} else {
self.state.a_reg[reg as usize]
}
@ -1713,14 +1803,18 @@ where
fn get_a_reg_mut(&mut self, reg: Register) -> &mut u32 {
if reg == 7 {
if self.is_supervisor() { &mut self.state.ssp } else { &mut self.state.usp }
if self.is_supervisor() {
&mut self.state.ssp
} else {
&mut self.state.usp
}
} else {
&mut self.state.a_reg[reg as usize]
}
}
fn is_supervisor(&self) -> bool {
self.state.sr & (Flags:: Supervisor as u16) != 0
self.state.sr & (Flags::Supervisor as u16) != 0
}
fn require_supervisor(&self) -> Result<(), M68kError<Bus::Error>> {
@ -1732,7 +1826,11 @@ where
}
fn set_sr(&mut self, value: u16) {
let mask = if self.cycle.decoder.cputype <= M68kType::MC68010 { 0xA71F } else { 0xF71F };
let mask = if self.cycle.decoder.cputype <= M68kType::MC68010 {
0xA71F
} else {
0xF71F
};
self.state.sr = value & mask;
}
@ -1805,15 +1903,23 @@ where
Condition::OverflowSet => self.get_flag(Flags::Overflow),
Condition::Plus => !self.get_flag(Flags::Negative),
Condition::Minus => self.get_flag(Flags::Negative),
Condition::GreaterThanOrEqual => (self.get_flag(Flags::Negative) && self.get_flag(Flags::Overflow)) || (!self.get_flag(Flags::Negative) && !self.get_flag(Flags::Overflow)),
Condition::LessThan => (self.get_flag(Flags::Negative) && !self.get_flag(Flags::Overflow)) || (!self.get_flag(Flags::Negative) && self.get_flag(Flags::Overflow)),
Condition::GreaterThan =>
Condition::GreaterThanOrEqual => {
(self.get_flag(Flags::Negative) && self.get_flag(Flags::Overflow))
|| (!self.get_flag(Flags::Negative) && !self.get_flag(Flags::Overflow))
},
Condition::LessThan => {
(self.get_flag(Flags::Negative) && !self.get_flag(Flags::Overflow))
|| (!self.get_flag(Flags::Negative) && self.get_flag(Flags::Overflow))
},
Condition::GreaterThan => {
(self.get_flag(Flags::Negative) && self.get_flag(Flags::Overflow) && !self.get_flag(Flags::Zero))
|| (!self.get_flag(Flags::Negative) && !self.get_flag(Flags::Overflow) && !self.get_flag(Flags::Zero)),
Condition::LessThanOrEqual =>
|| (!self.get_flag(Flags::Negative) && !self.get_flag(Flags::Overflow) && !self.get_flag(Flags::Zero))
},
Condition::LessThanOrEqual => {
self.get_flag(Flags::Zero)
|| (self.get_flag(Flags::Negative) && !self.get_flag(Flags::Overflow))
|| (!self.get_flag(Flags::Negative) && self.get_flag(Flags::Overflow)),
|| (self.get_flag(Flags::Negative) && !self.get_flag(Flags::Overflow))
|| (!self.get_flag(Flags::Negative) && self.get_flag(Flags::Overflow))
},
}
}
}
@ -1889,7 +1995,11 @@ fn rotate_left(value: u32, size: Size, use_extend: Option<bool>) -> (u32, bool)
fn rotate_right(value: u32, size: Size, use_extend: Option<bool>) -> (u32, bool) {
let bit = (value & 0x01) != 0;
let mask = if use_extend.unwrap_or(bit) { get_msb_mask(0xffffffff, size) } else { 0x0 };
let mask = if use_extend.unwrap_or(bit) {
get_msb_mask(0xffffffff, size)
} else {
0x0
};
((value >> 1) | mask, bit)
}
@ -1899,17 +2009,23 @@ fn get_nibbles_from_byte(value: u32) -> (u32, u32) {
fn get_value_sized(value: u32, size: Size) -> u32 {
match size {
Size::Byte => { 0x000000FF & value },
Size::Word => { 0x0000FFFF & value },
Size::Long => { value },
Size::Byte => 0x000000FF & value,
Size::Word => 0x0000FFFF & value,
Size::Long => value,
}
}
fn set_value_sized(addr: &mut u32, value: u32, size: Size) {
match size {
Size::Byte => { *addr = (*addr & 0xFFFFFF00) | (0x000000FF & value); }
Size::Word => { *addr = (*addr & 0xFFFF0000) | (0x0000FFFF & value); }
Size::Long => { *addr = value; }
Size::Byte => {
*addr = (*addr & 0xFFFFFF00) | (0x000000FF & value);
},
Size::Word => {
*addr = (*addr & 0xFFFF0000) | (0x0000FFFF & value);
},
Size::Long => {
*addr = value;
},
}
}
@ -1956,5 +2072,3 @@ fn get_bit_field_mask(offset: u32, width: u32) -> u32 {
fn get_bit_field_msb(offset: u32) -> u32 {
0x80000000 >> offset
}

View File

@ -1,4 +1,3 @@
use std::fmt::{self, Write};
@ -336,10 +335,12 @@ impl fmt::Display for Target {
let index_str = fmt_index_disp(index_reg);
write!(f, "([{}#{:08x}]{} + #{:08x})", base_reg, base_disp, index_str, outer_disp)
},
Target::IndirectMemory(value, size) => if *size == Size::Word {
write!(f, "(#{:04x})", value)
} else {
write!(f, "(#{:08x})", value)
Target::IndirectMemory(value, size) => {
if *size == Size::Word {
write!(f, "(#{:04x})", value)
} else {
write!(f, "(#{:08x})", value)
}
},
}
}
@ -432,7 +433,17 @@ impl fmt::Display for Instruction {
Instruction::EORtoCCR(value) => write!(f, "eorib\t#{:#02x}, %ccr", value),
Instruction::EORtoSR(value) => write!(f, "eoriw\t#{:#04x}, %sr", value),
Instruction::EXG(src, dest) => write!(f, "exg\t{}, {}", src, dest),
Instruction::EXT(reg, from_size, to_size) => write!(f, "ext{}{}\t%d{}", if *from_size == Size::Byte && *to_size == Size::Long { "b" } else { "" }, to_size, reg),
Instruction::EXT(reg, from_size, to_size) => write!(
f,
"ext{}{}\t%d{}",
if *from_size == Size::Byte && *to_size == Size::Long {
"b"
} else {
""
},
to_size,
reg
),
Instruction::ILLEGAL => write!(f, "illegal"),
@ -517,4 +528,3 @@ impl fmt::Display for Instruction {
}
}
}

View File

@ -1,17 +1,15 @@
pub mod assembler;
pub mod state;
pub mod debugger;
pub mod decode;
pub mod execute;
pub mod debugger;
pub mod instructions;
pub mod memory;
pub mod timing;
pub mod state;
pub mod tests;
pub mod timing;
#[cfg(feature = "moa")]
pub mod moa;
pub use crate::state::{M68k, M68kType, M68kError};
pub use crate::memory::{M68kAddress, M68kAddressSpace};

View File

@ -1,4 +1,3 @@
use core::cmp;
use core::fmt::Write;
use femtos::Instant;
@ -10,6 +9,7 @@ use crate::instructions::Size;
#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[rustfmt::skip]
pub enum FunctionCode {
Reserved0 = 0,
UserData = 1,
@ -148,7 +148,13 @@ impl M68kBusPort {
}
}
fn read<Bus, BusError>(&mut self, bus: &mut Bus, clock: Instant, addr: M68kAddress, data: &mut [u8]) -> Result<(), M68kError<BusError>>
fn read<Bus, BusError>(
&mut self,
bus: &mut Bus,
clock: Instant,
addr: M68kAddress,
data: &mut [u8],
) -> Result<(), M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
@ -162,7 +168,13 @@ impl M68kBusPort {
Ok(())
}
fn write<Bus, BusError>(&mut self, bus: &mut Bus, clock: Instant, addr: M68kAddress, data: &[u8]) -> Result<(), M68kError<BusError>>
fn write<Bus, BusError>(
&mut self,
bus: &mut Bus,
clock: Instant,
addr: M68kAddress,
data: &[u8],
) -> Result<(), M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
@ -185,10 +197,17 @@ impl M68kBusPort {
Size::Byte => self.read(bus, self.current_clock, addr, &mut data[3..4]),
Size::Word => self.read(bus, self.current_clock, addr, &mut data[2..4]),
Size::Long => self.read(bus, self.current_clock, addr, &mut data[0..4]),
}.map(|_| u32::from_be_bytes(data))
}
.map(|_| u32::from_be_bytes(data))
}
fn write_sized<Bus, BusError>(&mut self, bus: &mut Bus, addr: M68kAddress, size: Size, value: u32) -> Result<(), M68kError<BusError>>
fn write_sized<Bus, BusError>(
&mut self,
bus: &mut Bus,
addr: M68kAddress,
size: Size,
value: u32,
) -> Result<(), M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
@ -200,7 +219,13 @@ impl M68kBusPort {
}
}
pub(crate) fn read_data_sized<Bus, BusError>(&mut self, bus: &mut Bus, is_supervisor: bool, addr: M68kAddress, size: Size) -> Result<u32, M68kError<BusError>>
pub(crate) fn read_data_sized<Bus, BusError>(
&mut self,
bus: &mut Bus,
is_supervisor: bool,
addr: M68kAddress,
size: Size,
) -> Result<u32, M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
@ -208,7 +233,14 @@ impl M68kBusPort {
self.read_sized(bus, addr, size)
}
pub(crate) fn write_data_sized<Bus, BusError>(&mut self, bus: &mut Bus, is_supervisor: bool, addr: M68kAddress, size: Size, value: u32) -> Result<(), M68kError<BusError>>
pub(crate) fn write_data_sized<Bus, BusError>(
&mut self,
bus: &mut Bus,
is_supervisor: bool,
addr: M68kAddress,
size: Size,
value: u32,
) -> Result<(), M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
@ -216,7 +248,12 @@ impl M68kBusPort {
self.write_sized(bus, addr, size, value)
}
pub(crate) fn read_instruction_word<Bus, BusError>(&mut self, bus: &mut Bus, is_supervisor: bool, addr: u32) -> Result<u16, M68kError<BusError>>
pub(crate) fn read_instruction_word<Bus, BusError>(
&mut self,
bus: &mut Bus,
is_supervisor: bool,
addr: u32,
) -> Result<u16, M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
@ -224,7 +261,12 @@ impl M68kBusPort {
Ok(self.read_sized(bus, addr, Size::Word)? as u16)
}
pub(crate) fn read_instruction_long<Bus, BusError>(&mut self, bus: &mut Bus, is_supervisor: bool, addr: u32) -> Result<u32, M68kError<BusError>>
pub(crate) fn read_instruction_long<Bus, BusError>(
&mut self,
bus: &mut Bus,
is_supervisor: bool,
addr: u32,
) -> Result<u32, M68kError<BusError>>
where
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
{
@ -232,7 +274,15 @@ impl M68kBusPort {
self.read_sized(bus, addr, Size::Long)
}
pub(crate) fn start_request<BusError>(&mut self, is_supervisor: bool, addr: u32, size: Size, access: MemAccess, mtype: MemType, i_n_bit: bool) -> Result<u32, M68kError<BusError>> {
pub(crate) fn start_request<BusError>(
&mut self,
is_supervisor: bool,
addr: u32,
size: Size,
access: MemAccess,
mtype: MemType,
i_n_bit: bool,
) -> Result<u32, M68kError<BusError>> {
self.request.i_n_bit = i_n_bit;
self.request.code = match mtype {
MemType::Program => FunctionCode::program(is_supervisor),
@ -314,7 +364,7 @@ impl TargetAccess {
}
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
}
}
@ -357,7 +407,7 @@ impl ReadOnceAccess {
}
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
}
}
@ -376,7 +426,7 @@ impl ReadUpdateAccess {
}
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
}
}
@ -391,7 +441,7 @@ impl WriteOnceAccess {
}
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
}
}
*/

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Duration};
use emulator_hal::bus;
@ -13,11 +12,8 @@ impl Steppable for M68k {
let cycle = M68kCycle::new(self, system.clock);
let mut bus = system.bus.borrow_mut();
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> = bus::BusAdapter::new(
&mut *bus,
|addr| addr as u64,
|err| err,
);
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> =
bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err);
let mut executor = cycle.begin(self, &mut adapter);
executor.check_breakpoints()?;
@ -40,7 +36,7 @@ impl Steppable for M68k {
}
}
impl Interruptable for M68k { }
impl Interruptable for M68k {}
impl Transmutable for M68k {
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
@ -104,11 +100,8 @@ impl Debuggable for M68k {
let mut decoder = M68kDecoder::new(self.info.chip, true, 0);
let mut bus = system.bus.borrow_mut();
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> = bus::BusAdapter::new(
&mut *bus,
|addr| addr as u64,
|err| err,
);
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> =
bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err);
decoder.dump_disassembly(&mut adapter, addr as u32, count as u32);
}
@ -124,9 +117,10 @@ impl Debuggable for M68k {
"so" | "stepout" => {
self.debugger.step_until_return = Some(self.debugger.stack_tracer.calls.len() - 1);
},
_ => { return Ok(true); },
_ => {
return Ok(true);
},
}
Ok(false)
}
}

View File

@ -1,4 +1,3 @@
use core::fmt::{self, Write};
use femtos::{Duration, Frequency};
@ -14,10 +13,10 @@ pub type ClockCycles = u16;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum AddressWidth {
A32 = 32, // MC68020+
A24 = 24, // MC68000 64-Pin, MC68010
A22 = 22, // MC68008 52-Pin
A20 = 20, // MC68008 48-Pin
A32 = 32, // MC68020+
A24 = 24, // MC68000 64-Pin, MC68010
A22 = 22, // MC68008 52-Pin
A20 = 20, // MC68008 48-Pin
}
#[allow(dead_code)]
@ -99,7 +98,7 @@ impl CpuInfo {
address_width: AddressWidth::A32,
data_width: DataWidth::D32,
frequency,
}
},
}
}
}
@ -109,6 +108,7 @@ const FLAGS_ON_RESET: u16 = 0x2700;
#[repr(u16)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[rustfmt::skip]
pub enum Flags {
Carry = 0x0001,
Overflow = 0x0002,
@ -124,6 +124,7 @@ pub enum Flags {
#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[rustfmt::skip]
pub enum Exceptions {
BusError = 2,
AddressError = 3,
@ -298,4 +299,3 @@ impl InterruptPriority {
}
}
}

View File

@ -1,4 +1,3 @@
#[cfg(test)]
mod decode_unit_tests {
use femtos::Instant;
@ -96,7 +95,10 @@ mod decode_unit_tests {
let size = Size::Long;
let offset = -8;
decoder.bus.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap();
decoder
.bus
.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16)
.unwrap();
let target = decoder.get_mode_as_target(0b101, 0b100, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(4), None, offset));
@ -111,10 +113,24 @@ mod decode_unit_tests {
let brief_extension = 0x3800 | (((offset as i8) as u8) as u16);
decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
decoder.bus.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap();
decoder
.bus
.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16)
.unwrap();
let target = decoder.get_mode_as_target(0b110, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), Some(IndexRegister { xreg: XRegister::DReg(3), scale: 0, size: size }), offset));
assert_eq!(
target,
Target::IndirectRegOffset(
BaseRegister::AReg(2),
Some(IndexRegister {
xreg: XRegister::DReg(3),
scale: 0,
size: size
}),
offset
)
);
});
}
@ -129,7 +145,18 @@ mod decode_unit_tests {
decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
let target = decoder.get_mode_as_target(0b110, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(2), Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
assert_eq!(
target,
Target::IndirectRegOffset(
BaseRegister::AReg(2),
Some(IndexRegister {
xreg: XRegister::AReg(7),
scale: 1,
size: size
}),
offset
)
);
});
}
@ -144,7 +171,18 @@ mod decode_unit_tests {
decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
let target = decoder.get_mode_as_target(0b110, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::None, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
assert_eq!(
target,
Target::IndirectRegOffset(
BaseRegister::None,
Some(IndexRegister {
xreg: XRegister::AReg(7),
scale: 1,
size: size
}),
offset
)
);
});
}
@ -169,7 +207,10 @@ mod decode_unit_tests {
let size = Size::Long;
let offset = -8;
decoder.bus.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16).unwrap();
decoder
.bus
.write_beu16(Instant::START, INIT_ADDR, (offset as i16) as u16)
.unwrap();
let target = decoder.get_mode_as_target(0b111, 0b010, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, None, offset));
@ -184,10 +225,24 @@ mod decode_unit_tests {
let brief_extension = 0x3000 | (((offset as i8) as u8) as u16);
decoder.bus.write_beu16(Instant::START, INIT_ADDR, brief_extension).unwrap();
decoder.bus.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16).unwrap();
decoder
.bus
.write_beu16(Instant::START, INIT_ADDR + 2, (offset as i16) as u16)
.unwrap();
let target = decoder.get_mode_as_target(0b111, 0b011, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::DReg(3), scale: 0, size: size }), offset));
assert_eq!(
target,
Target::IndirectRegOffset(
BaseRegister::PC,
Some(IndexRegister {
xreg: XRegister::DReg(3),
scale: 0,
size: size
}),
offset
)
);
});
}
@ -202,7 +257,18 @@ mod decode_unit_tests {
decoder.bus.write_beu32(Instant::START, INIT_ADDR + 2, offset as u32).unwrap();
let target = decoder.get_mode_as_target(0b111, 0b011, Some(size)).unwrap();
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, Some(IndexRegister { xreg: XRegister::AReg(7), scale: 1, size: size }), offset));
assert_eq!(
target,
Target::IndirectRegOffset(
BaseRegister::PC,
Some(IndexRegister {
xreg: XRegister::AReg(7),
scale: 1,
size: size
}),
offset
)
);
});
}
@ -269,7 +335,9 @@ mod execute_unit_tests {
// Insert basic initialization
let len = 0x10_0000;
let mut data = Vec::with_capacity(len);
unsafe { data.set_len(len); }
unsafe {
data.set_len(len);
}
let mut memory = MemoryBlock::from(data);
memory.write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();

View File

@ -1,4 +1,3 @@
use crate::M68kType;
use crate::state::ClockCycles;
use crate::instructions::{Size, Sign, Direction, Target, Instruction};
@ -69,8 +68,7 @@ impl M68kInstructionTiming {
#[inline(always)]
pub fn add_reg_v_mem(&mut self, target: &Target, reg: u8, mem: u8) -> &mut Self {
self.internal += match target {
Target::DirectDReg(_) |
Target::DirectAReg(_) => reg,
Target::DirectDReg(_) | Target::DirectAReg(_) => reg,
_ => mem,
};
self
@ -95,7 +93,7 @@ impl M68kInstructionTiming {
pub fn add_immediate_set(&mut self, size: Size, target: &Target, reg: (u8, u8), mem: (u8, u8)) -> &mut Self {
match target {
Target::DirectDReg(_) | Target::DirectAReg(_) => self.add_word_v_long(size, reg.0, reg.1),
_ => self.add_word_v_long(size, mem.0, mem.1)
_ => self.add_word_v_long(size, mem.0, mem.1),
}
}
@ -115,7 +113,7 @@ impl M68kInstructionTiming {
pub fn add_reg_mem_set(&mut self, size: Size, target: &Target, reg: (u8, u8), mem: (u8, u8)) -> &mut Self {
match target {
Target::DirectDReg(_) | Target::DirectAReg(_) => self.add_word_v_long(size, reg.0, reg.1),
_ => self.add_word_v_long(size, mem.0, mem.1)
_ => self.add_word_v_long(size, mem.0, mem.1),
}
}
@ -139,14 +137,11 @@ impl M68kInstructionTiming {
Target::IndirectAReg(_) => self.add_access(size),
Target::IndirectARegInc(_) => self.add_access(size),
Target::IndirectARegDec(_) => self.add_access(size).add_internal(2),
Target::IndirectRegOffset(_, index_reg, _) => {
match index_reg {
None => self.add_access(size).add_internal(4),
Some(_) => self.add_access(size).add_internal(6),
}
Target::IndirectRegOffset(_, index_reg, _) => match index_reg {
None => self.add_access(size).add_internal(4),
Some(_) => self.add_access(size).add_internal(6),
},
Target::IndirectMemoryPreindexed(_, index_reg, _, _) |
Target::IndirectMemoryPostindexed(_, index_reg, _, _) => {
Target::IndirectMemoryPreindexed(_, index_reg, _, _) | Target::IndirectMemoryPostindexed(_, index_reg, _, _) => {
// TODO this is very wrong, but the 68020 timings are complicated
match index_reg {
None => self.add_access(size).add_internal(4),
@ -159,7 +154,9 @@ impl M68kInstructionTiming {
pub fn add_two_targets(&mut self, size: Size, src: &Target, dest: &Target) -> &mut Self {
match (src, dest) {
(Target::IndirectARegDec(_), Target::IndirectARegDec(_)) => self.add_target(size, src).add_target(size, dest).sub_internal(2),
(Target::IndirectARegDec(_), Target::IndirectARegDec(_)) => {
self.add_target(size, src).add_target(size, dest).sub_internal(2)
},
_ => self.add_target(size, src).add_target(size, dest),
}
}
@ -199,20 +196,31 @@ impl M68kInstructionTiming {
match instruction {
Instruction::ABCD(_, dest) => self.add_reg_v_mem(dest, 6, 18),
Instruction::ADD(Target::Immediate(x), dest, size) if *x <= 8 => self.add_immediate_set(*size, dest, (4, 8), (8, 12)).add_target(*size, dest),// ADDQ
Instruction::ADD(Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest), // ADDI
Instruction::ADD(Target::Immediate(x), dest, size) if *x <= 8 => {
self.add_immediate_set(*size, dest, (4, 8), (8, 12)).add_target(*size, dest)
}, // ADDQ
Instruction::ADD(Target::Immediate(_), dest, size) => {
self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest)
}, // ADDI
Instruction::ADD(src, dest, size) => self.add_standard_set(*size, dest, (8, 6), (4, 6), (8, 12)).add_two_targets(*size, src, dest),
Instruction::ADD(src, dest, size) => self
.add_standard_set(*size, dest, (8, 6), (4, 6), (8, 12))
.add_two_targets(*size, src, dest),
Instruction::ADDA(target, _, size) => self.add_word_v_long(*size, 8, 6).add_target(*size, target),
Instruction::ADDX(_, dest, size) => self.add_reg_mem_set(*size, dest, (4, 8), (18, 30)),
Instruction::AND(Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 14), (12, 20)).add_target(*size, dest),
Instruction::AND(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12)).add_two_targets(*size, src, dest),
Instruction::AND(Target::Immediate(_), dest, size) => {
self.add_immediate_set(*size, dest, (8, 14), (12, 20)).add_target(*size, dest)
},
Instruction::AND(src, dest, size) => self
.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12))
.add_two_targets(*size, src, dest),
Instruction::ANDtoCCR(_) => self.add_internal(20),
Instruction::ANDtoSR(_) => self.add_internal(20),
Instruction::ASL(_, target, size) |
Instruction::ASR(_, target, size) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target),
Instruction::ASL(_, target, size) | Instruction::ASR(_, target, size) => {
self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target)
},
Instruction::Bcc(_, _) => self.add_internal(8).add_on_branch(2),
Instruction::BRA(_) => self.add_internal(10),
@ -221,25 +229,36 @@ impl M68kInstructionTiming {
Instruction::BCHG(bit, target, size) => match bit {
Target::Immediate(_) => self.add_reg_v_mem(target, 12, 12),
_ => self.add_reg_v_mem(target, 8, 8),
}.add_target(*size, target),
}
.add_target(*size, target),
Instruction::BCLR(bit, target, size) => match bit {
Target::Immediate(_) => self.add_reg_v_mem(target, 14, 12),
_ => self.add_reg_v_mem(target, 10, 8),
}.add_target(*size, target),
}
.add_target(*size, target),
Instruction::BSET(bit, target, size) => match bit {
Target::Immediate(_) => self.add_reg_v_mem(target, 12, 12),
_ => self.add_reg_v_mem(target, 8, 8),
}.add_target(*size, target),
}
.add_target(*size, target),
Instruction::BTST(bit, target, size) => match bit {
Target::Immediate(_) => self.add_reg_v_mem(target, 10, 8),
_ => self.add_reg_v_mem(target, 6, 4),
}.add_target(*size, target),
}
.add_target(*size, target),
Instruction::CHK(_, _, _) => self.add_internal(10),
Instruction::CLR(target, size) => self.add_reg_v_mem(target, 4, 8).add_word_v_long(*size, 0, 2).add_target(*size, target),
Instruction::CLR(target, size) => self
.add_reg_v_mem(target, 4, 8)
.add_word_v_long(*size, 0, 2)
.add_target(*size, target),
Instruction::CMP(Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 14), (8, 12)).add_target(*size, dest),
Instruction::CMP(src, dest, size) => self.add_standard_set(*size, dest, (6, 6), (4, 6), (0, 0)).add_two_targets(*size, src, dest),
Instruction::CMP(Target::Immediate(_), dest, size) => {
self.add_immediate_set(*size, dest, (8, 14), (8, 12)).add_target(*size, dest)
},
Instruction::CMP(src, dest, size) => self
.add_standard_set(*size, dest, (6, 6), (4, 6), (0, 0))
.add_two_targets(*size, src, dest),
Instruction::CMPA(target, _, size) => self.add_word_v_long(*size, 6, 6).add_target(*size, target),
Instruction::DBcc(_, _, _) => self.add_internal(10).add_on_branch(4),
@ -248,8 +267,12 @@ impl M68kInstructionTiming {
Sign::Signed => self.add_internal(158).add_target(Size::Long, src),
},
Instruction::EOR(Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest),
Instruction::EOR(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 8), (8, 12)).add_two_targets(*size, src, dest),
Instruction::EOR(Target::Immediate(_), dest, size) => {
self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest)
},
Instruction::EOR(src, dest, size) => self
.add_standard_set(*size, dest, (0, 0), (4, 8), (8, 12))
.add_two_targets(*size, src, dest),
Instruction::EORtoCCR(_) => self.add_internal(20),
Instruction::EORtoSR(_) => self.add_internal(20),
@ -263,8 +286,9 @@ impl M68kInstructionTiming {
Instruction::LEA(target, _) => self.add_indirect_set(target, 4, 8, 12, 8, 12),
Instruction::LINK(_, _) => self.add_internal(16),
Instruction::LSL(_, target, size) |
Instruction::LSR(_, target, size) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target),
Instruction::LSL(_, target, size) | Instruction::LSR(_, target, size) => {
self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target)
},
Instruction::MOVE(src, dest, size) => self.add_internal(4).add_two_targets(*size, src, dest),
Instruction::MOVEA(target, _, size) => self.add_internal(4).add_target(*size, target),
@ -287,8 +311,12 @@ impl M68kInstructionTiming {
Instruction::NOP => self.add_internal(4),
Instruction::NOT(target, size) => self.add_reg_mem_set(*size, target, (4, 6), (8, 12)).add_target(*size, target),
Instruction::OR(Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest),
Instruction::OR(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12)).add_two_targets(*size, src, dest),
Instruction::OR(Target::Immediate(_), dest, size) => {
self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest)
},
Instruction::OR(src, dest, size) => self
.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12))
.add_two_targets(*size, src, dest),
Instruction::ORtoCCR(_) => self.add_internal(20),
Instruction::ORtoSR(_) => self.add_internal(20),
@ -296,26 +324,40 @@ impl M68kInstructionTiming {
Instruction::RESET => self.add_internal(132),
Instruction::ROL(_, target, size) |
Instruction::ROR(_, target, size) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target),
Instruction::ROXL(_, target, size) |
Instruction::ROXR(_, target, size) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target),
Instruction::ROL(_, target, size) | Instruction::ROR(_, target, size) => {
self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target)
},
Instruction::ROXL(_, target, size) | Instruction::ROXR(_, target, size) => {
self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target)
},
Instruction::RTE => self.add_internal(20),
Instruction::RTR => self.add_internal(20),
Instruction::RTS => self.add_internal(16),
//Instruction::RTD(offset) => ,
Instruction::SBCD(_, dest) => self.add_reg_v_mem(dest, 6, 18),
Instruction::Scc(_, target) => self.add_reg_v_mem(target, 4, 8).add_on_branch(2).add_target(Size::Byte, target),
Instruction::Scc(_, target) => self
.add_reg_v_mem(target, 4, 8)
.add_on_branch(2)
.add_target(Size::Byte, target),
Instruction::STOP(_) => self.add_internal(4),
Instruction::SUB(Target::Immediate(x), Target::DirectAReg(_), Size::Byte)
| Instruction::SUB(Target::Immediate(x), Target::DirectAReg(_), Size::Word) if *x <= 8 => self.add_internal(8), // SUBQ with an address reg as dest
Instruction::SUB(Target::Immediate(x), dest, size) if *x <= 8 => self.add_immediate_set(*size, dest, (4, 8), (8, 12)).add_target(*size, dest), // SUBQ
Instruction::SUB(Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest), // SUBI
| Instruction::SUB(Target::Immediate(x), Target::DirectAReg(_), Size::Word)
if *x <= 8 =>
{
self.add_internal(8)
}, // SUBQ with an address reg as dest
Instruction::SUB(Target::Immediate(x), dest, size) if *x <= 8 => {
self.add_immediate_set(*size, dest, (4, 8), (8, 12)).add_target(*size, dest)
}, // SUBQ
Instruction::SUB(Target::Immediate(_), dest, size) => {
self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest)
}, // SUBI
Instruction::SUB(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12)).add_two_targets(*size, src, dest),
Instruction::SUB(src, dest, size) => self
.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12))
.add_two_targets(*size, src, dest),
Instruction::SUBA(target, _, size) => self.add_word_v_long(*size, 8, 6).add_target(*size, target),
Instruction::SUBX(_, dest, size) => self.add_reg_mem_set(*size, dest, (4, 8), (18, 30)),
@ -362,9 +404,9 @@ impl M68kInstructionTiming {
pub fn calculate_clocks(&self) -> ClockCycles {
//println!("{:?}", self);
(self.accesses as ClockCycles * 4)
+ self.internal as ClockCycles
+ (if self.branched { self.on_branch as ClockCycles } else { 0 })
+ self.per_rep as ClockCycles * self.reps
+ self.internal as ClockCycles
+ (if self.branched { self.on_branch as ClockCycles } else { 0 })
+ self.per_rep as ClockCycles * self.reps
}
#[inline(always)]
@ -376,6 +418,3 @@ impl M68kInstructionTiming {
}
}
}

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess;
use emulator_hal_memory::MemoryBlock;
@ -69,7 +68,9 @@ fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Ins
// Insert basic initialization
let len = 0x2000;
let mut data = Vec::with_capacity(len);
unsafe { data.set_len(len); }
unsafe {
data.set_len(len);
}
let mut memory = MemoryBlock::from(data);
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR).unwrap();
@ -194,4 +195,3 @@ pub fn run_assembler_opcode_tests() {
}
}
*/

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess;
use emulator_hal::step::Step;
@ -44,7 +43,9 @@ where
// Insert basic initialization
let len = 0x10_0000;
let mut data = Vec::with_capacity(len);
unsafe { data.set_len(len); }
unsafe {
data.set_len(len);
}
let mut memory = MemoryBlock::from(data);
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR).unwrap();
@ -78,7 +79,7 @@ fn build_state(state: &TestState) -> M68kState {
fn load_memory<Bus: BusAccess<u32, Instant>>(bus: &mut Bus, data: &[u16]) {
for i in 0..data.len() {
bus.write_beu16(Instant::START, (i << 1) as u32, data[i]).unwrap();
}
}
}
fn run_test(case: &TestCase) {
@ -708,4 +709,3 @@ const TEST_CASES: &'static [TestCase] = &[
fini: TestState { pc: 0x00000002, ssp: 0x00000000, usp: 0x00000000, d0: 0x00000080, d1: 0x0000007F, a0: 0x00000000, a1: 0x00000000, sr: 0x2719, mem: 0x00000000 },
},
];

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess;
use emulator_hal_memory::MemoryBlock;
@ -17,7 +16,9 @@ fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Ins
// Insert basic initialization
let len = 0x10_0000;
let mut data = Vec::with_capacity(len);
unsafe { data.set_len(len); }
unsafe {
data.set_len(len);
}
let mut memory = MemoryBlock::from(data);
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR).unwrap();
@ -75,7 +76,10 @@ pub fn run_timing_tests() {
print!("Testing for {:?}...", case.ins);
match run_timing_test(case) {
Ok(()) => println!("ok"),
Err(err) => { println!("{:?}", err); errors += 1 },
Err(err) => {
println!("{:?}", err);
errors += 1
},
}
if let Err(_) = run_timing_test(case) {
@ -1624,4 +1628,3 @@ pub const TIMING_TESTS: &'static [TimingCase] = &[
TimingCase { cpu: M68kType::MC68000, data: &[0x4A98], timing: ( 12, 12, 6), ins: Instruction::TST(Target::IndirectARegInc(0), Size::Long) },
TimingCase { cpu: M68kType::MC68000, data: &[0x4E58], timing: ( 12, 12, 6), ins: Instruction::UNLK(0) },
];

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Frequency};
use emulator_hal::bus::BusAccess;
use emulator_hal_memory::MemoryBlock;
@ -19,16 +18,21 @@ struct TimingCase {
ins: Instruction,
}
const TIMING_TESTS: &'static [TimingCase] = &[
TimingCase { cpu: M68kType::MC68000, data: &[0xD090], timing: ( 14, 14, 6), ins: Instruction::ADD(Target::IndirectAReg(0), Target::DirectDReg(0), Size::Long) },
];
const TIMING_TESTS: &'static [TimingCase] = &[TimingCase {
cpu: M68kType::MC68000,
data: &[0xD090],
timing: (14, 14, 6),
ins: Instruction::ADD(Target::IndirectAReg(0), Target::DirectDReg(0), Size::Long),
}];
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Instant>) {
// Insert basic initialization
let len = 0x10_0000;
let mut data = Vec::with_capacity(len);
unsafe { data.set_len(len); }
unsafe {
data.set_len(len);
}
let mut memory = MemoryBlock::from(data);
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
memory.write_beu32(Instant::START, 4, INIT_ADDR).unwrap();
@ -85,7 +89,10 @@ pub fn run_timing_tests() {
print!("Testing for {:?}...", case.ins);
match run_timing_test(case) {
Ok(()) => println!("ok"),
Err(err) => { println!("{:?}", err); errors += 1 },
Err(err) => {
println!("{:?}", err);
errors += 1
},
}
if let Err(_) = run_timing_test(case) {
@ -97,4 +104,3 @@ pub fn run_timing_tests() {
panic!("{} errors", errors);
}
}

View File

@ -1,4 +1,3 @@
use moa_core::{System, Error, Address, Debuggable};
use crate::state::{Z80, Z80Error};
@ -37,10 +36,10 @@ impl Debuggable for Z80 {
fn run_command(&mut self, _system: &System, args: &[&str]) -> Result<bool, Error> {
match args[0] {
"l" => {
self.state.reg[Register::L as usize] = 0x05
"l" => self.state.reg[Register::L as usize] = 0x05,
_ => {
return Ok(true);
},
_ => { return Ok(true); },
}
Ok(false)
}
@ -62,4 +61,3 @@ impl Z80 {
Ok(())
}
}

View File

@ -1,11 +1,13 @@
use core::fmt::Write;
use femtos::Instant;
use moa_core::{Address, Addressable};
use crate::state::Z80Error;
use crate::instructions::{Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target, LoadTarget, UndocumentedCopy, Instruction};
use crate::instructions::{
Direction, Condition, Register, RegisterPair, IndexRegister, IndexRegisterHalf, SpecialRegister, InterruptMode, Target,
LoadTarget, UndocumentedCopy, Instruction,
};
//use emulator_hal::bus::BusAccess;
//
@ -57,177 +59,169 @@ impl Z80Decoder {
self.decode_bare(memory, ins, 0)
}
pub fn decode_bare(&mut self, memory: &mut dyn Addressable, ins: u8, extra_instruction_bytes: u16) -> Result<Instruction, Z80Error> {
pub fn decode_bare(
&mut self,
memory: &mut dyn Addressable,
ins: u8,
extra_instruction_bytes: u16,
) -> Result<Instruction, Z80Error> {
self.extra_instruction_bytes = extra_instruction_bytes;
match get_ins_x(ins) {
0 => {
match get_ins_z(ins) {
0 => {
match get_ins_y(ins) {
0 => Ok(Instruction::NOP),
1 => Ok(Instruction::EXafaf),
2 => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::DJNZ(offset))
},
3 => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::JR(offset))
},
y => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::JRcc(get_condition(y - 4), offset))
},
}
},
1 => {
if get_ins_q(ins) == 0 {
let data = self.read_instruction_word(memory)?;
Ok(Instruction::LD(LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))), LoadTarget::ImmediateWord(data)))
} else {
Ok(Instruction::ADD16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
}
},
0 => match get_ins_z(ins) {
0 => match get_ins_y(ins) {
0 => Ok(Instruction::NOP),
1 => Ok(Instruction::EXafaf),
2 => {
if (ins & 0x20) == 0 {
let target = match (ins & 0x10) != 0 {
false => LoadTarget::IndirectRegByte(RegisterPair::BC),
true => LoadTarget::IndirectRegByte(RegisterPair::DE),
};
match get_ins_q(ins) != 0 {
false => Ok(Instruction::LD(target, LoadTarget::DirectRegByte(Register::A))),
true => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), target)),
}
} else {
let addr = self.read_instruction_word(memory)?;
match (ins >> 3) & 0x03 {
0 => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(RegisterPair::HL))),
1 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(addr))),
2 => Ok(Instruction::LD(LoadTarget::IndirectByte(addr), LoadTarget::DirectRegByte(Register::A))),
3 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), LoadTarget::IndirectByte(addr))),
_ => panic!("InternalError: impossible value"),
}
}
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::DJNZ(offset))
},
3 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::INC16(get_register_pair(get_ins_p(ins))))
} else {
Ok(Instruction::DEC16(get_register_pair(get_ins_p(ins))))
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::JR(offset))
},
y => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::JRcc(get_condition(y - 4), offset))
},
},
1 => {
if get_ins_q(ins) == 0 {
let data = self.read_instruction_word(memory)?;
Ok(Instruction::LD(
LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))),
LoadTarget::ImmediateWord(data),
))
} else {
Ok(Instruction::ADD16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
}
},
2 => {
if (ins & 0x20) == 0 {
let target = match (ins & 0x10) != 0 {
false => LoadTarget::IndirectRegByte(RegisterPair::BC),
true => LoadTarget::IndirectRegByte(RegisterPair::DE),
};
match get_ins_q(ins) != 0 {
false => Ok(Instruction::LD(target, LoadTarget::DirectRegByte(Register::A))),
true => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), target)),
}
},
4 => {
Ok(Instruction::INC8(get_register(get_ins_y(ins))))
},
5 => {
Ok(Instruction::DEC8(get_register(get_ins_y(ins))))
},
6 => {
let data = self.read_instruction_byte(memory)?;
Ok(Instruction::LD(to_load_target(get_register(get_ins_y(ins))), LoadTarget::ImmediateByte(data)))
},
7 => {
match get_ins_y(ins) {
0 => Ok(Instruction::RLCA),
1 => Ok(Instruction::RRCA),
2 => Ok(Instruction::RLA),
3 => Ok(Instruction::RRA),
4 => Ok(Instruction::DAA),
5 => Ok(Instruction::CPL),
6 => Ok(Instruction::SCF),
7 => Ok(Instruction::CCF),
} else {
let addr = self.read_instruction_word(memory)?;
match (ins >> 3) & 0x03 {
0 => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(RegisterPair::HL))),
1 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(addr))),
2 => Ok(Instruction::LD(LoadTarget::IndirectByte(addr), LoadTarget::DirectRegByte(Register::A))),
3 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), LoadTarget::IndirectByte(addr))),
_ => panic!("InternalError: impossible value"),
}
},
}
},
3 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::INC16(get_register_pair(get_ins_p(ins))))
} else {
Ok(Instruction::DEC16(get_register_pair(get_ins_p(ins))))
}
},
4 => Ok(Instruction::INC8(get_register(get_ins_y(ins)))),
5 => Ok(Instruction::DEC8(get_register(get_ins_y(ins)))),
6 => {
let data = self.read_instruction_byte(memory)?;
Ok(Instruction::LD(to_load_target(get_register(get_ins_y(ins))), LoadTarget::ImmediateByte(data)))
},
7 => match get_ins_y(ins) {
0 => Ok(Instruction::RLCA),
1 => Ok(Instruction::RRCA),
2 => Ok(Instruction::RLA),
3 => Ok(Instruction::RRA),
4 => Ok(Instruction::DAA),
5 => Ok(Instruction::CPL),
6 => Ok(Instruction::SCF),
7 => Ok(Instruction::CCF),
_ => panic!("InternalError: impossible value"),
}
},
_ => panic!("InternalError: impossible value"),
},
1 => {
if ins == 0x76 {
Ok(Instruction::HALT)
} else {
Ok(Instruction::LD(to_load_target(get_register(get_ins_y(ins))), to_load_target(get_register(get_ins_z(ins)))))
Ok(Instruction::LD(
to_load_target(get_register(get_ins_y(ins))),
to_load_target(get_register(get_ins_z(ins))),
))
}
},
2 => {
Ok(get_alu_instruction(get_ins_y(ins), get_register(get_ins_z(ins))))
},
3 => {
match get_ins_z(ins) {
0 => {
Ok(Instruction::RETcc(get_condition(get_ins_y(ins))))
},
1 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::POP(get_register_pair_alt(get_ins_p(ins))))
} else {
match get_ins_p(ins) {
0 => Ok(Instruction::RET),
1 => Ok(Instruction::EXX),
2 => Ok(Instruction::JPIndirect(RegisterPair::HL)),
3 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::SP), LoadTarget::DirectRegWord(RegisterPair::HL))),
_ => panic!("InternalError: impossible value"),
}
}
},
2 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::JPcc(get_condition(get_ins_y(ins)), addr))
},
3 => {
match get_ins_y(ins) {
0 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::JP(addr))
},
1 => {
self.decode_prefix_cb(memory)
},
2 => {
let port = self.read_instruction_byte(memory)?;
Ok(Instruction::OUTx(port))
},
3 => {
let port = self.read_instruction_byte(memory)?;
Ok(Instruction::INx(port))
},
4 => Ok(Instruction::EXsp(RegisterPair::HL)),
5 => Ok(Instruction::EXhlde),
6 => Ok(Instruction::DI),
7 => Ok(Instruction::EI),
2 => Ok(get_alu_instruction(get_ins_y(ins), get_register(get_ins_z(ins)))),
3 => match get_ins_z(ins) {
0 => Ok(Instruction::RETcc(get_condition(get_ins_y(ins)))),
1 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::POP(get_register_pair_alt(get_ins_p(ins))))
} else {
match get_ins_p(ins) {
0 => Ok(Instruction::RET),
1 => Ok(Instruction::EXX),
2 => Ok(Instruction::JPIndirect(RegisterPair::HL)),
3 => Ok(Instruction::LD(
LoadTarget::DirectRegWord(RegisterPair::SP),
LoadTarget::DirectRegWord(RegisterPair::HL),
)),
_ => panic!("InternalError: impossible value"),
}
},
4 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::CALLcc(get_condition(get_ins_y(ins)), addr))
}
5 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::PUSH(get_register_pair_alt(get_ins_p(ins))))
} else {
match get_ins_p(ins) {
0 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::CALL(addr))
},
1 => self.decode_prefix_dd_fd(memory, IndexRegister::IX),
2 => self.decode_prefix_ed(memory),
3 => self.decode_prefix_dd_fd(memory, IndexRegister::IY),
_ => panic!("InternalError: impossible value"),
}
},
2 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::JPcc(get_condition(get_ins_y(ins)), addr))
},
3 => match get_ins_y(ins) {
0 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::JP(addr))
},
1 => self.decode_prefix_cb(memory),
2 => {
let port = self.read_instruction_byte(memory)?;
Ok(Instruction::OUTx(port))
},
3 => {
let port = self.read_instruction_byte(memory)?;
Ok(Instruction::INx(port))
},
4 => Ok(Instruction::EXsp(RegisterPair::HL)),
5 => Ok(Instruction::EXhlde),
6 => Ok(Instruction::DI),
7 => Ok(Instruction::EI),
_ => panic!("InternalError: impossible value"),
},
4 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::CALLcc(get_condition(get_ins_y(ins)), addr))
},
5 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::PUSH(get_register_pair_alt(get_ins_p(ins))))
} else {
match get_ins_p(ins) {
0 => {
let addr = self.read_instruction_word(memory)?;
Ok(Instruction::CALL(addr))
},
1 => self.decode_prefix_dd_fd(memory, IndexRegister::IX),
2 => self.decode_prefix_ed(memory),
3 => self.decode_prefix_dd_fd(memory, IndexRegister::IY),
_ => panic!("InternalError: impossible value"),
}
}
6 => {
let data = self.read_instruction_byte(memory)?;
Ok(get_alu_instruction(get_ins_y(ins), Target::Immediate(data)))
},
7 => {
Ok(Instruction::RST(get_ins_y(ins) * 8))
},
_ => panic!("InternalError: impossible value"),
}
},
6 => {
let data = self.read_instruction_byte(memory)?;
Ok(get_alu_instruction(get_ins_y(ins), Target::Immediate(data)))
},
7 => Ok(Instruction::RST(get_ins_y(ins) * 8)),
_ => panic!("InternalError: impossible value"),
},
_ => panic!("InternalError: impossible value"),
}
@ -266,92 +260,88 @@ impl Z80Decoder {
match get_ins_x(ins) {
0 => Ok(Instruction::NOP),
1 => {
match get_ins_z(ins) {
0 => {
let target = get_register(get_ins_y(ins));
if let Target::DirectReg(reg) = target {
Ok(Instruction::INic(reg))
} else {
Ok(Instruction::INicz)
}
},
1 => {
let target = get_register(get_ins_y(ins));
if let Target::DirectReg(reg) = target {
Ok(Instruction::OUTic(reg))
} else {
Ok(Instruction::OUTicz)
}
},
2 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::SBC16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
} else {
Ok(Instruction::ADC16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
}
},
3 => {
let addr = self.read_instruction_word(memory)?;
if get_ins_q(ins) == 0 {
Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins)))))
} else {
Ok(Instruction::LD(LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))), LoadTarget::IndirectWord(addr)))
}
},
4 => {
Ok(Instruction::NEG)
},
5 => {
if get_ins_y(ins) == 1 {
Ok(Instruction::RETI)
} else {
Ok(Instruction::RETN)
}
},
6 => {
match get_ins_y(ins) & 0x03 {
0 => Ok(Instruction::IM(InterruptMode::Mode0)),
1 => Ok(Instruction::IM(InterruptMode::Mode0)),
2 => Ok(Instruction::IM(InterruptMode::Mode1)),
3 => Ok(Instruction::IM(InterruptMode::Mode2)),
_ => panic!("InternalError: impossible value"),
}
},
7 => {
match get_ins_y(ins) {
0 => Ok(Instruction::LDsr(SpecialRegister::I, Direction::FromAcc)),
1 => Ok(Instruction::LDsr(SpecialRegister::R, Direction::FromAcc)),
2 => Ok(Instruction::LDsr(SpecialRegister::I, Direction::ToAcc)),
3 => Ok(Instruction::LDsr(SpecialRegister::R, Direction::ToAcc)),
4 => Ok(Instruction::RRD),
5 => Ok(Instruction::RLD),
_ => Ok(Instruction::NOP),
}
},
1 => match get_ins_z(ins) {
0 => {
let target = get_register(get_ins_y(ins));
if let Target::DirectReg(reg) = target {
Ok(Instruction::INic(reg))
} else {
Ok(Instruction::INicz)
}
},
1 => {
let target = get_register(get_ins_y(ins));
if let Target::DirectReg(reg) = target {
Ok(Instruction::OUTic(reg))
} else {
Ok(Instruction::OUTicz)
}
},
2 => {
if get_ins_q(ins) == 0 {
Ok(Instruction::SBC16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
} else {
Ok(Instruction::ADC16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
}
},
3 => {
let addr = self.read_instruction_word(memory)?;
if get_ins_q(ins) == 0 {
Ok(Instruction::LD(
LoadTarget::IndirectWord(addr),
LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))),
))
} else {
Ok(Instruction::LD(
LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))),
LoadTarget::IndirectWord(addr),
))
}
},
4 => Ok(Instruction::NEG),
5 => {
if get_ins_y(ins) == 1 {
Ok(Instruction::RETI)
} else {
Ok(Instruction::RETN)
}
},
6 => match get_ins_y(ins) & 0x03 {
0 => Ok(Instruction::IM(InterruptMode::Mode0)),
1 => Ok(Instruction::IM(InterruptMode::Mode0)),
2 => Ok(Instruction::IM(InterruptMode::Mode1)),
3 => Ok(Instruction::IM(InterruptMode::Mode2)),
_ => panic!("InternalError: impossible value"),
}
},
2 => {
match ins {
0xA0 => Ok(Instruction::LDI),
0xA1 => Ok(Instruction::CPI),
0xA2 => Ok(Instruction::INI),
0xA3 => Ok(Instruction::OUTI),
0xA8 => Ok(Instruction::LDD),
0xA9 => Ok(Instruction::CPD),
0xAA => Ok(Instruction::IND),
0xAB => Ok(Instruction::OUTD),
0xB0 => Ok(Instruction::LDIR),
0xB1 => Ok(Instruction::CPIR),
0xB2 => Ok(Instruction::INIR),
0xB3 => Ok(Instruction::OTIR),
0xB8 => Ok(Instruction::LDDR),
0xB9 => Ok(Instruction::CPDR),
0xBA => Ok(Instruction::INDR),
0xBB => Ok(Instruction::OTDR),
},
7 => match get_ins_y(ins) {
0 => Ok(Instruction::LDsr(SpecialRegister::I, Direction::FromAcc)),
1 => Ok(Instruction::LDsr(SpecialRegister::R, Direction::FromAcc)),
2 => Ok(Instruction::LDsr(SpecialRegister::I, Direction::ToAcc)),
3 => Ok(Instruction::LDsr(SpecialRegister::R, Direction::ToAcc)),
4 => Ok(Instruction::RRD),
5 => Ok(Instruction::RLD),
_ => Ok(Instruction::NOP),
}
},
_ => panic!("InternalError: impossible value"),
},
2 => match ins {
0xA0 => Ok(Instruction::LDI),
0xA1 => Ok(Instruction::CPI),
0xA2 => Ok(Instruction::INI),
0xA3 => Ok(Instruction::OUTI),
0xA8 => Ok(Instruction::LDD),
0xA9 => Ok(Instruction::CPD),
0xAA => Ok(Instruction::IND),
0xAB => Ok(Instruction::OUTD),
0xB0 => Ok(Instruction::LDIR),
0xB1 => Ok(Instruction::CPIR),
0xB2 => Ok(Instruction::INIR),
0xB3 => Ok(Instruction::OTIR),
0xB8 => Ok(Instruction::LDDR),
0xB9 => Ok(Instruction::CPDR),
0xBA => Ok(Instruction::INDR),
0xBB => Ok(Instruction::OTDR),
_ => Ok(Instruction::NOP),
},
3 => Ok(Instruction::NOP),
_ => panic!("InternalError: impossible value"),
@ -372,125 +362,120 @@ impl Z80Decoder {
}
match get_ins_p(ins) {
2 => {
match get_ins_z(ins) {
1 => {
let data = self.read_instruction_word(memory)?;
Ok(Instruction::LD(LoadTarget::DirectRegWord(index_reg.into()), LoadTarget::ImmediateWord(data)))
},
2 => {
let addr = self.read_instruction_word(memory)?;
let regpair = index_reg.into();
match get_ins_q(ins) != 0 {
false => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(regpair))),
true => Ok(Instruction::LD(LoadTarget::DirectRegWord(regpair), LoadTarget::IndirectWord(addr))),
}
},
3 => {
match get_ins_q(ins) != 0 {
false => Ok(Instruction::INC16(index_reg.into())),
true => Ok(Instruction::DEC16(index_reg.into())),
}
},
4 => {
self.extra_instruction_bytes = 4;
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
Ok(Instruction::INC8(half_target))
},
5 => {
self.extra_instruction_bytes = 4;
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
Ok(Instruction::DEC8(half_target))
},
6 => {
self.extra_instruction_bytes = 4;
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
let data = self.read_instruction_byte(memory)?;
Ok(Instruction::LD(to_load_target(half_target), LoadTarget::ImmediateByte(data)))
},
_ => self.decode_bare(memory, ins, 4),
}
2 => match get_ins_z(ins) {
1 => {
let data = self.read_instruction_word(memory)?;
Ok(Instruction::LD(LoadTarget::DirectRegWord(index_reg.into()), LoadTarget::ImmediateWord(data)))
},
2 => {
let addr = self.read_instruction_word(memory)?;
let regpair = index_reg.into();
match get_ins_q(ins) != 0 {
false => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(regpair))),
true => Ok(Instruction::LD(LoadTarget::DirectRegWord(regpair), LoadTarget::IndirectWord(addr))),
}
},
3 => match get_ins_q(ins) != 0 {
false => Ok(Instruction::INC16(index_reg.into())),
true => Ok(Instruction::DEC16(index_reg.into())),
},
4 => {
self.extra_instruction_bytes = 4;
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
Ok(Instruction::INC8(half_target))
},
5 => {
self.extra_instruction_bytes = 4;
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
Ok(Instruction::DEC8(half_target))
},
6 => {
self.extra_instruction_bytes = 4;
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
let data = self.read_instruction_byte(memory)?;
Ok(Instruction::LD(to_load_target(half_target), LoadTarget::ImmediateByte(data)))
},
_ => self.decode_bare(memory, ins, 4),
},
3 => {
match ins {
0x34 => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::INC8(Target::IndirectOffset(index_reg, offset)))
},
0x35 => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::DEC8(Target::IndirectOffset(index_reg, offset)))
},
0x36 => {
let offset = self.read_instruction_byte(memory)? as i8;
let immediate = self.read_instruction_byte(memory)?;
Ok(Instruction::LD(LoadTarget::IndirectOffsetByte(index_reg, offset), LoadTarget::ImmediateByte(immediate)))
},
_ => self.decode_bare(memory, ins, 4),
}
3 => match ins {
0x34 => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::INC8(Target::IndirectOffset(index_reg, offset)))
},
0x35 => {
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::DEC8(Target::IndirectOffset(index_reg, offset)))
},
0x36 => {
let offset = self.read_instruction_byte(memory)? as i8;
let immediate = self.read_instruction_byte(memory)?;
Ok(Instruction::LD(
LoadTarget::IndirectOffsetByte(index_reg, offset),
LoadTarget::ImmediateByte(immediate),
))
},
_ => self.decode_bare(memory, ins, 4),
},
_ => self.decode_bare(memory, ins, 4),
}
},
1 => {
match get_ins_p(ins) {
0 | 1 => {
1 => match get_ins_p(ins) {
0 | 1 => {
let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? {
Some(target) => target,
None => return self.decode_bare(memory, ins, 4),
};
match (ins & 0x18) >> 3 {
0 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::B), to_load_target(target))),
1 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::C), to_load_target(target))),
2 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::D), to_load_target(target))),
3 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::E), to_load_target(target))),
_ => panic!("InternalError: impossible value"),
}
},
2 => {
let src = match get_ins_z(ins) {
0 => Target::DirectReg(Register::B),
1 => Target::DirectReg(Register::C),
2 => Target::DirectReg(Register::D),
3 => Target::DirectReg(Register::E),
4 => Target::DirectRegHalf(get_index_register_half(index_reg, 0)),
5 => Target::DirectRegHalf(get_index_register_half(index_reg, 1)),
6 => {
let offset = self.read_instruction_byte(memory)? as i8;
let src = to_load_target(Target::IndirectOffset(index_reg, offset));
if get_ins_q(ins) == 0 {
return Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::H), src));
} else {
return Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::L), src));
}
},
7 => Target::DirectReg(Register::A),
_ => panic!("InternalError: impossible value"),
};
let dest = get_index_register_half(index_reg, get_ins_q(ins));
Ok(Instruction::LD(LoadTarget::DirectRegHalfByte(dest), to_load_target(src)))
},
3 => {
if get_ins_q(ins) == 0 {
if get_ins_z(ins) == 6 {
return self.decode_bare(memory, ins, 4);
}
let src = get_register(get_ins_z(ins));
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::LD(LoadTarget::IndirectOffsetByte(index_reg, offset), to_load_target(src)))
} else {
let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? {
Some(target) => target,
None => return self.decode_bare(memory, ins, 4),
};
match (ins & 0x18) >> 3 {
0 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::B), to_load_target(target))),
1 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::C), to_load_target(target))),
2 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::D), to_load_target(target))),
3 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::E), to_load_target(target))),
_ => panic!("InternalError: impossible value"),
}
},
2 => {
let src = match get_ins_z(ins) {
0 => Target::DirectReg(Register::B),
1 => Target::DirectReg(Register::C),
2 => Target::DirectReg(Register::D),
3 => Target::DirectReg(Register::E),
4 => Target::DirectRegHalf(get_index_register_half(index_reg, 0)),
5 => Target::DirectRegHalf(get_index_register_half(index_reg, 1)),
6 => {
let offset = self.read_instruction_byte(memory)? as i8;
let src = to_load_target(Target::IndirectOffset(index_reg, offset));
if get_ins_q(ins) == 0 {
return Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::H), src));
} else {
return Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::L), src));
}
},
7 => Target::DirectReg(Register::A),
_ => panic!("InternalError: impossible value"),
};
let dest = get_index_register_half(index_reg, get_ins_q(ins));
Ok(Instruction::LD(LoadTarget::DirectRegHalfByte(dest), to_load_target(src)))
},
3 => {
if get_ins_q(ins) == 0 {
if get_ins_z(ins) == 6 {
return self.decode_bare(memory, ins, 4);
}
let src = get_register(get_ins_z(ins));
let offset = self.read_instruction_byte(memory)? as i8;
Ok(Instruction::LD(LoadTarget::IndirectOffsetByte(index_reg, offset), to_load_target(src)))
} else {
let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? {
Some(target) => target,
None => return self.decode_bare(memory, ins, 4),
};
Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), to_load_target(target)))
}
},
_ => panic!("InternalError: impossible value"),
}
Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), to_load_target(target)))
}
},
_ => panic!("InternalError: impossible value"),
},
2 => {
self.extra_instruction_bytes = 4;
@ -512,21 +497,27 @@ impl Z80Decoder {
_ => panic!("InternalError: impossible value"),
}
},
3 => {
match ins {
0xE1 => Ok(Instruction::POP(index_reg.into())),
0xE3 => Ok(Instruction::EXsp(index_reg.into())),
0xE5 => Ok(Instruction::PUSH(index_reg.into())),
0xE9 => Ok(Instruction::JPIndirect(index_reg.into())),
0xF9 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::SP), LoadTarget::DirectRegWord(index_reg.into()))),
_ => self.decode_bare(memory, ins, 4),
}
3 => match ins {
0xE1 => Ok(Instruction::POP(index_reg.into())),
0xE3 => Ok(Instruction::EXsp(index_reg.into())),
0xE5 => Ok(Instruction::PUSH(index_reg.into())),
0xE9 => Ok(Instruction::JPIndirect(index_reg.into())),
0xF9 => Ok(Instruction::LD(
LoadTarget::DirectRegWord(RegisterPair::SP),
LoadTarget::DirectRegWord(index_reg.into()),
)),
_ => self.decode_bare(memory, ins, 4),
},
_ => panic!("InternalError: impossible value"),
}
}
fn decode_index_target(&mut self, memory: &mut dyn Addressable, index_reg: IndexRegister, z: u8) -> Result<Option<Target>, Z80Error> {
fn decode_index_target(
&mut self,
memory: &mut dyn Addressable,
index_reg: IndexRegister,
z: u8,
) -> Result<Option<Target>, Z80Error> {
let result = match z {
4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))),
5 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 1))),
@ -675,8 +666,20 @@ fn get_register_pair_alt(reg: u8) -> RegisterPair {
fn get_index_register_half(reg: IndexRegister, q: u8) -> IndexRegisterHalf {
match reg {
IndexRegister::IX => if q == 0 { IndexRegisterHalf::IXH } else { IndexRegisterHalf::IXL },
IndexRegister::IY => if q == 0 { IndexRegisterHalf::IYH } else { IndexRegisterHalf::IYL },
IndexRegister::IX => {
if q == 0 {
IndexRegisterHalf::IXH
} else {
IndexRegisterHalf::IXL
}
},
IndexRegister::IY => {
if q == 0 {
IndexRegisterHalf::IYH
} else {
IndexRegisterHalf::IYL
}
},
}
}
@ -726,4 +729,3 @@ fn get_ins_p(ins: u8) -> u8 {
fn get_ins_q(ins: u8) -> u8 {
(ins >> 3) & 0x01
}

View File

@ -1,16 +1,18 @@
use femtos::{Instant, Duration};
use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16};
use crate::instructions::{Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister, IndexRegisterHalf, Size, Direction, UndocumentedCopy};
use crate::instructions::{
Condition, Instruction, LoadTarget, Target, Register, InterruptMode, RegisterPair, IndexRegister, SpecialRegister,
IndexRegisterHalf, Size, Direction, UndocumentedCopy,
};
use crate::state::{Z80, Z80Error, Status, Flags};
use crate::timing::Z80InstructionCycles;
const FLAGS_NUMERIC: u8 = 0xC0;
const FLAGS_ARITHMETIC: u8 = 0x17;
const FLAGS_CARRY_HALF_CARRY: u8 = 0x11;
const FLAGS_NUMERIC: u8 = 0xC0;
const FLAGS_ARITHMETIC: u8 = 0x17;
const FLAGS_CARRY_HALF_CARRY: u8 = 0x11;
enum RotateType {
@ -36,7 +38,7 @@ impl Steppable for Z80 {
}
}
impl Interruptable for Z80 { }
impl Interruptable for Z80 {}
impl Transmutable for Z80 {
@ -72,7 +74,6 @@ impl From<Error> for Z80Error {
Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(msg.to_string()),
}
}
}
#[derive(Clone)]
pub struct Z80Executor {
@ -95,11 +96,9 @@ impl Z80 {
match self.state.status {
Status::Init => self.init(),
Status::Halted => Err(Z80Error::Halted),
Status::Running => {
match self.cycle_one() {
Ok(clocks) => Ok(clocks),
Err(err) => Err(err),
}
Status::Running => match self.cycle_one() {
Ok(clocks) => Ok(clocks),
Err(err) => Err(err),
},
}
}
@ -120,12 +119,15 @@ impl Z80 {
self.decode_next()?;
self.execute_current()?;
Ok(Z80InstructionCycles::from_instruction(&self.decoder.instruction, self.decoder.extra_instruction_bytes)?
.calculate_cycles(self.executor.took_branch))
Ok(
Z80InstructionCycles::from_instruction(&self.decoder.instruction, self.decoder.extra_instruction_bytes)?
.calculate_cycles(self.executor.took_branch),
)
}
pub fn decode_next(&mut self) -> Result<(), Z80Error> {
self.decoder.decode_at(&mut self.port, self.executor.current_clock, self.state.pc)?;
self.decoder
.decode_at(&mut self.port, self.executor.current_clock, self.state.pc)?;
self.increment_refresh(self.decoder.end.saturating_sub(self.decoder.start) as u8);
self.state.pc = self.decoder.end;
Ok(())
@ -186,9 +188,7 @@ impl Z80 {
Instruction::LDsr(special_reg, dir) => self.execute_ldsr(special_reg, dir),
Instruction::LDD | Instruction::LDDR | Instruction::LDI | Instruction::LDIR => self.execute_ldx(),
Instruction::NEG => self.execute_neg(),
Instruction::NOP => {
Ok(())
},
Instruction::NOP => Ok(()),
Instruction::OR(target) => self.execute_or(target),
//Instruction::OTDR => {
//},
@ -230,9 +230,7 @@ impl Z80 {
Instruction::SRL(target, opt_copy) => self.execute_srl(target, opt_copy),
Instruction::SUB(target) => self.execute_sub(target),
Instruction::XOR(target) => self.execute_xor(target),
_ => {
Err(Z80Error::Unimplemented(self.decoder.instruction.clone()))
}
_ => Err(Z80Error::Unimplemented(self.decoder.instruction.clone())),
}
}
@ -242,7 +240,14 @@ impl Z80 {
let (result1, carry1, overflow1, half_carry1) = add_bytes(acc, self.get_flag(Flags::Carry) as u8);
let (result2, carry2, overflow2, half_carry2) = add_bytes(result1, src);
self.set_arithmetic_op_flags(result2 as u16, Size::Byte, false, carry1 | carry2, overflow1 ^ overflow2, half_carry1 | half_carry2);
self.set_arithmetic_op_flags(
result2 as u16,
Size::Byte,
false,
carry1 | carry2,
overflow1 ^ overflow2,
half_carry1 | half_carry2,
);
self.set_register_value(Register::A, result2);
Ok(())
@ -410,7 +415,7 @@ impl Z80 {
let value = self.get_target_value(target)?;
let (result, _, overflow, half_carry) = sub_bytes(value, 1);
let carry = self.get_flag(Flags::Carry); // Preserve the carry bit, according to Z80 reference
let carry = self.get_flag(Flags::Carry); // Preserve the carry bit, according to Z80 reference
self.set_arithmetic_op_flags(result as u16, Size::Byte, true, carry, overflow, half_carry);
self.set_target_value(target, result)?;
@ -498,7 +503,7 @@ impl Z80 {
fn execute_inc8(&mut self, target: Target) -> Result<(), Z80Error> {
let value = self.get_target_value(target)?;
let (result, _, overflow, half_carry) = add_bytes(value, 1);
let carry = self.get_flag(Flags::Carry); // Preserve the carry bit, according to Z80 reference
let carry = self.get_flag(Flags::Carry); // Preserve the carry bit, according to Z80 reference
self.set_arithmetic_op_flags(result as u16, Size::Byte, false, carry, overflow, half_carry);
self.set_target_value(target, result)?;
@ -626,9 +631,7 @@ impl Z80 {
let parity = if count != 0 { Flags::Parity as u8 } else { 0 };
self.set_flags(mask, parity);
if (self.decoder.instruction == Instruction::LDIR || self.decoder.instruction == Instruction::LDDR)
&& count != 0
{
if (self.decoder.instruction == Instruction::LDIR || self.decoder.instruction == Instruction::LDDR) && count != 0 {
self.executor.took_branch = true;
self.state.pc -= 2;
}
@ -860,7 +863,14 @@ impl Z80 {
let (result1, carry1, overflow1, half_carry1) = sub_bytes(acc, src);
let (result2, carry2, overflow2, half_carry2) = sub_bytes(result1, self.get_flag(Flags::Carry) as u8);
self.set_arithmetic_op_flags(result2 as u16, Size::Byte, true, carry1 | carry2, overflow1 ^ overflow2, half_carry1 | half_carry2);
self.set_arithmetic_op_flags(
result2 as u16,
Size::Byte,
true,
carry1 | carry2,
overflow1 ^ overflow2,
half_carry1 | half_carry2,
);
self.set_register_value(Register::A, result2);
Ok(())
@ -1041,12 +1051,8 @@ impl Z80 {
let addr = self.get_register_pair_value(regpair);
self.read_port_u16(addr)?
},
LoadTarget::IndirectByte(addr) => {
self.read_port_u8(addr)? as u16
},
LoadTarget::IndirectWord(addr) => {
self.read_port_u16(addr)?
},
LoadTarget::IndirectByte(addr) => self.read_port_u8(addr)? as u16,
LoadTarget::IndirectWord(addr) => self.read_port_u16(addr)?,
LoadTarget::ImmediateByte(data) => data as u16,
LoadTarget::ImmediateWord(data) => data,
_ => panic!("Unsupported LoadTarget for set"),
@ -1176,10 +1182,18 @@ impl Z80 {
fn set_index_register_half_value(&mut self, reg: IndexRegisterHalf, value: u8) {
match reg {
IndexRegisterHalf::IXH => { self.state.ix = (self.state.ix & 0x00FF) | (value as u16) << 8; },
IndexRegisterHalf::IXL => { self.state.ix = (self.state.ix & 0xFF00) | value as u16; },
IndexRegisterHalf::IYH => { self.state.iy = (self.state.iy & 0x00FF) | (value as u16) << 8; },
IndexRegisterHalf::IYL => { self.state.iy = (self.state.iy & 0xFF00) | value as u16; },
IndexRegisterHalf::IXH => {
self.state.ix = (self.state.ix & 0x00FF) | (value as u16) << 8;
},
IndexRegisterHalf::IXL => {
self.state.ix = (self.state.ix & 0xFF00) | value as u16;
},
IndexRegisterHalf::IYH => {
self.state.iy = (self.state.iy & 0x00FF) | (value as u16) << 8;
},
IndexRegisterHalf::IYL => {
self.state.iy = (self.state.iy & 0xFF00) | value as u16;
},
}
}
@ -1197,13 +1211,27 @@ impl Z80 {
fn set_register_pair_value(&mut self, regpair: RegisterPair, value: u16) {
match regpair {
RegisterPair::BC => { write_beu16(&mut self.state.reg[0..2], value); },
RegisterPair::DE => { write_beu16(&mut self.state.reg[2..4], value); },
RegisterPair::HL => { write_beu16(&mut self.state.reg[4..6], value); },
RegisterPair::AF => { write_beu16(&mut self.state.reg[6..8], value); },
RegisterPair::SP => { self.state.sp = value; },
RegisterPair::IX => { self.state.ix = value; },
RegisterPair::IY => { self.state.iy = value; },
RegisterPair::BC => {
write_beu16(&mut self.state.reg[0..2], value);
},
RegisterPair::DE => {
write_beu16(&mut self.state.reg[2..4], value);
},
RegisterPair::HL => {
write_beu16(&mut self.state.reg[4..6], value);
},
RegisterPair::AF => {
write_beu16(&mut self.state.reg[6..8], value);
},
RegisterPair::SP => {
self.state.sp = value;
},
RegisterPair::IX => {
self.state.ix = value;
},
RegisterPair::IY => {
self.state.iy = value;
},
}
}
@ -1234,7 +1262,11 @@ impl Z80 {
}
fn set_parity_flags(&mut self, value: u8) {
let parity = if (value.count_ones() & 0x01) == 0 { Flags::Parity as u8 } else { 0 };
let parity = if (value.count_ones() & 0x01) == 0 {
Flags::Parity as u8
} else {
0
};
self.set_flags(Flags::Parity as u8, parity);
}
@ -1313,4 +1345,3 @@ fn get_msb(value: u16, size: Size) -> bool {
Size::Word => (value & 0x8000) != 0,
}
}

View File

@ -1,4 +1,3 @@
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Direction {
ToAcc,
@ -206,5 +205,3 @@ impl RegisterPair {
matches!(self, RegisterPair::IX | RegisterPair::IY)
}
}

View File

@ -1,4 +1,3 @@
pub mod debugger;
pub mod decode;
pub mod execute;
@ -7,4 +6,3 @@ pub mod state;
pub mod timing;
pub use self::state::{Z80, Z80Type, Z80Error};

View File

@ -1,4 +1,3 @@
use std::rc::Rc;
use std::cell::RefCell;
use femtos::{Instant, Frequency};
@ -28,6 +27,7 @@ pub enum Status {
#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[rustfmt::skip]
pub enum Flags {
Carry = 0x01,
AddSubtract = 0x02,
@ -134,9 +134,20 @@ impl Z80 {
}
}
pub fn from_type(cputype: Z80Type, frequency: Frequency, bus: Rc<RefCell<Bus>>, addr_offset: Address, io_bus: Option<(Rc<RefCell<Bus>>, Address)>) -> Self {
pub fn from_type(
cputype: Z80Type,
frequency: Frequency,
bus: Rc<RefCell<Bus>>,
addr_offset: Address,
io_bus: Option<(Rc<RefCell<Bus>>, Address)>,
) -> Self {
match cputype {
Z80Type::Z80 => Self::new(cputype, frequency, BusPort::new(addr_offset, 16, 8, bus), io_bus.map(|(io_bus, io_offset)| BusPort::new(io_offset, 16, 8, io_bus))),
Z80Type::Z80 => Self::new(
cputype,
frequency,
BusPort::new(addr_offset, 16, 8, bus),
io_bus.map(|(io_bus, io_offset)| BusPort::new(io_offset, 16, 8, io_bus)),
),
}
}
@ -155,18 +166,45 @@ impl Z80 {
println!("IX: {:#06x}", self.state.ix);
println!("IY: {:#06x}", self.state.iy);
println!("A: {:#04x} F: {:#04x} A': {:#04x} F': {:#04x}", self.state.reg[Register::A as usize], self.state.reg[Register::F as usize], self.state.shadow_reg[Register::A as usize], self.state.shadow_reg[Register::F as usize]);
println!("B: {:#04x} C: {:#04x} B': {:#04x} C': {:#04x}", self.state.reg[Register::B as usize], self.state.reg[Register::C as usize], self.state.shadow_reg[Register::B as usize], self.state.shadow_reg[Register::C as usize]);
println!("D: {:#04x} E: {:#04x} D': {:#04x} E': {:#04x}", self.state.reg[Register::D as usize], self.state.reg[Register::E as usize], self.state.shadow_reg[Register::D as usize], self.state.shadow_reg[Register::E as usize]);
println!("H: {:#04x} L: {:#04x} H': {:#04x} L': {:#04x}", self.state.reg[Register::H as usize], self.state.reg[Register::L as usize], self.state.shadow_reg[Register::H as usize], self.state.shadow_reg[Register::L as usize]);
println!(
"A: {:#04x} F: {:#04x} A': {:#04x} F': {:#04x}",
self.state.reg[Register::A as usize],
self.state.reg[Register::F as usize],
self.state.shadow_reg[Register::A as usize],
self.state.shadow_reg[Register::F as usize]
);
println!(
"B: {:#04x} C: {:#04x} B': {:#04x} C': {:#04x}",
self.state.reg[Register::B as usize],
self.state.reg[Register::C as usize],
self.state.shadow_reg[Register::B as usize],
self.state.shadow_reg[Register::C as usize]
);
println!(
"D: {:#04x} E: {:#04x} D': {:#04x} E': {:#04x}",
self.state.reg[Register::D as usize],
self.state.reg[Register::E as usize],
self.state.shadow_reg[Register::D as usize],
self.state.shadow_reg[Register::E as usize]
);
println!(
"H: {:#04x} L: {:#04x} H': {:#04x} L': {:#04x}",
self.state.reg[Register::H as usize],
self.state.reg[Register::L as usize],
self.state.shadow_reg[Register::H as usize],
self.state.shadow_reg[Register::L as usize]
);
println!("I: {:#04x} R: {:#04x}", self.state.i, self.state.r);
println!("IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2);
println!("Current Instruction: {} {:?}", self.decoder.format_instruction_bytes(&mut self.port), self.decoder.instruction);
println!(
"Current Instruction: {} {:?}",
self.decoder.format_instruction_bytes(&mut self.port),
self.decoder.instruction
);
println!();
self.port.dump_memory(clock, self.state.sp as Address, 0x40);
println!();
}
}

View File

@ -1,18 +1,11 @@
use moa_core::Error;
use crate::instructions::{Instruction, Target, LoadTarget, RegisterPair};
pub enum Z80InstructionCycles {
Single(u16),
Branch {
taken: u16,
not_taken: u16
},
Repeating {
repeating: u16,
terminating: u16,
},
Branch { taken: u16, not_taken: u16 },
Repeating { repeating: u16, terminating: u16 },
}
impl Z80InstructionCycles {
@ -20,33 +13,47 @@ impl Z80InstructionCycles {
match self {
Z80InstructionCycles::Single(cycles) => *cycles,
Z80InstructionCycles::Branch { taken, not_taken } => if took_branch { *taken } else { *not_taken },
Z80InstructionCycles::Branch {
taken,
not_taken,
} => {
if took_branch {
*taken
} else {
*not_taken
}
},
Z80InstructionCycles::Repeating { repeating, terminating } => if took_branch { *repeating } else { *terminating },
Z80InstructionCycles::Repeating {
repeating,
terminating,
} => {
if took_branch {
*repeating
} else {
*terminating
}
},
}
}
pub fn from_instruction(instruction: &Instruction, extra: u16) -> Result<Z80InstructionCycles, Error> {
let cycles = match instruction {
Instruction::ADCa(target) |
Instruction::ADDa(target) |
Instruction::AND(target) |
Instruction::CP(target) |
Instruction::SBCa(target) |
Instruction::SUB(target) |
Instruction::OR(target) |
Instruction::XOR(target) => {
match target {
Target::DirectReg(_) |
Target::DirectRegHalf(_) => 4,
Target::IndirectReg(_) => 7,
Target::Immediate(_) => 7,
Target::IndirectOffset(_, _) => 19,
}
Instruction::ADCa(target)
| Instruction::ADDa(target)
| Instruction::AND(target)
| Instruction::CP(target)
| Instruction::SBCa(target)
| Instruction::SUB(target)
| Instruction::OR(target)
| Instruction::XOR(target) => match target {
Target::DirectReg(_) | Target::DirectRegHalf(_) => 4,
Target::IndirectReg(_) => 7,
Target::Immediate(_) => 7,
Target::IndirectOffset(_, _) => 19,
},
Instruction::ADC16(_, _) |
Instruction::SBC16(_, _) => 15,
Instruction::ADC16(_, _) | Instruction::SBC16(_, _) => 15,
Instruction::ADD16(dest_pair, _) => {
if !dest_pair.is_index_reg() {
@ -56,13 +63,11 @@ impl Z80InstructionCycles {
}
},
Instruction::BIT(_, target) => {
match target {
Target::DirectReg(_) => 8,
Target::IndirectReg(_) => 12,
Target::IndirectOffset(_, _) => 20,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
}
Instruction::BIT(_, target) => match target {
Target::DirectReg(_) => 8,
Target::IndirectReg(_) => 12,
Target::IndirectOffset(_, _) => 20,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
},
Instruction::CALL(_) => 17,
@ -76,45 +81,40 @@ impl Z80InstructionCycles {
Instruction::CCF => 4,
Instruction::CPD |
Instruction::CPI |
Instruction::IND |
Instruction::INI |
Instruction::LDD |
Instruction::LDI |
Instruction::OUTD |
Instruction::OUTI => 16,
Instruction::CPD
| Instruction::CPI
| Instruction::IND
| Instruction::INI
| Instruction::LDD
| Instruction::LDI
| Instruction::OUTD
| Instruction::OUTI => 16,
Instruction::CPDR |
Instruction::CPIR |
Instruction::INDR |
Instruction::INIR |
Instruction::LDDR |
Instruction::LDIR |
Instruction::OTDR |
Instruction::OTIR => {
Instruction::CPDR
| Instruction::CPIR
| Instruction::INDR
| Instruction::INIR
| Instruction::LDDR
| Instruction::LDIR
| Instruction::OTDR
| Instruction::OTIR => {
return Ok(Z80InstructionCycles::Repeating {
repeating: 21 + extra,
terminating: 16 + extra,
})
});
},
Instruction::CPL => 4,
Instruction::DAA => 4,
Instruction::DEC8(target) |
Instruction::INC8(target) => {
match target {
Target::DirectReg(_) |
Target::DirectRegHalf(_) => 4,
Target::IndirectReg(_) => 11,
Target::IndirectOffset(_, _) => 23,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
}
Instruction::DEC8(target) | Instruction::INC8(target) => match target {
Target::DirectReg(_) | Target::DirectRegHalf(_) => 4,
Target::IndirectReg(_) => 11,
Target::IndirectOffset(_, _) => 23,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
},
Instruction::DEC16(regpair) |
Instruction::INC16(regpair) => {
Instruction::DEC16(regpair) | Instruction::INC16(regpair) => {
if !regpair.is_index_reg() {
6
} else {
@ -122,8 +122,7 @@ impl Z80InstructionCycles {
}
},
Instruction::DI |
Instruction::EI => 4,
Instruction::DI | Instruction::EI => 4,
Instruction::DJNZ(_) => {
return Ok(Z80InstructionCycles::Branch {
@ -146,13 +145,9 @@ impl Z80InstructionCycles {
Instruction::HALT => 4,
Instruction::IM(_) => 8,
Instruction::INic(_) |
Instruction::INicz |
Instruction::OUTic(_) |
Instruction::OUTicz => 12,
Instruction::INic(_) | Instruction::INicz | Instruction::OUTic(_) | Instruction::OUTicz => 12,
Instruction::INx(_) |
Instruction::OUTx(_) => 11,
Instruction::INx(_) | Instruction::OUTx(_) => 11,
Instruction::JP(_) => 10,
Instruction::JR(_) => 12,
@ -177,29 +172,24 @@ impl Z80InstructionCycles {
Instruction::LD(dest, src) => {
match (dest, src) {
// 8-Bit Operations
(LoadTarget::DirectRegByte(_), LoadTarget::DirectRegByte(_)) => 4,
(LoadTarget::DirectRegHalfByte(_), LoadTarget::DirectRegByte(_)) |
(LoadTarget::DirectRegByte(_), LoadTarget::DirectRegHalfByte(_)) |
(LoadTarget::DirectRegHalfByte(_), LoadTarget::DirectRegHalfByte(_)) => 8,
(LoadTarget::DirectRegHalfByte(_), LoadTarget::DirectRegByte(_))
| (LoadTarget::DirectRegByte(_), LoadTarget::DirectRegHalfByte(_))
| (LoadTarget::DirectRegHalfByte(_), LoadTarget::DirectRegHalfByte(_)) => 8,
(LoadTarget::DirectRegByte(_) | LoadTarget::DirectRegHalfByte(_), LoadTarget::ImmediateByte(_)) => 7,
(LoadTarget::IndirectRegByte(_), LoadTarget::ImmediateByte(_)) => 10,
(LoadTarget::IndirectOffsetByte(_, _), _) |
(_, LoadTarget::IndirectOffsetByte(_, _)) => 19,
(LoadTarget::IndirectOffsetByte(_, _), _) | (_, LoadTarget::IndirectOffsetByte(_, _)) => 19,
(_, LoadTarget::IndirectRegByte(_)) |
(LoadTarget::IndirectRegByte(_), _) => 7,
(_, LoadTarget::IndirectRegByte(_)) | (LoadTarget::IndirectRegByte(_), _) => 7,
(_, LoadTarget::IndirectByte(_)) |
(LoadTarget::IndirectByte(_), _) => 13,
(_, LoadTarget::IndirectByte(_)) | (LoadTarget::IndirectByte(_), _) => 13,
// 16-Bit Operations
(LoadTarget::DirectRegWord(regpair), LoadTarget::ImmediateWord(_)) |
(LoadTarget::ImmediateWord(_), LoadTarget::DirectRegWord(regpair)) => {
(LoadTarget::DirectRegWord(regpair), LoadTarget::ImmediateWord(_))
| (LoadTarget::ImmediateWord(_), LoadTarget::DirectRegWord(regpair)) => {
if !regpair.is_index_reg() {
10
} else {
@ -215,11 +205,10 @@ impl Z80InstructionCycles {
}
},
(LoadTarget::IndirectWord(_), LoadTarget::DirectRegWord(RegisterPair::HL)) |
(LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(_)) => 16,
(LoadTarget::IndirectWord(_), LoadTarget::DirectRegWord(RegisterPair::HL))
| (LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(_)) => 16,
(LoadTarget::IndirectWord(_), _) |
(_, LoadTarget::IndirectWord(_)) => 20,
(LoadTarget::IndirectWord(_), _) | (_, LoadTarget::IndirectWord(_)) => 20,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
}
@ -230,7 +219,6 @@ impl Z80InstructionCycles {
Instruction::NEG => 8,
Instruction::NOP => 4,
Instruction::POP(regpair) => {
if !regpair.is_index_reg() {
10
@ -246,14 +234,11 @@ impl Z80InstructionCycles {
}
},
Instruction::RES(_, target, _) |
Instruction::SET(_, target, _) => {
match target {
Target::DirectReg(_) => 8,
Target::IndirectReg(_) => 15,
Target::IndirectOffset(_, _) => 23,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
}
Instruction::RES(_, target, _) | Instruction::SET(_, target, _) => match target {
Target::DirectReg(_) => 8,
Target::IndirectReg(_) => 15,
Target::IndirectOffset(_, _) => 23,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
},
Instruction::RET => 10,
@ -267,26 +252,21 @@ impl Z80InstructionCycles {
});
},
Instruction::RL(target, _) |
Instruction::RLC(target, _) |
Instruction::RR(target, _) |
Instruction::RRC(target, _) |
Instruction::SLA(target, _) |
Instruction::SLL(target, _) |
Instruction::SRA(target, _) |
Instruction::SRL(target, _) => {
match target {
Target::DirectReg(_) => 8,
Target::IndirectReg(_) => 15,
Target::IndirectOffset(_, _) => 23,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
}
Instruction::RL(target, _)
| Instruction::RLC(target, _)
| Instruction::RR(target, _)
| Instruction::RRC(target, _)
| Instruction::SLA(target, _)
| Instruction::SLL(target, _)
| Instruction::SRA(target, _)
| Instruction::SRL(target, _) => match target {
Target::DirectReg(_) => 8,
Target::IndirectReg(_) => 15,
Target::IndirectOffset(_, _) => 23,
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
},
Instruction::RLA |
Instruction::RLCA |
Instruction::RRA |
Instruction::RRCA => 4,
Instruction::RLA | Instruction::RLCA | Instruction::RRA | Instruction::RRCA => 4,
Instruction::RLD => 18,
Instruction::RRD => 18,
@ -298,4 +278,3 @@ impl Z80InstructionCycles {
Ok(Z80InstructionCycles::Single(cycles + extra))
}
}

View File

@ -1,4 +1,3 @@
use femtos::Frequency;
use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device};
@ -55,6 +54,7 @@ fn run_all_decode_tests() {
}
}
#[rustfmt::skip]
const DECODE_TESTS: &'static [(&[u8], Instruction)] = &[
(&[0x00], Instruction::NOP),
(&[0x01, 0x01, 0x02], Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::BC), LoadTarget::ImmediateWord(0x0201))),
@ -70,4 +70,3 @@ const DECODE_TESTS: &'static [(&[u8], Instruction)] = &[
(&[0xDD, 0x84], Instruction::ADDa(Target::DirectRegHalf(IndexRegisterHalf::IXH))),
(&[0xDD, 0x85], Instruction::ADDa(Target::DirectRegHalf(IndexRegisterHalf::IXL))),
];

View File

@ -1,4 +1,3 @@
use femtos::Frequency;
use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device};
@ -26,6 +25,7 @@ struct TestCase {
fini: TestState,
}
#[rustfmt::skip]
const TEST_CASES: &'static [TestCase] = &[
/*
TestCase {
@ -551,4 +551,3 @@ pub fn run_execute_tests() {
run_test(case);
}
}

View File

@ -1,4 +1,3 @@
use std::sync::{Arc, Mutex, MutexGuard};
use femtos::{Instant, Duration};
@ -21,10 +20,7 @@ impl AudioSource {
let (id, sample_rate) = {
let mut mixer = mixer.borrow_mut();
let id = mixer.add_source(queue.clone());
(
id,
mixer.sample_rate(),
)
(id, mixer.sample_rate())
};
Self {
@ -122,7 +118,9 @@ impl AudioMixerInner {
if let Some((clock, mut frame)) = source.pop_next() {
index = (clock.duration_since(frame_start) / sample_duration) as usize;
let size = frame.data.len().min(data.len() - index);
frame.data.iter()
frame
.data
.iter()
.zip(&mut data[index..index + size])
.for_each(|(source, dest)| {
dest.0 += source.0;
@ -198,4 +196,3 @@ impl AudioOutput {
self.queue.is_empty()
}
}

View File

@ -1,5 +1,7 @@
use cpal::{Stream, SampleRate, SampleFormat, StreamConfig, OutputCallbackInfo, traits::{DeviceTrait, HostTrait, StreamTrait}};
use cpal::{
Stream, SampleRate, SampleFormat, StreamConfig, OutputCallbackInfo,
traits::{DeviceTrait, HostTrait, StreamTrait},
};
use crate::audio::{AudioOutput, SAMPLE_RATE};
@ -27,7 +29,9 @@ impl CpalAudioOutput {
while index < data.len() {
if let Some((clock, mut frame)) = output.receive() {
let size = (frame.data.len() * 2).min(data.len() - index);
frame.data.iter()
frame
.data
.iter()
.zip(data[index..index + size].chunks_mut(2))
.for_each(|(sample, location)| {
location[0] = sample.0;
@ -45,13 +49,11 @@ impl CpalAudioOutput {
}
};
let stream = device.build_output_stream(
&config,
data_callback,
move |err| {
let stream = device
.build_output_stream(&config, data_callback, move |err| {
log::error!("ERROR: {:?}", err);
},
).unwrap();
})
.unwrap();
stream.play().unwrap();
@ -68,4 +70,3 @@ impl CpalAudioOutput {
}
}
}

View File

@ -1,4 +1,3 @@
#[cfg(feature = "tty")]
pub mod tty;
@ -9,4 +8,3 @@ pub use crate::audio::{AudioMixer, AudioSource};
pub mod cpal;
#[cfg(feature = "audio")]
pub use crate::cpal::CpalAudioOutput;

View File

@ -1,4 +1,3 @@
use std::thread;
use std::sync::mpsc;
use std::time::Duration;
@ -34,11 +33,13 @@ impl SimplePty {
}
pub fn open() -> Result<SimplePty, SimplePtyError> {
let pty = pty::posix_openpt(OFlag::O_RDWR).and_then(|pty| {
pty::grantpt(&pty)?;
pty::unlockpt(&pty)?;
Ok(pty)
}).map_err(|_| SimplePtyError::Open)?;
let pty = pty::posix_openpt(OFlag::O_RDWR)
.and_then(|pty| {
pty::grantpt(&pty)?;
pty::unlockpt(&pty)?;
Ok(pty)
})
.map_err(|_| SimplePtyError::Open)?;
let name = unsafe { pty::ptsname(&pty).map_err(|_| SimplePtyError::PtsName)? };
let (input_tx, input_rx) = mpsc::channel();
@ -61,8 +62,10 @@ impl SimplePty {
Ok(_) => {
input_tx.send(buf[0]).unwrap();
},
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => { },
Err(err) => { println!("ERROR: {:?}", err); }
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {},
Err(err) => {
println!("ERROR: {:?}", err);
},
}
while let Ok(data) = output_rx.try_recv() {
@ -92,4 +95,3 @@ impl Tty for SimplePty {
true
}
}

View File

@ -1,4 +1,3 @@
use std::thread;
use std::time::Duration;
use femtos::Frequency;
@ -45,4 +44,3 @@ fn main() {
});
thread::sleep(Duration::from_secs(10));
}

View File

@ -1,4 +1,3 @@
use clap::Arg;
use moa_console::ConsoleFrontend;
@ -6,11 +5,13 @@ use moa_systems_computie::{build_computie, ComputieOptions};
fn main() {
let matches = ConsoleFrontend::args("Computie68k Emulator")
.arg(Arg::new("ROM")
.short('r')
.long("rom")
.value_name("FILE")
.help("ROM file to load at the start of memory"))
.arg(
Arg::new("ROM")
.short('r')
.long("rom")
.value_name("FILE")
.help("ROM file to load at the start of memory"),
)
.get_matches();
let mut options = ComputieOptions::default();
@ -23,4 +24,3 @@ fn main() {
let system = build_computie(&frontend, options).unwrap();
frontend.start(matches, system);
}

View File

@ -1,4 +1,3 @@
use clap::{Arg};
use moa_console::ConsoleFrontend;
@ -6,8 +5,7 @@ use moa_systems_genesis::{build_genesis, SegaGenesisOptions};
fn main() {
let matches = ConsoleFrontend::args("Sega Genesis/Mega Drive Emulator")
.arg(Arg::new("ROM")
.help("ROM file to load (must be flat binary)"))
.arg(Arg::new("ROM").help("ROM file to load (must be flat binary)"))
.get_matches();
let mut frontend = ConsoleFrontend;
@ -20,4 +18,3 @@ fn main() {
let system = build_genesis(&mut frontend, options).unwrap();
frontend.start(matches, system);
}

View File

@ -1,4 +1,3 @@
use clap::{Command, Arg, ArgAction, ArgMatches};
use std::io::{self, Write};
use femtos::Duration;
@ -14,7 +13,8 @@ impl Host for ConsoleFrontend {
fn add_pty(&self) -> Result<Box<dyn Tty>, HostError<Self::Error>> {
use moa_common::tty::SimplePty;
Ok(Box::new(SimplePty::open().map_err(|_| HostError::TTYNotSupported)?)) //.map_err(|err| Error::new(format!("console: error opening pty: {:?}", err)))?))
Ok(Box::new(SimplePty::open().map_err(|_| HostError::TTYNotSupported)?))
//.map_err(|err| Error::new(format!("console: error opening pty: {:?}", err)))?))
}
fn add_video_source(&mut self, _receiver: FrameReceiver) -> Result<(), HostError<Self::Error>> {
@ -42,15 +42,19 @@ impl Default for ConsoleFrontend {
impl ConsoleFrontend {
pub fn args(application_name: &'static str) -> Command {
Command::new(application_name)
.arg(Arg::new("log-level")
.short('l')
.long("log-level")
.help("Set the type of log messages to print"))
.arg(Arg::new("debugger")
.short('d')
.long("debugger")
.action(ArgAction::SetTrue)
.help("Start the debugger before running machine"))
.arg(
Arg::new("log-level")
.short('l')
.long("log-level")
.help("Set the type of log messages to print"),
)
.arg(
Arg::new("debugger")
.short('d')
.long("debugger")
.action(ArgAction::SetTrue)
.help("Start the debugger before running machine"),
)
}
pub fn start(self, matches: ArgMatches, mut system: System) {
@ -108,4 +112,3 @@ impl ConsoleFrontend {
}
}
}

View File

@ -1,12 +1,10 @@
use clap::Arg;
use moa_systems_genesis::{build_genesis, SegaGenesisOptions};
fn main() {
let matches = moa_minifb::new("Sega Genesis/Mega Drive Emulator")
.arg(Arg::new("ROM")
.help("ROM file to load (must be flat binary)"))
.arg(Arg::new("ROM").help("ROM file to load (must be flat binary)"))
.get_matches();
let mut options = SegaGenesisOptions::default();
@ -14,8 +12,5 @@ fn main() {
options.rom = filename.to_string();
}
moa_minifb::run(matches, |frontend| {
build_genesis(frontend, options)
});
moa_minifb::run(matches, |frontend| build_genesis(frontend, options));
}

View File

@ -1,12 +1,7 @@
use moa_systems_macintosh::build_macintosh_512k;
fn main() {
let matches = moa_minifb::new("Macintosh 512k Emulator")
.get_matches();
let matches = moa_minifb::new("Macintosh 512k Emulator").get_matches();
moa_minifb::run(matches, |frontend| {
build_macintosh_512k(frontend)
});
moa_minifb::run(matches, |frontend| build_macintosh_512k(frontend));
}

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Duration, Frequency};
use moa_peripherals_yamaha::{Ym2612, Sn76489};
@ -26,20 +25,23 @@ impl SynthControl {
impl Steppable for SynthControl {
fn step(&mut self, system: &System) -> Result<Duration, Error> {
if let Some(event) = self.key_receiver.receive() {
match event.key {
Key::Enter => {
system.get_bus().write_u8(system.clock, 0x00, 0x28)?;
system.get_bus().write_u8(system.clock, 0x01, if event.state { 0xF0 } else { 0x00 })?;
system
.get_bus()
.write_u8(system.clock, 0x01, if event.state { 0xF0 } else { 0x00 })?;
},
Key::A => {
system.get_bus().write_u8(system.clock, 0x10, 0x84)?;
system.get_bus().write_u8(system.clock, 0x10, 0x0F)?;
system.get_bus().write_u8(system.clock, 0x10, if event.state { 0x90 } else { 0x9F })?;
system
.get_bus()
.write_u8(system.clock, 0x10, if event.state { 0x90 } else { 0x9F })?;
},
_ => { },
_ => {},
}
}
@ -79,8 +81,7 @@ fn initialize_ym(ym_sound: Device) -> Result<(), Error> {
}
fn main() {
let matches = moa_minifb::new("YM2612 Tester/Synth")
.get_matches();
let matches = moa_minifb::new("YM2612 Tester/Synth").get_matches();
moa_minifb::run(matches, |host| {
let mut system = System::default();
@ -103,5 +104,3 @@ fn main() {
Ok(system)
});
}

View File

@ -1,16 +1,17 @@
use clap::{Arg, ArgAction};
use moa_systems_trs80::{build_trs80, Trs80Options};
fn main() {
let matches = moa_minifb::new("TRS-80 Emulator")
.arg(Arg::new("ROM")
.short('r')
.long("rom")
.action(ArgAction::SetTrue)
.value_name("FILE")
.help("ROM file to load at the start of memory"))
.arg(
Arg::new("ROM")
.short('r')
.long("rom")
.action(ArgAction::SetTrue)
.value_name("FILE")
.help("ROM file to load at the start of memory"),
)
.get_matches();
let mut options = Trs80Options::default();
@ -18,8 +19,5 @@ fn main() {
options.rom = filename.to_string();
}
moa_minifb::run(matches, |frontend| {
build_trs80(frontend, options)
});
moa_minifb::run(matches, |frontend| build_trs80(frontend, options));
}

View File

@ -1,19 +1,17 @@
use minifb::Key as MiniKey;
use moa_host::ControllerInput;
pub fn map_controller_a(key: MiniKey, state: bool) -> Option<ControllerInput> {
match key {
MiniKey::A => { Some(ControllerInput::ButtonA(state)) },
MiniKey::O => { Some(ControllerInput::ButtonB(state)) },
MiniKey::E => { Some(ControllerInput::ButtonC(state)) },
MiniKey::Up => { Some(ControllerInput::DpadUp(state)) },
MiniKey::Down => { Some(ControllerInput::DpadDown(state)) },
MiniKey::Left => { Some(ControllerInput::DpadLeft(state)) },
MiniKey::Right => { Some(ControllerInput::DpadRight(state)) },
MiniKey::Enter => { Some(ControllerInput::Start(state)) },
MiniKey::M => { Some(ControllerInput::Mode(state)) },
MiniKey::A => Some(ControllerInput::ButtonA(state)),
MiniKey::O => Some(ControllerInput::ButtonB(state)),
MiniKey::E => Some(ControllerInput::ButtonC(state)),
MiniKey::Up => Some(ControllerInput::DpadUp(state)),
MiniKey::Down => Some(ControllerInput::DpadDown(state)),
MiniKey::Left => Some(ControllerInput::DpadLeft(state)),
MiniKey::Right => Some(ControllerInput::DpadRight(state)),
MiniKey::Enter => Some(ControllerInput::Start(state)),
MiniKey::M => Some(ControllerInput::Mode(state)),
_ => None,
}
}

View File

@ -1,4 +1,3 @@
use minifb::Key as MiniKey;
use moa_host::Key;
@ -109,4 +108,3 @@ pub fn map_key(key: MiniKey) -> Key {
_ => Key::Unknown,
}
}

View File

@ -1,4 +1,3 @@
use std::thread;
use std::io::{self, Write};
use std::sync::{Arc, Mutex};
@ -10,13 +9,16 @@ use femtos::{Duration as FemtosDuration};
use moa_core::{System, Error, Device};
use moa_debugger::{Debugger, DebugControl};
use moa_host::{Host, HostError, Audio, KeyEvent, MouseEvent, MouseState, ControllerDevice, ControllerEvent, EventSender, PixelEncoding, Frame, FrameReceiver};
use moa_host::{
Host, HostError, Audio, KeyEvent, MouseEvent, MouseState, ControllerDevice, ControllerEvent, EventSender, PixelEncoding, Frame,
FrameReceiver,
};
use moa_common::{AudioMixer, AudioSource};
use moa_common::CpalAudioOutput;
mod keys;
mod controllers;
mod keys;
use crate::keys::map_key;
use crate::controllers::map_controller_a;
@ -28,36 +30,46 @@ const HEIGHT: u32 = 224;
pub fn new(name: &'static str) -> Command {
Command::new(name)
.arg(Arg::new("scale")
.short('s')
.long("scale")
.help("Scale the screen"))
.arg(Arg::new("speed")
.short('x')
.long("speed")
.help("Adjust the speed of the simulation"))
.arg(Arg::new("threaded")
.short('t')
.long("threaded")
.action(ArgAction::SetTrue)
.help("Run the simulation in a separate thread"))
.arg(Arg::new("log-level")
.short('l')
.long("log-level")
.help("Set the type of log messages to print"))
.arg(Arg::new("debugger")
.short('d')
.long("debugger")
.action(ArgAction::SetTrue)
.help("Start the debugger before running machine"))
.arg(Arg::new("disable-audio")
.short('a')
.long("disable-audio")
.action(ArgAction::SetTrue)
.help("Disable audio output"))
.arg(Arg::new("scale").short('s').long("scale").help("Scale the screen"))
.arg(
Arg::new("speed")
.short('x')
.long("speed")
.help("Adjust the speed of the simulation"),
)
.arg(
Arg::new("threaded")
.short('t')
.long("threaded")
.action(ArgAction::SetTrue)
.help("Run the simulation in a separate thread"),
)
.arg(
Arg::new("log-level")
.short('l')
.long("log-level")
.help("Set the type of log messages to print"),
)
.arg(
Arg::new("debugger")
.short('d')
.long("debugger")
.action(ArgAction::SetTrue)
.help("Start the debugger before running machine"),
)
.arg(
Arg::new("disable-audio")
.short('a')
.long("disable-audio")
.action(ArgAction::SetTrue)
.help("Disable audio output"),
)
}
pub fn run<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static {
pub fn run<I>(matches: ArgMatches, init: I)
where
I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static,
{
if matches.get_flag("threaded") {
run_threaded(matches, init);
} else {
@ -65,16 +77,20 @@ pub fn run<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBui
}
}
pub fn run_inline<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> {
pub fn run_inline<I>(matches: ArgMatches, init: I)
where
I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error>,
{
let mut frontend = MiniFrontendBuilder::default();
let system = init(&mut frontend).unwrap();
frontend
.build()
.start(matches, Some(system));
frontend.build().start(matches, Some(system));
}
pub fn run_threaded<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static {
pub fn run_threaded<I>(matches: ArgMatches, init: I)
where
I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static,
{
let frontend = Arc::new(Mutex::new(MiniFrontendBuilder::default()));
{
@ -88,10 +104,7 @@ pub fn run_threaded<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFr
wait_until_initialized(frontend.clone());
frontend
.lock().unwrap()
.build()
.start(matches, None);
frontend.lock().unwrap().build().start(matches, None);
}
fn wait_until_initialized(frontend: Arc<Mutex<MiniFrontendBuilder>>) {
@ -156,7 +169,9 @@ impl Host for MiniFrontendBuilder {
fn register_controllers(&mut self, sender: EventSender<ControllerEvent>) -> Result<(), HostError<Self::Error>> {
if self.controllers.is_some() {
return Err(HostError::Specific(Error::new("A controller updater has already been registered with the frontend")));
return Err(HostError::Specific(Error::new(
"A controller updater has already been registered with the frontend",
)));
}
self.controllers = Some(sender);
Ok(())
@ -253,13 +268,7 @@ impl MiniFrontend {
queue.request_encoding(PixelEncoding::ARGB);
}
let mut window = minifb::Window::new(
"Test - ESC to exit",
size.0 as usize,
size.1 as usize,
options,
)
.unwrap_or_else(|e| {
let mut window = minifb::Window::new("Test - ESC to exit", size.0 as usize, size.1 as usize, options).unwrap_or_else(|e| {
panic!("{}", e);
});
@ -345,7 +354,9 @@ impl MiniFrontend {
if let Some((_clock, frame)) = queue.latest() {
last_frame = frame
}
window.update_with_buffer(&last_frame.bitmap, last_frame.width as usize, last_frame.height as usize).unwrap();
window
.update_with_buffer(&last_frame.bitmap, last_frame.width as usize, last_frame.height as usize)
.unwrap();
}
}
}
@ -363,4 +374,3 @@ impl MiniFrontend {
}
}
}

View File

@ -1,4 +1,3 @@
use std::f32::consts::PI;
@ -72,7 +71,11 @@ impl Iterator for SquareWave {
fn next(&mut self) -> Option<f32> {
self.position += 1;
let samples_per_hz = self.sample_rate as f32 / self.frequency;
let result = if (self.position as f32 % samples_per_hz) < (samples_per_hz / 2.0) { 1.0 } else { -1.0 };
let result = if (self.position as f32 % samples_per_hz) < (samples_per_hz / 2.0) {
1.0
} else {
-1.0
};
Some(result)
}
}
@ -162,4 +165,3 @@ impl Iterator for SkewedSquareWave {
pub fn db_to_gain(db: f32) -> f32 {
(10.0_f32).powf(db / 20.0)
}

View File

@ -1,4 +1,3 @@
use moa_core::{Error, System, Address, Addressable};
@ -56,11 +55,7 @@ impl Debugger {
let args: Vec<&str> = command.split_whitespace().collect();
// If no command given, then run the `step` command
let args = if args.is_empty() {
vec!["step"]
} else {
args
};
let args = if args.is_empty() { vec!["step"] } else { args };
match args[0] {
"b" | "break" | "breakpoint" => {
@ -123,7 +118,11 @@ impl Debugger {
"d" | "dump" => {
if args.len() > 1 {
let addr = u32::from_str_radix(args[1], 16).map_err(|_| Error::new("Unable to parse address"))?;
let len = if args.len() > 2 { u32::from_str_radix(args[2], 16).map_err(|_| Error::new("Unable to parse length"))? } else { 0x20 };
let len = if args.len() > 2 {
u32::from_str_radix(args[2], 16).map_err(|_| Error::new("Unable to parse length"))?
} else {
0x20
};
system.get_bus().dump_memory(system.clock, addr as Address, len as Address);
} else {
//self.port.dump_memory(self.state.ssp as Address, 0x40 as Address);
@ -135,7 +134,9 @@ impl Debugger {
} else {
let device = system.get_device(args[1])?;
let subargs = if args.len() > 2 { &args[2..] } else { &[""] };
device.borrow_mut().as_inspectable()
device
.borrow_mut()
.as_inspectable()
.ok_or_else(|| Error::new("That device is not inspectable"))?
.inspect(system, subargs)?;
}
@ -154,7 +155,11 @@ impl Debugger {
};
if let Some(device) = system.get_next_debuggable_device() {
device.borrow_mut().as_debuggable().unwrap().print_disassembly(system, addr, count);
device
.borrow_mut()
.as_debuggable()
.unwrap()
.print_disassembly(system, addr, count);
}
},
"c" | "continue" => {
@ -170,7 +175,7 @@ impl Debugger {
self.trace_only = true;
system.step_until_debuggable()?;
return Ok(DebugControl::Continue);
}
},
"setb" | "setw" | "setl" => {
if args.len() != 3 {
println!("Usage: set[b|w|l] <addr> <data>");
@ -208,7 +213,9 @@ impl Debugger {
fn check_repeat_arg(&mut self, args: &[&str]) -> Result<(), Error> {
if args.len() > 1 {
let count = args[1].parse::<u32>().map_err(|_| Error::new("Unable to parse repeat number"))?;
let count = args[1]
.parse::<u32>()
.map_err(|_| Error::new("Unable to parse repeat number"))?;
self.repeat_command = Some((count, args[0].to_string()));
}
Ok(())
@ -227,4 +234,3 @@ fn parse_address(arg: &str) -> Result<(Option<&str>, Address), Error> {
let addr = Address::from_str_radix(addrstr, 16).map_err(|_| Error::new("Unable to parse address"))?;
Ok((name, addr))
}

View File

@ -1,4 +1,3 @@
#[derive(Copy, Clone, Default)]
pub struct Sample(pub f32, pub f32);
@ -22,4 +21,3 @@ impl AudioFrame {
}
}
}

View File

@ -1,4 +1,3 @@
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ControllerDevice {
A,
@ -37,4 +36,3 @@ impl ControllerEvent {
}
}
}

View File

@ -30,12 +30,9 @@ impl Pixel {
};
match encoding {
PixelEncoding::RGBA =>
(r << 24) | (g << 16) | (b << 8) | a,
PixelEncoding::ARGB =>
(a << 24) | (r << 16) | (g << 8) | b,
PixelEncoding::ABGR =>
(a << 24) | (b << 16) | (g << 8) | r,
PixelEncoding::RGBA => (r << 24) | (g << 16) | (b << 8) | a,
PixelEncoding::ARGB => (a << 24) | (r << 16) | (g << 8) | b,
PixelEncoding::ABGR => (a << 24) | (b << 16) | (g << 8) | r,
}
}
}
@ -71,22 +68,22 @@ impl Frame {
#[inline]
pub fn set_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: Pixel) {
match pixel {
Pixel::Mask => {}
Pixel::Mask => {},
value if pos_x < self.width && pos_y < self.height => {
self.bitmap[(pos_x + (pos_y * self.width)) as usize] = value.encode(self.encoding);
}
_ => {}
},
_ => {},
}
}
#[inline]
pub fn set_encoded_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: u32) {
match pixel {
MASK_COLOUR => { },
MASK_COLOUR => {},
value if pos_x < self.width && pos_y < self.height => {
self.bitmap[(pos_x + (pos_y * self.width)) as usize] = value;
},
_ => { },
_ => {},
}
}
@ -94,11 +91,11 @@ impl Frame {
for y in pos_y..(pos_y + height) {
for x in pos_x..(pos_x + width) {
match bitmap.next().unwrap() {
Pixel::Mask => {}
Pixel::Mask => {},
value if x < self.width && y < self.height => {
self.bitmap[(x + (y * self.width)) as usize] = value.encode(self.encoding);
}
_ => {}
},
_ => {},
}
}
}
@ -159,4 +156,3 @@ impl FrameReceiver {
self.queue.pop_latest()
}
}

View File

@ -1,4 +1,3 @@
use std::sync::{Arc, Mutex};
use std::collections::VecDeque;
@ -35,4 +34,3 @@ impl<T> EventReceiver<T> {
self.queue.lock().unwrap().pop_front()
}
}

View File

@ -1,4 +1,3 @@
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Key {
A,
@ -124,4 +123,3 @@ impl KeyEvent {
}
}
}

View File

@ -1,4 +1,3 @@
mod audio;
mod controllers;
mod gfx;
@ -14,4 +13,3 @@ pub use crate::mouse::{MouseButton, MouseEventType, MouseEvent, MouseState};
pub use crate::controllers::{ControllerDevice, ControllerInput, ControllerEvent};
pub use crate::input::{EventSender, EventReceiver, event_queue};
pub use crate::traits::{Host, HostError, Tty, Audio, ClockedQueue, DummyAudio};

View File

@ -1,4 +1,3 @@
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MouseButton {
Left,
@ -68,7 +67,8 @@ impl MouseState {
self.pos = next_state.pos;
let events: Vec<MouseEvent> = self
.buttons.into_iter()
.buttons
.into_iter()
.zip(next_state.buttons)
.enumerate()
.filter_map(|(i, (prev, next))| {
@ -102,8 +102,7 @@ impl MouseState {
match event.etype {
MouseEventType::Up(button) => self.buttons[usize::from(button)] = false,
MouseEventType::Down(button) => self.buttons[usize::from(button)] = true,
MouseEventType::Move => { },
MouseEventType::Move => {},
}
}
}

View File

@ -1,4 +1,3 @@
use std::fmt;
use std::error::Error;
use std::collections::VecDeque;
@ -131,4 +130,3 @@ impl Audio for DummyAudio {
fn write_samples(&mut self, _clock: Instant, _buffer: &[Sample]) {}
}

View File

@ -1,4 +1,3 @@
use std::str::Chars;
use std::iter::Peekable;
@ -56,9 +55,13 @@ impl<'input> AssemblyParser<'input> {
fn parse_line(&mut self) -> Result<Option<AssemblyLine>, ParserError> {
let token = loop {
match self.lexer.get_next() {
Some(token) if token == "\n" => { },
Some(token) => { break token; }
None => { return Ok(None); },
Some(token) if token == "\n" => {},
Some(token) => {
break token;
},
None => {
return Ok(None);
},
}
};
@ -79,7 +82,11 @@ impl<'input> AssemblyParser<'input> {
}
},
_ => {
return Err(ParserError::new(format!("parse error at line {}: expected word, found {:?}", self.lexer.lineno(), token)));
return Err(ParserError::new(format!(
"parse error at line {}: expected word, found {:?}",
self.lexer.lineno(),
token
)));
},
};
@ -164,7 +171,7 @@ impl<'input> AssemblyParser<'input> {
} else {
Ok(AssemblyOperand::Label(token))
}
}
},
}
}
}
@ -237,7 +244,8 @@ impl<'input> AssemblyLexer<'input> {
}
pub fn expect_next(&mut self) -> Result<String, ParserError> {
self.get_next().ok_or_else(|| ParserError::new(format!("unexpected end of input at line {}", self.lineno)))
self.get_next()
.ok_or_else(|| ParserError::new(format!("unexpected end of input at line {}", self.lineno)))
}
pub fn expect_token(&mut self, expected: &str) -> Result<(), ParserError> {
@ -245,7 +253,10 @@ impl<'input> AssemblyLexer<'input> {
if token == expected {
Ok(())
} else {
Err(ParserError::new(format!("parse error at line {}: expected {:?}, found {:?}", self.lineno, expected, token)))
Err(ParserError::new(format!(
"parse error at line {}: expected {:?}, found {:?}",
self.lineno, expected, token
)))
}
}
@ -309,7 +320,12 @@ pub fn expect_args(lineno: usize, args: &[AssemblyOperand], expected: usize) ->
if args.len() == expected {
Ok(())
} else {
Err(ParserError::new(format!("error at line {}: expected {} args, but found {}", lineno, expected, args.len())))
Err(ParserError::new(format!(
"error at line {}: expected {} args, but found {}",
lineno,
expected,
args.len()
)))
}
}
@ -326,7 +342,9 @@ pub fn expect_immediate(lineno: usize, operand: &AssemblyOperand) -> Result<usiz
if let AssemblyOperand::Immediate(value) = operand {
Ok(*value)
} else {
Err(ParserError::new(format!("error at line {}: expected an immediate value, but found {:?}", lineno, operand)))
Err(ParserError::new(format!(
"error at line {}: expected an immediate value, but found {:?}",
lineno, operand
)))
}
}

View File

@ -135,4 +135,3 @@ pub trait SignalDriver<T> {
//pub struct LevelTriggeredOutput

View File

@ -1,35 +1,40 @@
use std::fs;
use femtos::Instant;
use moa_core::{Error, Address, Addressable, Transmutable};
#[rustfmt::skip]
mod reg {
use super::Address;
pub(super) const DATA_WORD: Address = 0x20;
pub(super) const DATA_BYTE: Address = 0x21;
pub(super) const FEATURE: Address = 0x23;
pub(super) const ERROR: Address = 0x23;
pub(super) const SECTOR_COUNT: Address = 0x25;
pub(super) const SECTOR_NUM: Address = 0x27;
pub(super) const CYL_LOW: Address = 0x29;
pub(super) const CYL_HIGH: Address = 0x2B;
pub(super) const DRIVE_HEAD: Address = 0x2D;
pub(super) const STATUS: Address = 0x2F;
pub(super) const COMMAND: Address = 0x2F;
}
const ATA_REG_DATA_WORD: Address = 0x20;
const ATA_REG_DATA_BYTE: Address = 0x21;
const ATA_REG_FEATURE: Address = 0x23;
const ATA_REG_ERROR: Address = 0x23;
const ATA_REG_SECTOR_COUNT: Address = 0x25;
const ATA_REG_SECTOR_NUM: Address = 0x27;
const ATA_REG_CYL_LOW: Address = 0x29;
const ATA_REG_CYL_HIGH: Address = 0x2B;
const ATA_REG_DRIVE_HEAD: Address = 0x2D;
const ATA_REG_STATUS: Address = 0x2F;
const ATA_REG_COMMAND: Address = 0x2F;
const ATA_CMD_READ_SECTORS: u8 = 0x20;
const ATA_CMD_WRITE_SECTORS: u8 = 0x30;
const ATA_CMD_IDENTIFY: u8 = 0xEC;
const ATA_CMD_SET_FEATURE: u8 = 0xEF;
#[rustfmt::skip]
mod cmd {
pub(super) const READ_SECTORS: u8 = 0x20;
pub(super) const WRITE_SECTORS: u8 = 0x30;
pub(super) const IDENTIFY: u8 = 0xEC;
pub(super) const SET_FEATURE: u8 = 0xEF;
}
#[allow(dead_code)]
const ATA_ST_BUSY: u8 = 0x80;
const ATA_ST_BUSY: u8 = 0x80;
#[allow(dead_code)]
const ATA_ST_DATA_READY: u8 = 0x08;
const ATA_ST_DATA_READY: u8 = 0x08;
#[allow(dead_code)]
const ATA_ST_ERROR: u8 = 0x01;
const ATA_ST_ERROR: u8 = 0x01;
const ATA_SECTOR_SIZE: u32 = 512;
const ATA_SECTOR_SIZE: u32 = 512;
const DEV_NAME: &str = "ata";
@ -60,7 +65,7 @@ impl Addressable for AtaDevice {
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
match addr {
ATA_REG_DATA_WORD => {
reg::DATA_WORD => {
self.selected_count -= 2;
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize;
data[0] = self.contents[offset];
@ -70,7 +75,7 @@ impl Addressable for AtaDevice {
self.selected_count = 0;
}
},
ATA_REG_DATA_BYTE => {
reg::DATA_BYTE => {
self.selected_count -= 1;
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize;
data[0] = self.contents[offset];
@ -79,13 +84,15 @@ impl Addressable for AtaDevice {
self.selected_count = 0;
}
},
ATA_REG_STATUS => {
reg::STATUS => {
data[0] = ATA_ST_DATA_READY;
},
ATA_REG_ERROR => {
reg::ERROR => {
data[0] = self.last_error;
},
_ => { log::debug!("{}: reading from {:0x}", DEV_NAME, addr); },
_ => {
log::debug!("{}: reading from {:0x}", DEV_NAME, addr);
},
}
Ok(())
@ -94,27 +101,43 @@ impl Addressable for AtaDevice {
fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
match addr {
ATA_REG_DRIVE_HEAD => { self.selected_sector |= ((data[0] & 0x1F) as u32) << 24; },
ATA_REG_CYL_HIGH => { self.selected_sector |= (data[0] as u32) << 16; },
ATA_REG_CYL_LOW => { self.selected_sector |= (data[0] as u32) << 8; },
ATA_REG_SECTOR_NUM => { self.selected_sector |= data[0] as u32; },
ATA_REG_SECTOR_COUNT => { self.selected_count = (data[0] as u32) * ATA_SECTOR_SIZE; },
ATA_REG_COMMAND => {
match data[0] {
ATA_CMD_READ_SECTORS => { log::debug!("{}: reading sector {:x}", DEV_NAME, self.selected_sector); },
ATA_CMD_WRITE_SECTORS => { log::debug!("{}: writing sector {:x}", DEV_NAME, self.selected_sector); },
ATA_CMD_IDENTIFY => { },
ATA_CMD_SET_FEATURE => { },
_ => { log::debug!("{}: unrecognized command {:x}", DEV_NAME, data[0]); },
}
reg::DRIVE_HEAD => {
self.selected_sector |= ((data[0] & 0x1F) as u32) << 24;
},
ATA_REG_FEATURE => {
reg::CYL_HIGH => {
self.selected_sector |= (data[0] as u32) << 16;
},
reg::CYL_LOW => {
self.selected_sector |= (data[0] as u32) << 8;
},
reg::SECTOR_NUM => {
self.selected_sector |= data[0] as u32;
},
reg::SECTOR_COUNT => {
self.selected_count = (data[0] as u32) * ATA_SECTOR_SIZE;
},
reg::COMMAND => match data[0] {
cmd::READ_SECTORS => {
log::debug!("{}: reading sector {:x}", DEV_NAME, self.selected_sector);
},
cmd::WRITE_SECTORS => {
log::debug!("{}: writing sector {:x}", DEV_NAME, self.selected_sector);
},
cmd::IDENTIFY => {},
cmd::SET_FEATURE => {},
_ => {
log::debug!("{}: unrecognized command {:x}", DEV_NAME, data[0]);
},
},
reg::FEATURE => {
// TODO implement features
},
ATA_REG_DATA_BYTE => {
reg::DATA_BYTE => {
// TODO implement writing
},
_ => { log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); },
_ => {
log::debug!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr);
},
}
Ok(())
}
@ -125,4 +148,3 @@ impl Transmutable for AtaDevice {
Some(self)
}
}

View File

@ -1,4 +1,2 @@
mod ata;
pub use crate::ata::AtaDevice;

View File

@ -1,4 +1,2 @@
mod mos6522;
pub use crate::mos6522::Mos6522;

View File

@ -1,17 +1,20 @@
use femtos::{Instant, Duration};
use moa_core::{Error, System, Address, Addressable, Steppable, Transmutable};
use moa_signals::{Signal, ObservableSignal, Observable};
const REG_OUTPUT_B: Address = 0x00;
const REG_OUTPUT_A: Address = 0x01;
const REG_DDR_B: Address = 0x02;
const REG_DDR_A: Address = 0x03;
const REG_PERIPH_CTRL: Address = 0x0C;
const REG_INT_FLAGS: Address = 0x0D;
const REG_INT_ENABLE: Address = 0x0E;
const REG_OUTPUT_A_NHS: Address = 0x0F;
#[rustfmt::skip]
mod reg {
use super::Address;
pub(super) const OUTPUT_B: Address = 0x00;
pub(super) const OUTPUT_A: Address = 0x01;
pub(super) const DDR_B: Address = 0x02;
pub(super) const DDR_A: Address = 0x03;
pub(super) const PERIPH_CTRL: Address = 0x0C;
pub(super) const INT_FLAGS: Address = 0x0D;
pub(super) const INT_ENABLE: Address = 0x0E;
pub(super) const OUTPUT_A_NHS: Address = 0x0F;
}
const DEV_NAME: &str = "mos6522";
@ -61,12 +64,24 @@ impl Addressable for Mos6522 {
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
match addr {
REG_OUTPUT_B => { data[0] = self.port_b.borrow_mut().data; },
REG_OUTPUT_A => { data[0] = self.port_a.borrow_mut().data; },
REG_DDR_B => { data[0] = self.port_b.borrow_mut().ddr; },
REG_DDR_A => { data[0] = self.port_a.borrow_mut().ddr; },
REG_INT_FLAGS => { data[0] = self.interrupt_flags; },
REG_INT_ENABLE => { data[0] = self.interrupt_enable | 0x80; },
reg::OUTPUT_B => {
data[0] = self.port_b.borrow_mut().data;
},
reg::OUTPUT_A => {
data[0] = self.port_a.borrow_mut().data;
},
reg::DDR_B => {
data[0] = self.port_b.borrow_mut().ddr;
},
reg::DDR_A => {
data[0] = self.port_a.borrow_mut().ddr;
},
reg::INT_FLAGS => {
data[0] = self.interrupt_flags;
},
reg::INT_ENABLE => {
data[0] = self.interrupt_enable | 0x80;
},
_ => {
log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
},
@ -78,20 +93,40 @@ impl Addressable for Mos6522 {
fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
match addr {
REG_OUTPUT_B => { self.port_b.borrow_mut().data = data[0]; self.port_b.notify(); },
REG_OUTPUT_A => { self.port_a.borrow_mut().data = data[0]; self.port_a.notify(); },
REG_DDR_B => { self.port_b.borrow_mut().ddr = data[0]; self.port_b.notify(); },
REG_DDR_A => { self.port_a.borrow_mut().ddr = data[0]; self.port_a.notify(); },
REG_PERIPH_CTRL => { println!("SET TO {:?}", data[0]); self.peripheral_ctrl = data[0]; },
REG_INT_FLAGS => { self.interrupt_flags &= !data[0] & 0x7F; },
REG_INT_ENABLE => {
reg::OUTPUT_B => {
self.port_b.borrow_mut().data = data[0];
self.port_b.notify();
},
reg::OUTPUT_A => {
self.port_a.borrow_mut().data = data[0];
self.port_a.notify();
},
reg::DDR_B => {
self.port_b.borrow_mut().ddr = data[0];
self.port_b.notify();
},
reg::DDR_A => {
self.port_a.borrow_mut().ddr = data[0];
self.port_a.notify();
},
reg::PERIPH_CTRL => {
println!("SET TO {:?}", data[0]);
self.peripheral_ctrl = data[0];
},
reg::INT_FLAGS => {
self.interrupt_flags &= !data[0] & 0x7F;
},
reg::INT_ENABLE => {
if (data[0] & 0x80) == 0 {
self.interrupt_flags &= !data[0];
self.interrupt_flags &= !data[0];
} else {
self.interrupt_flags |= data[0];
self.interrupt_flags |= data[0];
}
},
REG_OUTPUT_A_NHS => { self.port_a.borrow_mut().data = data[0]; self.port_a.notify(); },
reg::OUTPUT_A_NHS => {
self.port_a.borrow_mut().data = data[0];
self.port_a.notify();
},
_ => {
log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr);
},
@ -102,7 +137,6 @@ impl Addressable for Mos6522 {
impl Steppable for Mos6522 {
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
Ok(Duration::from_micros(16_600))
}
}
@ -117,4 +151,3 @@ impl Transmutable for Mos6522 {
Some(self)
}
}

View File

@ -1,4 +1,2 @@
mod mc68681;
pub use crate::mc68681::MC68681;

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Duration, Frequency};
use moa_core::{System, Error, Address, Steppable, Addressable, Transmutable};
@ -95,15 +94,23 @@ impl MC68681Port {
pub fn set_tx_status(&mut self, value: bool) {
match value {
true => { self.status |= SR_TX_READY | SR_TX_EMPTY; },
false => { self.status &= !(SR_TX_READY | SR_TX_EMPTY); },
true => {
self.status |= SR_TX_READY | SR_TX_EMPTY;
},
false => {
self.status &= !(SR_TX_READY | SR_TX_EMPTY);
},
}
}
pub fn set_rx_status(&mut self, value: bool) {
match value {
true => { self.status |= SR_RX_READY; },
false => { self.status &= !SR_RX_READY; },
true => {
self.status |= SR_RX_READY;
},
false => {
self.status &= !SR_RX_READY;
},
}
}
@ -126,7 +133,7 @@ impl MC68681Port {
}
pub fn handle_command(&mut self, data: u8) -> Option<bool> {
let rx_cmd = data& 0x03;
let rx_cmd = data & 0x03;
if rx_cmd == 0b01 {
self.rx_enabled = true;
} else if rx_cmd == 0b10 {
@ -202,7 +209,9 @@ impl MC68681 {
}
fn check_interrupt_state(&mut self, system: &System) -> Result<(), Error> {
system.get_interrupt_controller().set((self.int_status & self.int_mask) != 0, 4, self.int_vector)
system
.get_interrupt_controller()
.set((self.int_status & self.int_mask) != 0, 4, self.int_vector)
}
}
@ -254,17 +263,13 @@ impl Addressable for MC68681 {
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
match addr {
REG_SRA_RD => {
data[0] = self.port_a.status
},
REG_SRA_RD => data[0] = self.port_a.status,
REG_RBA_RD => {
data[0] = self.port_a.input;
self.port_a.set_rx_status(false);
self.set_interrupt_flag(ISR_CH_A_RX_READY_FULL, false);
},
REG_SRB_RD => {
data[0] = self.port_b.status
},
REG_SRB_RD => data[0] = self.port_b.status,
REG_RBB_RD => {
data[0] = self.port_b.input;
self.port_b.set_rx_status(false);
@ -294,7 +299,7 @@ impl Addressable for MC68681 {
}
self.set_interrupt_flag(ISR_TIMER_CHANGE, false);
},
_ => { },
_ => {},
}
if addr != REG_SRA_RD && addr != REG_SRB_RD {
@ -312,7 +317,7 @@ impl Addressable for MC68681 {
},
REG_ACR_WR => {
self.acr = data[0];
}
},
REG_TBA_WR => {
log::debug!("{}a: write {}", DEV_NAME, data[0] as char);
self.port_a.send_byte(data[0]);
@ -354,7 +359,7 @@ impl Addressable for MC68681 {
REG_OUT_RESET => {
self.output_state &= !data[0];
},
_ => { },
_ => {},
}
Ok(())
}

View File

@ -1,7 +1,5 @@
mod sn76489;
pub use crate::sn76489::Sn76489;
mod ym2612;
pub use crate::ym2612::Ym2612;

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Duration, Frequency};
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
@ -96,7 +95,7 @@ pub struct Sn76489 {
impl Sn76489 {
pub fn new<H, E>(host: &mut H, _clock_frequency: Frequency) -> Result<Self, HostError<E>>
where
H: Host<Error = E>
H: Host<Error = E>,
{
let source = host.add_audio_source()?;
let sample_rate = source.samples_per_second();
@ -134,7 +133,7 @@ impl Steppable for Sn76489 {
}
self.source.write_samples(system.clock, &buffer);
Ok(Duration::from_millis(1)) // Every 1ms of simulated time
Ok(Duration::from_millis(1)) // Every 1ms of simulated time
}
}
@ -163,7 +162,9 @@ impl Addressable for Sn76489 {
5 => self.tones[2].set_attenuation(value),
6 => self.noise.set_control(value),
7 => self.noise.set_attenuation(value),
_ => { self.first_byte = Some(data[0]); },
_ => {
self.first_byte = Some(data[0]);
},
}
} else {
let first = self.first_byte.unwrap_or(0);
@ -173,7 +174,7 @@ impl Addressable for Sn76489 {
0 => self.tones[0].set_counter(value),
2 => self.tones[1].set_counter(value),
4 => self.tones[2].set_counter(value),
_ => { },
_ => {},
}
}
log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
@ -190,4 +191,3 @@ impl Transmutable for Sn76489 {
Some(self)
}
}

View File

@ -28,6 +28,7 @@ use moa_host::{Host, HostError, Audio, Sample};
///
/// The value here is used to shift a bit to get the number of global cycles between each increment
/// of the envelope attenuation, based on the rate that's currently active
#[rustfmt::skip]
const COUNTER_SHIFT_VALUES: &[u16] = &[
11, 11, 11, 11,
10, 10, 10, 10,
@ -53,6 +54,7 @@ const COUNTER_SHIFT_VALUES: &[u16] = &[
/// than attenuation, and the values will always be below 64. This table maps each of the 64
/// possible angle values to a sequence of 8 cycles, and the amount to increment the attenuation
/// at each point in that cycle
#[rustfmt::skip]
const RATE_TABLE: &[u16] = &[
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@ -120,6 +122,7 @@ const RATE_TABLE: &[u16] = &[
8, 8, 8, 8, 8, 8, 8, 8,
];
#[rustfmt::skip]
const DETUNE_TABLE: &[u8] = &[
0, 0, 1, 2,
0, 0, 1, 2,
@ -271,9 +274,6 @@ impl EnvelopeGenerator {
let rate = self.get_scaled_rate(self.envelope_state, rate_adjust);
let counter_shift = COUNTER_SHIFT_VALUES[rate];
//if self.debug_name == "ch 2, op 0" {
//println!("{:4x} {:4x} {:4x}", envelope_clock, counter_shift, envelope_clock % (1 << counter_shift));
//}
if envelope_clock % (1 << counter_shift) == 0 {
let update_cycle = (envelope_clock >> counter_shift) & 0x07;
let increment = RATE_TABLE[rate * 8 + update_cycle as usize];
@ -286,9 +286,6 @@ impl EnvelopeGenerator {
// to bitwise-and with 0xFFC instead, which will wrap the number to a 12-bit signed number, which when
// clamped to MAX_ENVELOPE will produce the same results
let new_envelope = self.envelope + (((!self.envelope * increment) as i16) >> 4) as u16;
//if self.debug_name == "ch 2, op 0" {
//println!("{:4x} {:4x} {:4x} {:4x} {:4x}", self.envelope, update_cycle, rate * 8 + update_cycle as usize, (((!self.envelope * increment) as i16) >> 4) as u16 & 0xFFFC, new_envelope);
//}
if new_envelope > self.envelope {
self.envelope_state = EnvelopeState::Decay;
self.envelope = 0;
@ -296,19 +293,16 @@ impl EnvelopeGenerator {
self.envelope = new_envelope.min(MAX_ENVELOPE);
}
},
EnvelopeState::Decay |
EnvelopeState::Sustain |
EnvelopeState::Release => {
EnvelopeState::Decay | EnvelopeState::Sustain | EnvelopeState::Release => {
// Convert it to a fixed point decimal number of 4 bit : 8 bits, which will be the output
self.envelope += increment << 2;
if self.envelope > MAX_ENVELOPE || self.envelope_state == EnvelopeState::Release && self.envelope >= ENVELOPE_CENTER {
if self.envelope > MAX_ENVELOPE
|| self.envelope_state == EnvelopeState::Release && self.envelope >= ENVELOPE_CENTER
{
self.envelope = MAX_ENVELOPE;
}
},
}
//if self.debug_name == "ch 2, op 0" {
//println!("{:4x} {:4x} {:4x} {:4x} {:4x}", rate, counter_shift, self.envelope_state as usize, increment, self.envelope);
//}
}
}
@ -411,7 +405,8 @@ impl PhaseGenerator {
increment.saturating_add(detune)
} else {
increment.saturating_sub(detune)
}.min(0x1FFFF);
}
.min(0x1FFFF);
// Apply multiple
let increment = if self.multiple == 0 {
@ -440,6 +435,7 @@ impl PhaseGenerator {
///
/// K1 = F11
/// K0 = F11 & (F10 | F9 | F8) | !F11 & (F10 & F9 & F8)
#[rustfmt::skip]
const FNUMBER_TO_KEYCODE: &[u8] = &[
0, 0, 0, 0, 0, 0, 0, 1,
2, 3, 3, 3, 3, 3, 3, 3,
@ -507,7 +503,8 @@ impl Operator {
}
fn notify_key_change(&mut self, state: bool, envelope_clock: EnvelopeClock) {
self.envelope.notify_key_change(state, envelope_clock, self.phase.get_rate_adjust());
self.envelope
.notify_key_change(state, envelope_clock, self.phase.get_rate_adjust());
self.phase.reset();
}
@ -519,10 +516,6 @@ impl Operator {
let mod_phase = phase + modulator;
//if self.debug_name == "ch 2, op 0" {
//println!("{:4x} = {:4x} + {:4x} + {:4x}, e: {:x}, {:4x} {:4x}", mod_phase, phase, self.phase.increment, modulator, self.envelope.envelope_state as usize, envelope, self.envelope.envelope);
//}
// The sine table contains the first half of the wave as an attenuation value
// Use the phase with the sign truncated to get the attenuation, plus the
// attenuation from the envelope, to get the total attenuation at this point
@ -567,7 +560,9 @@ impl Channel {
Self {
debug_name: debug_name.clone(),
enabled: (true, true),
operators: (0..OPERATORS).map(|i| Operator::new(format!("{}, op {}", debug_name, i))).collect(),
operators: (0..OPERATORS)
.map(|i| Operator::new(format!("{}, op {}", debug_name, i)))
.collect(),
algorithm: OperatorAlgorithm::A0,
feedback: 0,
@ -617,10 +612,6 @@ impl Channel {
//let output = sign_extend_u16(output, 14);
//let output = output * 2 / 3;
//if self.debug_name == "ch 2" {
//println!("{:6x}", output);
//}
let sample = output as f32 / (1 << 13) as f32;
let left = if self.enabled.0 { sample } else { 0.0 };
@ -663,7 +654,9 @@ impl Channel {
},
OperatorAlgorithm::A5 => {
let output1 = self.operators[0].get_output(feedback, clocks);
self.operators[1].get_output(output1, clocks) + self.operators[2].get_output(output1, clocks) + self.operators[3].get_output(output1, clocks)
self.operators[1].get_output(output1, clocks)
+ self.operators[2].get_output(output1, clocks)
+ self.operators[3].get_output(output1, clocks)
},
OperatorAlgorithm::A6 => {
let output1 = self.operators[0].get_output(feedback, clocks);
@ -672,9 +665,9 @@ impl Channel {
},
OperatorAlgorithm::A7 => {
self.operators[0].get_output(feedback, clocks)
+ self.operators[1].get_output(0, clocks)
+ self.operators[2].get_output(0, clocks)
+ self.operators[3].get_output(0, clocks)
+ self.operators[1].get_output(0, clocks)
+ self.operators[2].get_output(0, clocks)
+ self.operators[3].get_output(0, clocks)
},
}
}
@ -808,7 +801,7 @@ impl Steppable for Ym2612 {
}
self.source.write_samples(system.clock, &buffer);
Ok(Duration::from_millis(1)) // Every 1ms of simulated time
Ok(Duration::from_millis(1)) // Every 1ms of simulated time
}
}
@ -919,7 +912,7 @@ impl Ym2612 {
self.channels[ch].operators[op].set_rate(EnvelopeState::Decay, first_decay_rate);
},
reg if is_reg_range(reg, 0x70)=> {
reg if is_reg_range(reg, 0x70) => {
let (ch, op) = get_ch_op(bank, reg);
let index = get_index(bank, reg);
@ -938,7 +931,11 @@ impl Ym2612 {
// Register is 4 bits, so adjust it to match total_level's scale
let sustain_level = (self.registers[0x80 + index] as u16 & 0xF0) << 3;
// Adjust the maximum storable value to be the max attenuation
let sustain_level = if sustain_level == (0x00F0 << 3) { MAX_ENVELOPE } else { sustain_level };
let sustain_level = if sustain_level == (0x00F0 << 3) {
MAX_ENVELOPE
} else {
sustain_level
};
self.channels[ch].operators[op].set_sustain_level(sustain_level);
},
@ -1033,7 +1030,7 @@ impl Addressable for Ym2612 {
0..=3 => {
// Read the status byte (busy/overflow)
data[0] = ((self.timer_a_overflow as u8) << 1) | (self.timer_b_overflow as u8);
}
},
_ => {
log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
},
@ -1078,4 +1075,3 @@ impl Transmutable for Ym2612 {
Some(self)
}
}

View File

@ -1,4 +1,2 @@
mod z8530;
pub use crate::z8530::Z8530;

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Duration};
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
@ -6,9 +5,7 @@ use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
const DEV_NAME: &str = "z8530";
#[derive(Default)]
pub struct Z8530 {
}
pub struct Z8530 {}
impl Addressable for Z8530 {
fn size(&self) -> usize {
@ -30,7 +27,6 @@ impl Addressable for Z8530 {
impl Steppable for Z8530 {
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
Ok(Duration::from_secs(1))
}
}
@ -44,5 +40,3 @@ impl Transmutable for Z8530 {
Some(self)
}
}

View File

@ -1,4 +1,2 @@
mod system;
pub use crate::system::{build_computie, build_computie_k30, launch_terminal_emulator, launch_slip_connection, ComputieOptions};

View File

@ -1,4 +1,3 @@
use femtos::Frequency;
use moa_core::{System, Error, Debuggable, MemoryBlock, Device};
@ -47,15 +46,6 @@ pub fn build_computie<H: Host>(host: &H, options: ComputieOptions) -> Result<Sys
let mut cpu = M68k::from_type(M68kType::MC68010, options.frequency);
//cpu.enable_tracing();
//cpu.add_breakpoint(0x10781a);
//cpu.add_breakpoint(0x10bc9c);
//cpu.add_breakpoint(0x106a94);
//cpu.add_breakpoint(0x1015b2);
//cpu.add_breakpoint(0x103332);
//cpu.decoder.dump_disassembly(&mut system, 0x100000, 0x2000);
//cpu.decoder.dump_disassembly(&mut system, 0x2ac, 0x200);
cpu.add_breakpoint(0);
system.add_interruptable_device("cpu", Device::new(cpu))?;
@ -85,15 +75,6 @@ pub fn build_computie_k30<H: Host>(host: &H) -> Result<System, Error> {
let cpu = M68k::from_type(M68kType::MC68030, Frequency::from_hz(10_000_000));
//cpu.enable_tracing();
//cpu.add_breakpoint(0x10781a);
//cpu.add_breakpoint(0x10bc9c);
//cpu.add_breakpoint(0x106a94);
//cpu.add_breakpoint(0x1015b2);
//cpu.add_breakpoint(0x103332);
//cpu.decoder.dump_disassembly(&mut system, 0x100000, 0x2000);
//cpu.decoder.dump_disassembly(&mut system, 0x2ac, 0x200);
system.add_interruptable_device("cpu", Device::new(cpu))?;
Ok(system)
@ -104,18 +85,39 @@ pub fn launch_terminal_emulator(name: String) {
use std::time::Duration;
use std::process::Command;
Command::new("x-terminal-emulator").arg("-e").arg(&format!("pyserial-miniterm {}", name)).spawn().unwrap();
Command::new("x-terminal-emulator")
.arg("-e")
.arg(&format!("pyserial-miniterm {}", name))
.spawn()
.unwrap();
thread::sleep(Duration::from_secs(1));
}
pub fn launch_slip_connection(name: String) {
use std::process::Command;
Command::new("sudo").args(["slattach", "-s", "38400", "-p", "slip", &name]).spawn().unwrap();
Command::new("sudo").args(["ifconfig", "sl0", "192.168.1.2", "pointopoint", "192.168.1.200", "up"]).status().unwrap();
Command::new("sudo").args(["arp", "-Ds", "192.168.1.200", "enp4s0", "pub"]).status().unwrap();
Command::new("sudo").args(["iptables", "-A", "FORWARD", "-i", "sl0", "-j", "ACCEPT"]).status().unwrap();
Command::new("sudo").args(["iptables", "-A", "FORWARD", "-o", "sl0", "-j", "ACCEPT"]).status().unwrap();
Command::new("sudo").args(["sh", "-c", "echo 1 > /proc/sys/net/ipv4/ip_forward"]).status().unwrap();
Command::new("sudo")
.args(["slattach", "-s", "38400", "-p", "slip", &name])
.spawn()
.unwrap();
Command::new("sudo")
.args(["ifconfig", "sl0", "192.168.1.2", "pointopoint", "192.168.1.200", "up"])
.status()
.unwrap();
Command::new("sudo")
.args(["arp", "-Ds", "192.168.1.200", "enp4s0", "pub"])
.status()
.unwrap();
Command::new("sudo")
.args(["iptables", "-A", "FORWARD", "-i", "sl0", "-j", "ACCEPT"])
.status()
.unwrap();
Command::new("sudo")
.args(["iptables", "-A", "FORWARD", "-o", "sl0", "-j", "ACCEPT"])
.status()
.unwrap();
Command::new("sudo")
.args(["sh", "-c", "echo 1 > /proc/sys/net/ipv4/ip_forward"])
.status()
.unwrap();
}

View File

@ -1,7 +1,5 @@
pub mod peripherals;
pub mod utils;
mod system;
pub use crate::system::{SegaGenesisOptions, build_genesis};

View File

@ -1,20 +1,19 @@
use femtos::{Instant, Duration};
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
use moa_host::{self, Host, HostError, ControllerDevice, ControllerInput, ControllerEvent, EventReceiver};
use moa_signals::{Signal};
const REG_VERSION: Address = 0x01;
const REG_DATA1: Address = 0x03;
const REG_DATA2: Address = 0x05;
const REG_DATA3: Address = 0x07;
const REG_CTRL1: Address = 0x09;
const REG_CTRL2: Address = 0x0B;
const REG_CTRL3: Address = 0x0D;
const REG_S_CTRL1: Address = 0x13;
const REG_S_CTRL2: Address = 0x19;
const REG_S_CTRL3: Address = 0x1F;
const REG_VERSION: Address = 0x01;
const REG_DATA1: Address = 0x03;
const REG_DATA2: Address = 0x05;
const REG_DATA3: Address = 0x07;
const REG_CTRL1: Address = 0x09;
const REG_CTRL2: Address = 0x0B;
const REG_CTRL3: Address = 0x0D;
const REG_S_CTRL1: Address = 0x13;
const REG_S_CTRL2: Address = 0x19;
const REG_S_CTRL3: Address = 0x1F;
const DEV_NAME: &str = "genesis_controller";
@ -50,13 +49,13 @@ impl GenesisControllerPort {
let th_state = (self.outputs & 0x40) != 0;
match (th_state, self.th_count) {
(true, 0) => self.outputs | ((inputs & 0x003F) as u8),
(true, 0) => self.outputs | ((inputs & 0x003F) as u8),
(false, 1) => self.outputs | (((inputs & 0x00C0) >> 2) as u8) | ((inputs & 0x0003) as u8),
(true, 1) => self.outputs | ((inputs & 0x003F) as u8),
(true, 1) => self.outputs | ((inputs & 0x003F) as u8),
(false, 2) => self.outputs | (((inputs & 0x00C0) >> 2) as u8),
(true, 2) => self.outputs | ((inputs & 0x0030) as u8) | (((inputs & 0x0F00) >> 8) as u8),
(true, 2) => self.outputs | ((inputs & 0x0030) as u8) | (((inputs & 0x0F00) >> 8) as u8),
(false, 3) => self.outputs | (((inputs & 0x00C0) >> 2) as u8) | 0x0F,
(true, 3) => self.outputs | ((inputs & 0x003F) as u8),
(true, 3) => self.outputs | ((inputs & 0x003F) as u8),
(false, 0) => self.outputs | (((inputs & 0x00C0) >> 2) as u8) | ((inputs & 0x0003) as u8),
_ => 0,
}
@ -159,17 +158,39 @@ impl Addressable for GenesisControllers {
}
match addr {
REG_VERSION => { data[i] = 0xA0; } // Overseas Version, NTSC, No Expansion
REG_DATA1 => { data[i] = self.port_1.get_data(); },
REG_DATA2 => { data[i] = self.port_2.get_data(); },
REG_DATA3 => { data[i] = self.expansion.get_data(); },
REG_CTRL1 => { data[i] = self.port_1.ctrl; },
REG_CTRL2 => { data[i] = self.port_2.ctrl; },
REG_CTRL3 => { data[i] = self.expansion.ctrl; },
REG_S_CTRL1 => { data[i] = self.port_1.s_ctrl | 0x02; },
REG_S_CTRL2 => { data[i] = self.port_2.s_ctrl | 0x02; },
REG_S_CTRL3 => { data[i] = self.expansion.s_ctrl | 0x02; },
_ => { log::warn!("{}: !!! unhandled reading from {:0x}", DEV_NAME, addr); },
REG_VERSION => {
data[i] = 0xA0;
}, // Overseas Version, NTSC, No Expansion
REG_DATA1 => {
data[i] = self.port_1.get_data();
},
REG_DATA2 => {
data[i] = self.port_2.get_data();
},
REG_DATA3 => {
data[i] = self.expansion.get_data();
},
REG_CTRL1 => {
data[i] = self.port_1.ctrl;
},
REG_CTRL2 => {
data[i] = self.port_2.ctrl;
},
REG_CTRL3 => {
data[i] = self.expansion.ctrl;
},
REG_S_CTRL1 => {
data[i] = self.port_1.s_ctrl | 0x02;
},
REG_S_CTRL2 => {
data[i] = self.port_2.s_ctrl | 0x02;
},
REG_S_CTRL3 => {
data[i] = self.expansion.s_ctrl | 0x02;
},
_ => {
log::warn!("{}: !!! unhandled reading from {:0x}", DEV_NAME, addr);
},
}
log::info!("{}: read from register {:x} the value {:x}", DEV_NAME, addr, data[0]);
Ok(())
@ -180,16 +201,36 @@ impl Addressable for GenesisControllers {
log::info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
match addr {
REG_DATA1 => { self.port_1.set_data(data[0]); }
REG_DATA2 => { self.port_2.set_data(data[0]); },
REG_DATA3 => { self.expansion.set_data(data[0]); },
REG_CTRL1 => { self.port_1.set_ctrl(data[0]); },
REG_CTRL2 => { self.port_2.set_ctrl(data[0]); },
REG_CTRL3 => { self.expansion.set_ctrl(data[0]); },
REG_S_CTRL1 => { self.port_1.s_ctrl = data[0] & 0xF8; },
REG_S_CTRL2 => { self.port_2.s_ctrl = data[0] & 0xF8; },
REG_S_CTRL3 => { self.expansion.s_ctrl = data[0] & 0xF8; },
_ => { log::warn!("{}: !!! unhandled write of {:0x} to {:0x}", DEV_NAME, data[0], addr); },
REG_DATA1 => {
self.port_1.set_data(data[0]);
},
REG_DATA2 => {
self.port_2.set_data(data[0]);
},
REG_DATA3 => {
self.expansion.set_data(data[0]);
},
REG_CTRL1 => {
self.port_1.set_ctrl(data[0]);
},
REG_CTRL2 => {
self.port_2.set_ctrl(data[0]);
},
REG_CTRL3 => {
self.expansion.set_ctrl(data[0]);
},
REG_S_CTRL1 => {
self.port_1.s_ctrl = data[0] & 0xF8;
},
REG_S_CTRL2 => {
self.port_2.s_ctrl = data[0] & 0xF8;
},
REG_S_CTRL3 => {
self.expansion.s_ctrl = data[0] & 0xF8;
},
_ => {
log::warn!("{}: !!! unhandled write of {:0x} to {:0x}", DEV_NAME, data[0], addr);
},
}
Ok(())
}
@ -197,7 +238,7 @@ impl Addressable for GenesisControllers {
impl Steppable for GenesisControllers {
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
let duration = Duration::from_micros(100); // Update every 100us
let duration = Duration::from_micros(100); // Update every 100us
while let Some(event) = self.receiver.receive() {
self.process_event(event);
@ -222,5 +263,3 @@ impl Transmutable for GenesisControllers {
Some(self)
}
}

View File

@ -1,4 +1,3 @@
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use femtos::Instant;
@ -31,9 +30,15 @@ impl Addressable for CoprocessorCoordinator {
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
match addr {
0x100 => {
data[0] = if self.bus_request.get() && self.reset.get() { 0x01 } else { 0x00 };
data[0] = if self.bus_request.get() && self.reset.get() {
0x01
} else {
0x00
};
},
_ => {
log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
},
_ => { log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); },
}
log::info!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data);
Ok(())
@ -49,7 +54,9 @@ impl Addressable for CoprocessorCoordinator {
0x200 => {
self.reset.set(data[0] == 0);
},
_ => { log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); },
_ => {
log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr);
},
}
Ok(())
}
@ -101,7 +108,9 @@ pub struct CoprocessorBankArea {
impl CoprocessorBankArea {
pub fn new(bus: Rc<RefCell<Bus>>) -> (Self, CoprocessorBankRegister) {
let base = Rc::new(Cell::new(0));
let register = CoprocessorBankRegister { base: base.clone() };
let register = CoprocessorBankRegister {
base: base.clone(),
};
let bank = Self {
base,
bus,
@ -129,4 +138,3 @@ impl Transmutable for CoprocessorBankArea {
Some(self)
}
}

View File

@ -1,5 +1,3 @@
pub mod ym7101;
pub mod controllers;
pub mod coprocessor;
pub mod ym7101;

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Duration, Frequency};
use moa_core::{System, Error, Address, Addressable, Steppable, Inspectable, Transmutable, Device, read_beu16, dump_slice};
@ -171,7 +170,13 @@ impl Ym7101Memory {
4 => Memory::Vsram,
_ => Memory::Cram,
};
log::debug!("{}: transfer requested of type {:x} ({:?}) to address {:x}", DEV_NAME, self.transfer_type, self.transfer_target, self.transfer_dest_addr);
log::debug!(
"{}: transfer requested of type {:x} ({:?}) to address {:x}",
DEV_NAME,
self.transfer_type,
self.transfer_target,
self.transfer_dest_addr
);
if (self.transfer_type & 0x20) != 0 {
if (self.transfer_type & 0x10) != 0 {
self.set_dma_mode(DmaType::Copy);
@ -198,7 +203,15 @@ impl Ym7101Memory {
}
}
self.transfer_dest_addr += self.transfer_auto_inc;
log::debug!("{}: data port read {} bytes from {:?}:{:x} returning {:x},{:x}", DEV_NAME, data.len(), self.transfer_target, addr, data[0], data[1]);
log::debug!(
"{}: data port read {} bytes from {:?}:{:x} returning {:x},{:x}",
DEV_NAME,
data.len(),
self.transfer_target,
addr,
data[0],
data[1]
);
Ok(())
}
@ -208,7 +221,14 @@ impl Ym7101Memory {
self.transfer_fill_word = if data.len() >= 2 { read_beu16(data) } else { data[0] as u16 };
self.set_dma_mode(DmaType::Fill);
} else {
log::debug!("{}: data port write {} bytes to {:?}:{:x} with {:?}", DEV_NAME, data.len(), self.transfer_target, self.transfer_dest_addr, data);
log::debug!(
"{}: data port write {} bytes to {:?}:{:x} with {:?}",
DEV_NAME,
data.len(),
self.transfer_target,
self.transfer_dest_addr,
data
);
{
let addr = self.transfer_dest_addr as usize;
@ -225,10 +245,12 @@ impl Ym7101Memory {
fn write_control_port(&mut self, data: &[u8]) -> Result<(), Error> {
let value = read_beu16(data);
match (data.len(), self.ctrl_port_buffer) {
(2, None) => { self.ctrl_port_buffer = Some(value) },
(2, None) => self.ctrl_port_buffer = Some(value),
(2, Some(upper)) => self.setup_transfer(upper, read_beu16(data)),
(4, None) => self.setup_transfer(value, read_beu16(&data[2..])),
_ => { log::error!("{}: !!! error when writing to control port with {} bytes of {:?}", DEV_NAME, data.len(), data); },
_ => {
log::error!("{}: !!! error when writing to control port with {} bytes of {:?}", DEV_NAME, data.len(), data);
},
}
Ok(())
}
@ -239,7 +261,15 @@ impl Ym7101Memory {
match self.transfer_run {
DmaType::Memory => {
log::debug!("{}: starting dma transfer {:x} from Mem:{:x} to {:?}:{:x} ({} bytes)", DEV_NAME, self.transfer_type, self.transfer_src_addr, self.transfer_target, self.transfer_dest_addr, self.transfer_remain);
log::debug!(
"{}: starting dma transfer {:x} from Mem:{:x} to {:?}:{:x} ({} bytes)",
DEV_NAME,
self.transfer_type,
self.transfer_src_addr,
self.transfer_target,
self.transfer_dest_addr,
self.transfer_remain
);
let mut bus = system.get_bus();
while self.transfer_remain > 0 {
@ -257,7 +287,13 @@ impl Ym7101Memory {
}
},
DmaType::Copy => {
log::debug!("{}: starting dma copy from VRAM:{:x} to VRAM:{:x} ({} bytes)", DEV_NAME, self.transfer_src_addr, self.transfer_dest_addr, self.transfer_remain);
log::debug!(
"{}: starting dma copy from VRAM:{:x} to VRAM:{:x} ({} bytes)",
DEV_NAME,
self.transfer_src_addr,
self.transfer_dest_addr,
self.transfer_remain
);
while self.transfer_remain > 0 {
self.vram[self.transfer_dest_addr as usize] = self.vram[self.transfer_src_addr as usize];
self.transfer_dest_addr += self.transfer_auto_inc;
@ -266,14 +302,22 @@ impl Ym7101Memory {
}
},
DmaType::Fill => {
log::debug!("{}: starting dma fill to VRAM:{:x} ({} bytes) with {:x}", DEV_NAME, self.transfer_dest_addr, self.transfer_remain, self.transfer_fill_word);
log::debug!(
"{}: starting dma fill to VRAM:{:x} ({} bytes) with {:x}",
DEV_NAME,
self.transfer_dest_addr,
self.transfer_remain,
self.transfer_fill_word
);
while self.transfer_remain > 0 {
self.vram[self.transfer_dest_addr as usize] = self.transfer_fill_word as u8;
self.transfer_dest_addr += self.transfer_auto_inc;
self.transfer_remain -= 1;
}
},
_ => { log::warn!("{}: !!! error unexpected transfer mode {:x}", DEV_NAME, self.transfer_type); },
_ => {
log::warn!("{}: !!! error unexpected transfer mode {:x}", DEV_NAME, self.transfer_type);
},
}
self.set_dma_mode(DmaType::None);
@ -426,7 +470,12 @@ impl Ym7101State {
Pixel::Rgb(((rgb & 0x00F) << 4) as u8, (rgb & 0x0F0) as u8, ((rgb & 0xF00) >> 4) as u8).encode(encoding)
} else {
let offset = if mode == ColourMode::Highlight { 0x80 } else { 0x00 };
Pixel::Rgb(((rgb & 0x00F) << 3) as u8 | offset, ((rgb & 0x0F0) >> 1) as u8 | offset, ((rgb & 0xF00) >> 5) as u8 | offset).encode(encoding)
Pixel::Rgb(
((rgb & 0x00F) << 3) as u8 | offset,
((rgb & 0x0F0) >> 1) as u8 | offset,
((rgb & 0xF00) >> 5) as u8 | offset,
)
.encode(encoding)
}
}
@ -631,7 +680,18 @@ impl Sprite {
}
fn calculate_pattern(&self, cell_x: usize, cell_y: usize) -> u16 {
let (h, v) = (if !self.rev.0 { cell_x } else { self.size.0 as usize - 1 - cell_x }, if !self.rev.1 { cell_y } else { self.size.1 as usize - 1 - cell_y });
let (h, v) = (
if !self.rev.0 {
cell_x
} else {
self.size.0 as usize - 1 - cell_x
},
if !self.rev.1 {
cell_y
} else {
self.size.1 as usize - 1 - cell_y
},
);
(self.pattern & 0xF800) | ((self.pattern & 0x07FF) + (h as u16 * self.size.1) + v as u16)
}
}
@ -664,7 +724,7 @@ impl Steppable for Ym7101 {
self.state.current_y += 1;
self.state.h_scanlines = self.state.h_scanlines.wrapping_sub(1);
if self.state.hsync_int_enabled() && self.state.h_scanlines == 0 {
if self.state.hsync_int_enabled() && self.state.h_scanlines == 0 {
self.state.h_scanlines = self.state.h_int_lines;
system.get_interrupt_controller().set(true, 4, 28)?;
}
@ -686,7 +746,8 @@ impl Steppable for Ym7101 {
}
if (self.state.mode_1 & mode1::BF_DISABLE_DISPLAY) == 0 && self.state.screen_size != (0, 0) {
let mut frame = Frame::new(self.state.screen_size.0 as u32 * 8, self.state.screen_size.1 as u32 * 8, self.sender.encoding());
let mut frame =
Frame::new(self.state.screen_size.0 as u32 * 8, self.state.screen_size.1 as u32 * 8, self.sender.encoding());
self.state.draw_frame(&mut frame);
self.sender.add(system.clock, frame);
}
@ -699,7 +760,12 @@ impl Steppable for Ym7101 {
if (self.state.mode_2 & mode2::BF_DMA_ENABLED) != 0 {
self.state.memory.step_dma(system)?;
self.state.status = (self.state.status & !status::DMA_BUSY) | (if self.state.memory.transfer_dma_busy { status::DMA_BUSY } else { 0 });
self.state.status = (self.state.status & !status::DMA_BUSY)
| (if self.state.memory.transfer_dma_busy {
status::DMA_BUSY
} else {
0
});
}
Ok(Frequency::from_hz(13_423_294).period_duration() * 4)
@ -742,24 +808,44 @@ impl Ym7101 {
fn update_register_value(&mut self, reg: usize, data: u8) {
match reg {
reg::MODE_SET_1 => { self.state.mode_1 = data; },
reg::MODE_SET_1 => {
self.state.mode_1 = data;
},
reg::MODE_SET_2 => {
self.state.mode_2 = data;
self.state.update_screen_size();
},
reg::SCROLL_A_ADDR => { self.state.scroll_a_addr = (data as usize) << 10; },
reg::WINDOW_ADDR => { self.state.window_addr = (data as usize) << 10; },
reg::SCROLL_B_ADDR => { self.state.scroll_b_addr = (data as usize) << 13; },
reg::SPRITES_ADDR => { self.state.sprites_addr = (data as usize) << 9; },
reg::BACKGROUND => { self.state.background = data; },
reg::H_INTERRUPT => { self.state.h_int_lines = data; },
reg::MODE_SET_3 => { self.state.mode_3 = data; },
reg::SCROLL_A_ADDR => {
self.state.scroll_a_addr = (data as usize) << 10;
},
reg::WINDOW_ADDR => {
self.state.window_addr = (data as usize) << 10;
},
reg::SCROLL_B_ADDR => {
self.state.scroll_b_addr = (data as usize) << 13;
},
reg::SPRITES_ADDR => {
self.state.sprites_addr = (data as usize) << 9;
},
reg::BACKGROUND => {
self.state.background = data;
},
reg::H_INTERRUPT => {
self.state.h_int_lines = data;
},
reg::MODE_SET_3 => {
self.state.mode_3 = data;
},
reg::MODE_SET_4 => {
self.state.mode_4 = data;
self.state.update_screen_size();
},
reg::HSCROLL_ADDR => { self.state.hscroll_addr = (data as usize) << 10; },
reg::AUTO_INCREMENT => { self.state.memory.transfer_auto_inc = data as u32; },
reg::HSCROLL_ADDR => {
self.state.hscroll_addr = (data as usize) << 10;
},
reg::AUTO_INCREMENT => {
self.state.memory.transfer_auto_inc = data as u32;
},
reg::SCROLL_SIZE => {
let h = decode_scroll_size(data & 0x03);
let v = decode_scroll_size((data >> 4) & 0x03);
@ -790,10 +876,13 @@ impl Ym7101 {
reg::DMA_ADDR_HIGH => {
let mask = if (data & 0x80) == 0 { 0x7F } else { 0x3F };
self.state.memory.transfer_bits = data & 0xC0;
self.state.memory.transfer_src_addr = (self.state.memory.transfer_src_addr & 0x01FFFF) | (((data & mask) as u32) << 17);
self.state.memory.transfer_src_addr =
(self.state.memory.transfer_src_addr & 0x01FFFF) | (((data & mask) as u32) << 17);
},
0x6 | 0x8 | 0x9 | 0xE => { /* Reserved */ },
_ => { panic!("{}: unknown register: {:?}", DEV_NAME, reg); },
_ => {
panic!("{}: unknown register: {:?}", DEV_NAME, reg);
},
}
}
}
@ -838,7 +927,9 @@ impl Addressable for Ym7101 {
}
},
_ => { println!("{}: !!! unhandled read from {:x}", DEV_NAME, addr); },
_ => {
println!("{}: !!! unhandled read from {:x}", DEV_NAME, addr);
},
}
Ok(())
}
@ -864,7 +955,12 @@ impl Addressable for Ym7101 {
}
} else {
self.state.memory.write_control_port(data)?;
self.state.status = (self.state.status & !status::DMA_BUSY) | (if self.state.memory.transfer_dma_busy { status::DMA_BUSY } else { 0 });
self.state.status = (self.state.status & !status::DMA_BUSY)
| (if self.state.memory.transfer_dma_busy {
status::DMA_BUSY
} else {
0
});
}
},
@ -872,7 +968,9 @@ impl Addressable for Ym7101 {
self.sn_sound.borrow_mut().as_addressable().unwrap().write(clock, 0, data)?;
},
_ => { log::warn!("{}: !!! unhandled write to {:x} with {:?}", DEV_NAME, addr, data); },
_ => {
log::warn!("{}: !!! unhandled write to {:x} with {:?}", DEV_NAME, addr, data);
},
}
Ok(())
}
@ -905,7 +1003,7 @@ impl Inspectable for Ym7101 {
"vsram" => {
self.state.dump_vsram();
},
_ => { },
_ => {},
}
Ok(())
}
@ -941,4 +1039,3 @@ impl Ym7101State {
dump_slice(&self.memory.vsram, 80);
}
}

View File

@ -1,4 +1,3 @@
use std::mem;
use std::rc::Rc;
use std::cell::RefCell;
@ -99,4 +98,3 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
Ok(system)
}

View File

@ -1,4 +1,3 @@
use std::fs;
use moa_core::Error;
@ -39,5 +38,3 @@ pub fn load_rom_file(filename: &str) -> Result<Vec<u8>, Error> {
Ok(contents)
}

View File

@ -1,6 +1,4 @@
pub mod peripherals;
mod system;
pub use crate::system::{build_macintosh_512k};

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Duration};
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
@ -8,10 +7,10 @@ use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
//const CA1: u8 = 0x02;
//const CA2: u8 = 0x04;
//const LSTRB: u8 = 0x08;
const ENABLE: u8 = 0x10;
const ENABLE: u8 = 0x10;
//const SELECT: u8 = 0x20;
const Q6: u8 = 0x40;
const Q7: u8 = 0x80;
const Q6: u8 = 0x40;
const Q7: u8 = 0x80;
const DEV_NAME: &str = "iwm";
@ -61,7 +60,7 @@ impl Addressable for IWM {
Q7 => {
// read "write-handshake" register
data[i] = 0x3F | self.handshake;
}
},
b if b == (Q7 | Q6) => {
panic!("");
},
@ -111,4 +110,3 @@ impl Transmutable for IWM {
Some(self)
}
}

View File

@ -1,4 +1,3 @@
use std::rc::Rc;
use std::cell::RefCell;
use femtos::{Instant, Duration};
@ -47,16 +46,30 @@ impl Mainboard {
if (port.data & 0x10) == 0 {
println!("{}: overlay is 0 (normal)", DEV_NAME);
lower_bus.borrow_mut().clear_all_bus_devices();
lower_bus.borrow_mut().insert(0x000000, Device::new(AddressRepeater::new(ram.clone(), 0x400000)));
lower_bus.borrow_mut().insert(0x400000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
lower_bus.borrow_mut().insert(0x600000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
lower_bus
.borrow_mut()
.insert(0x000000, Device::new(AddressRepeater::new(ram.clone(), 0x400000)));
lower_bus
.borrow_mut()
.insert(0x400000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
lower_bus
.borrow_mut()
.insert(0x600000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
} else {
println!("{}: overlay is 1 (startup)", DEV_NAME);
lower_bus.borrow_mut().clear_all_bus_devices();
lower_bus.borrow_mut().insert(0x000000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
lower_bus.borrow_mut().insert(0x200000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
lower_bus.borrow_mut().insert(0x400000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
lower_bus.borrow_mut().insert(0x600000, Device::new(AddressRepeater::new(ram.clone(), 0x200000)));
lower_bus
.borrow_mut()
.insert(0x000000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
lower_bus
.borrow_mut()
.insert(0x200000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
lower_bus
.borrow_mut()
.insert(0x400000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
lower_bus
.borrow_mut()
.insert(0x600000, Device::new(AddressRepeater::new(ram.clone(), 0x200000)));
}
});
@ -139,9 +152,7 @@ impl Transmutable for Mainboard {
#[derive(Default)]
pub struct PhaseRead {
}
pub struct PhaseRead {}
impl Addressable for PhaseRead {
fn size(&self) -> usize {
@ -159,4 +170,3 @@ impl Addressable for PhaseRead {
Ok(())
}
}

View File

@ -1,5 +1,3 @@
pub mod iwm;
pub mod video;
pub mod mainboard;
pub mod video;

View File

@ -1,11 +1,10 @@
use femtos::Duration;
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
use moa_host::{self, Host, HostError, Frame, FrameSender, Pixel};
const SCRN_BASE: u32 = 0x07A700;
const SCRN_BASE: u32 = 0x07A700;
const SCRN_SIZE: (u32, u32) = (512, 342);
pub struct MacVideo {
@ -81,4 +80,3 @@ impl Transmutable for MacVideo {
Some(self)
}
}

View File

@ -1,4 +1,3 @@
use femtos::Frequency;
use moa_core::{System, Error, MemoryBlock, Debuggable, Device};
@ -80,7 +79,7 @@ pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
//cpu.add_breakpoint(0x400694); // Ram Test
//cpu.add_breakpoint(0x400170); // Failed, loops infinitely
cpu.add_breakpoint(0x4000f4); // Failed, should show the sad mac
cpu.add_breakpoint(0x4000f4); // Failed, should show the sad mac
//cpu.add_breakpoint(0x4006ae);
//cpu.add_breakpoint(0x400706);
//cpu.add_breakpoint(0x400722); // end of ram test
@ -96,7 +95,7 @@ pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
//cpu.add_breakpoint(0x40045c);
//cpu.add_breakpoint(0x400614); // Start of InitIO
cpu.add_breakpoint(0x40062a); // Loop in InitIO
cpu.add_breakpoint(0x40062a); // Loop in InitIO
//cpu.add_breakpoint(0x400648);
//cpu.add_breakpoint(0x40064c);
//cpu.add_breakpoint(0x4014a6); // DrvrInstall
@ -107,7 +106,7 @@ pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
// Issue of writing to 0x100000 which doesn't exist
cpu.add_breakpoint(0x400d62);
cpu.add_breakpoint(0x400464); // Boot Screen
cpu.add_breakpoint(0x400464); // Boot Screen
/*
use crate::devices::Addressable;
@ -127,4 +126,3 @@ pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
Ok(system)
}

View File

@ -1,6 +1,4 @@
pub mod peripherals;
mod system;
pub use crate::system::{Trs80Options, build_trs80};

View File

@ -1,5 +1,5 @@
pub mod model1;
#[rustfmt::skip]
pub mod keymap;
#[rustfmt::skip]
pub mod charset;

View File

@ -1,4 +1,3 @@
use femtos::{Instant, Duration};
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
@ -8,12 +7,12 @@ use super::keymap;
use super::charset::CharacterGenerator;
const DEV_NAME: &str = "model1";
const SCREEN_SIZE: (u32, u32) = (384, 128);
const DEV_NAME: &str = "model1";
const SCREEN_SIZE: (u32, u32) = (384, 128);
pub struct Model1Keyboard {
receiver: EventReceiver<KeyEvent>,
receiver: EventReceiver<KeyEvent>,
keyboard_mem: [u8; 8],
}
@ -41,14 +40,30 @@ impl Addressable for Model1Keyboard {
if (0x20..=0xA0).contains(&addr) {
let offset = addr - 0x20;
data[0] = 0;
if (offset & 0x01) != 0 { data[0] |= self.keyboard_mem[0]; }
if (offset & 0x02) != 0 { data[0] |= self.keyboard_mem[1]; }
if (offset & 0x04) != 0 { data[0] |= self.keyboard_mem[2]; }
if (offset & 0x08) != 0 { data[0] |= self.keyboard_mem[3]; }
if (offset & 0x10) != 0 { data[0] |= self.keyboard_mem[4]; }
if (offset & 0x20) != 0 { data[0] |= self.keyboard_mem[5]; }
if (offset & 0x40) != 0 { data[0] |= self.keyboard_mem[6]; }
if (offset & 0x80) != 0 { data[0] |= self.keyboard_mem[7]; }
if (offset & 0x01) != 0 {
data[0] |= self.keyboard_mem[0];
}
if (offset & 0x02) != 0 {
data[0] |= self.keyboard_mem[1];
}
if (offset & 0x04) != 0 {
data[0] |= self.keyboard_mem[2];
}
if (offset & 0x08) != 0 {
data[0] |= self.keyboard_mem[3];
}
if (offset & 0x10) != 0 {
data[0] |= self.keyboard_mem[4];
}
if (offset & 0x20) != 0 {
data[0] |= self.keyboard_mem[5];
}
if (offset & 0x40) != 0 {
data[0] |= self.keyboard_mem[6];
}
if (offset & 0x80) != 0 {
data[0] |= self.keyboard_mem[7];
}
//info!("{}: read from keyboard {:x} of {:?}", DEV_NAME, addr, data);
} else {
log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
@ -147,4 +162,3 @@ impl Transmutable for Model1Video {
Some(self)
}
}

View File

@ -1,4 +1,3 @@
use femtos::Frequency;
use moa_core::{System, Error, MemoryBlock, Device};
@ -46,32 +45,8 @@ pub fn build_trs80<H: Host>(host: &mut H, options: Trs80Options) -> Result<Syste
// TODO the ioport needs to be hooked up
let cpu = Z80::from_type(Z80Type::Z80, options.frequency, system.bus.clone(), 0, None);
//cpu.add_breakpoint(0x0);
//cpu.add_breakpoint(0xb55);
//cpu.add_breakpoint(0xb76);
//cpu.add_breakpoint(0x1e5);
//cpu.add_breakpoint(0x340); // "exec", the function that executes the line typed in
//cpu.add_breakpoint(0x357);
//cpu.add_breakpoint(0x401); // LIST command exec
//cpu.add_breakpoint(0x10); // putchar
//cpu.add_breakpoint(0x970);
//cpu.add_breakpoint(0x9f9);
//cpu.add_breakpoint(0xa58); // return from printing the line number
//cpu.add_breakpoint(0xc59); // the function called first thing when printing a decimal number
//cpu.add_breakpoint(0xe00); // normalize the float??
//cpu.add_breakpoint(0x970); // just after the decimal number print function is called, but after the call at the start is complete
//cpu.add_breakpoint(0xa6c);
//cpu.add_breakpoint(0xe00);
//cpu.add_breakpoint(0xc77);
//cpu.add_breakpoint(0xc83);
//cpu.add_breakpoint(0x96d);
//cpu.add_breakpoint(0x970);
//cpu.add_breakpoint(0x9e2);
//cpu.add_breakpoint(0x9f9);
system.add_interruptable_device("cpu", Device::new(cpu))?;
Ok(system)
}

View File

@ -1,4 +1,3 @@
const DEFAULT_HARTE_TESTS: &str = "tests/ProcessorTests/680x0/68000/v1/";
use std::io::prelude::*;
@ -95,7 +94,7 @@ struct TestCase {
initial_state: TestState,
#[serde(rename(deserialize = "final"))]
final_state: TestState,
length: usize
length: usize,
}
impl TestState {
@ -151,7 +150,9 @@ fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k, Memo
// Insert basic initialization
let len = 0x100_0000;
let mut data = Vec::with_capacity(len);
unsafe { data.set_len(len); }
unsafe {
data.set_len(len);
}
let mut memory = MemoryBlock::<u32, Instant>::from(data);
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
@ -164,7 +165,7 @@ fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k, Memo
fn assert_value<T>(actual: T, expected: T, message: &str) -> Result<(), Error>
where
T: PartialEq + Debug + UpperHex
T: PartialEq + Debug + UpperHex,
{
if actual == expected {
Ok(())
@ -197,13 +198,15 @@ fn load_state(cpu: &mut M68k, memory: &mut MemoryBlock<u32, Instant>, initial: &
// Load instructions into memory
for (i, ins) in initial.prefetch.iter().enumerate() {
memory.write_beu16(Instant::START, initial.pc + (i as u32 * 2), *ins)
memory
.write_beu16(Instant::START, initial.pc + (i as u32 * 2), *ins)
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
}
// Load data bytes into memory
for (addr, byte) in initial.ram.iter() {
memory.write_u8(Instant::START, *addr, *byte)
memory
.write_u8(Instant::START, *addr, *byte)
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
}
@ -237,14 +240,16 @@ fn assert_state(cpu: &M68k, memory: &mut MemoryBlock<u32, Instant>, expected: &T
// Load instructions into memory
for (i, ins) in expected.prefetch.iter().enumerate() {
let addr = expected.pc + (i as u32 * 2);
let actual = memory.read_beu16(Instant::START, addr & addr_mask)
let actual = memory
.read_beu16(Instant::START, addr & addr_mask)
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
assert_value(actual, *ins, &format!("prefetch at {:x}", addr))?;
}
// Load data bytes into memory
for (addr, byte) in expected.ram.iter() {
let actual = memory.read_u8(Instant::START, *addr & addr_mask)
let actual = memory
.read_u8(Instant::START, *addr & addr_mask)
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
assert_value(actual, *byte, &format!("ram at {:x}", addr))?;
}
@ -252,8 +257,14 @@ fn assert_state(cpu: &M68k, memory: &mut MemoryBlock<u32, Instant>, expected: &T
Ok(())
}
fn step_cpu_and_assert(cpu: &mut M68k, memory: &mut MemoryBlock<u32, Instant>, case: &TestCase, test_timing: bool) -> Result<(), Error> {
let clock_elapsed = cpu.step(Instant::START, memory)
fn step_cpu_and_assert(
cpu: &mut M68k,
memory: &mut MemoryBlock<u32, Instant>,
case: &TestCase,
test_timing: bool,
) -> Result<(), Error> {
let clock_elapsed = cpu
.step(Instant::START, memory)
.map_err(|err| Error::Step(format!("{:?}", err)))?;
let cycles = clock_elapsed.as_duration() / cpu.info.frequency.period_duration();
@ -282,7 +293,7 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> {
initial_cpu.dump_state(&mut writer).unwrap();
cpu.dump_state(&mut writer).unwrap();
}
writeln!(writer, "FAILED: {:?}", err).unwrap();
writeln!(writer, "FAILED: {:?}", err).unwrap();
println!("{}", writer);
}
Err(err)
@ -315,8 +326,9 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) {
// Only run the test if it's selected by the exceptions flag
if case.is_extended_exception_case() && args.exceptions == Selection::ExcludeAddr
|| case.is_exception_case() && args.exceptions == Selection::Exclude
|| !case.is_exception_case() && args.exceptions == Selection::Only {
|| case.is_exception_case() && args.exceptions == Selection::Exclude
|| !case.is_exception_case() && args.exceptions == Selection::Only
{
continue;
}
@ -334,7 +346,7 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) {
if let Err(err) = result {
failed += 1;
if !args.quiet {
println!("FAILED: {:?}", err);
println!("FAILED: {:?}", err);
}
} else {
passed += 1
@ -401,7 +413,11 @@ fn run_all_tests(args: &Args) {
}
println!();
println!("passed: {}, failed: {}, total {:.0}%", passed, failed, ((passed as f32) / (passed as f32 + failed as f32)) * 100.0);
println!(
"passed: {}, failed: {}, total {:.0}%",
passed,
failed,
((passed as f32) / (passed as f32 + failed as f32)) * 100.0
);
println!("completed in {}m {}s", elapsed_secs / 60, elapsed_secs % 60);
}

View File

@ -1,4 +1,3 @@
const DEFAULT_RAD_TESTS: &str = "tests/jsmoo/misc/tests/GeneratedTests/z80/v1/";
use std::rc::Rc;
@ -171,7 +170,7 @@ fn init_execute_test(cputype: Z80Type, state: &TestState, ports: &[TestPort]) ->
fn assert_value<T>(actual: T, expected: T, message: &str) -> Result<(), Error>
where
T: PartialEq + Debug + UpperHex
T: PartialEq + Debug + UpperHex,
{
if actual == expected {
Ok(())
@ -180,7 +179,13 @@ where
}
}
fn load_state(cpu: &mut Z80, system: &mut System, io_bus: Rc<RefCell<Bus>>, initial: &TestState, ports: &[TestPort]) -> Result<(), Error> {
fn load_state(
cpu: &mut Z80,
system: &mut System,
io_bus: Rc<RefCell<Bus>>,
initial: &TestState,
ports: &[TestPort],
) -> Result<(), Error> {
cpu.state.reg[0] = initial.b;
cpu.state.reg[1] = initial.c;
cpu.state.reg[2] = initial.d;
@ -223,7 +228,14 @@ fn load_state(cpu: &mut Z80, system: &mut System, io_bus: Rc<RefCell<Bus>>, init
const IGNORE_FLAG_MASK: u8 = Flags::F3 as u8 | Flags::F5 as u8;
fn assert_state(cpu: &Z80, system: &System, io_bus: Rc<RefCell<Bus>>, expected: &TestState, check_extra_flags: bool, ports: &[TestPort]) -> Result<(), Error> {
fn assert_state(
cpu: &Z80,
system: &System,
io_bus: Rc<RefCell<Bus>>,
expected: &TestState,
check_extra_flags: bool,
ports: &[TestPort],
) -> Result<(), Error> {
assert_value(cpu.state.reg[0], expected.b, "b")?;
assert_value(cpu.state.reg[1], expected.c, "c")?;
assert_value(cpu.state.reg[2], expected.d, "d")?;
@ -279,14 +291,24 @@ fn assert_state(cpu: &Z80, system: &System, io_bus: Rc<RefCell<Bus>>, expected:
Ok(())
}
fn step_cpu_and_assert(cpu: &mut Z80, system: &System, io_bus: Rc<RefCell<Bus>>, case: &TestCase, args: &Args) -> Result<(), Error> {
fn step_cpu_and_assert(
cpu: &mut Z80,
system: &System,
io_bus: Rc<RefCell<Bus>>,
case: &TestCase,
args: &Args,
) -> Result<(), Error> {
let clock_elapsed = cpu.step(system)?;
assert_state(cpu, system, io_bus, &case.final_state, args.check_extra_flags, &case.ports)?;
if args.check_timings {
let cycles = clock_elapsed / cpu.frequency.period_duration();
if cycles != case.cycles.len() as Address {
return Err(Error::assertion(format!("expected instruction to take {} cycles, but took {}", case.cycles.len(), cycles)));
return Err(Error::assertion(format!(
"expected instruction to take {} cycles, but took {}",
case.cycles.len(),
cycles
)));
}
}
@ -309,7 +331,7 @@ fn run_test(case: &TestCase, args: &Args) -> Result<(), Error> {
initial_cpu.dump_state(system.clock);
cpu.dump_state(system.clock);
}
println!("FAILED: {:?}", err);
println!("FAILED: {:?}", err);
}
Err(err)
},
@ -353,7 +375,7 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) {
if let Err(err) = result {
failed += 1;
if !args.quiet {
println!("FAILED: {:?}", err);
println!("FAILED: {:?}", err);
}
} else {
passed += 1
@ -426,7 +448,12 @@ fn run_all_tests(args: &Args) {
}
println!();
println!("passed: {}, failed: {}, total {:.0}%", passed, failed, ((passed as f32) / (passed as f32 + failed as f32)) * 100.0);
println!(
"passed: {}, failed: {}, total {:.0}%",
passed,
failed,
((passed as f32) / (passed as f32 + failed as f32)) * 100.0
);
println!("completed in {}m {}s", elapsed_secs / 60, elapsed_secs % 60);
}
@ -438,28 +465,24 @@ fn is_undocumented_instruction(name: &str) -> bool {
opcodes.extend(vec![0; 3 - opcodes.len()]);
match (opcodes[0], opcodes[1]) {
(0xCB, op) => {
(0x30..=0x37).contains(&op)
},
(0xDD, 0xCB) |
(0xFD, 0xCB) => {
!(opcodes[2] & 0x07 == 0x06 && opcodes[2] != 0x36)
},
(0xDD, op) |
(0xFD, op) => {
(0xCB, op) => (0x30..=0x37).contains(&op),
(0xDD, 0xCB) | (0xFD, 0xCB) => !(opcodes[2] & 0x07 == 0x06 && opcodes[2] != 0x36),
(0xDD, op) | (0xFD, op) => {
let upper = op & 0xF0;
let lower = op & 0x0F;
!(lower == 0x0E && (0x40..=0xB0).contains(&upper) || (0x70..=0x77).contains(&op) && op != 0x76 || op != 0x76 && (0x70..=0x77).contains(&op) || lower == 0x06 && (0x30..=0xB0).contains(&upper) && upper != 0x70) &&
!((0x21..=0x23).contains(&op) || (0x34..=0x36).contains(&op) || (0x29..=0x2B).contains(&op)) &&
!(lower == 0x09 && upper <= 0x30) &&
!(op == 0xE1 || op == 0xE3 || op == 0xE5 || op == 0xE9 || op == 0xF9)
!(lower == 0x0E && (0x40..=0xB0).contains(&upper)
|| (0x70..=0x77).contains(&op) && op != 0x76
|| op != 0x76 && (0x70..=0x77).contains(&op)
|| lower == 0x06 && (0x30..=0xB0).contains(&upper) && upper != 0x70)
&& !((0x21..=0x23).contains(&op) || (0x34..=0x36).contains(&op) || (0x29..=0x2B).contains(&op))
&& !(lower == 0x09 && upper <= 0x30)
&& !(op == 0xE1 || op == 0xE3 || op == 0xE5 || op == 0xE9 || op == 0xF9)
},
(0xED, op) => {
// NOTE this assumes the tests don't have the missing instructions, or the Z180 instructions
// so it only checks for the undocumented ones
op == 0x63 || op == 0x6B || op == 0x70 || op == 0x71
},
_ => false
_ => false,
}
}

View File

@ -1,17 +1,19 @@
* convert the Z80
* implement Inspect/Debug traits
* fix dump_state everywhere, which now requires a writer. Is there an easier way? Is there a way that doesn't require std
* can you clean it up more?
* implement the inspect and debug traits
* move the interrupt controller logic to the step() function only, and have a customish interrupt interface into the sim
* fix it to use the full 68k address space, and maybe see if it's possible to make the address translation cleaner/nicer/simpler/faster
* figure out how to do interrupts, and add them to emulator-hal, implement them in m68k
* convert peripherals to use BusAccess and Step
* replace Addressable/Steppable and modify Transmutable to use the emulator-hal traits
* remove the custom moa impls from m68k if possible at this point
* publish the emulator-hal crate
* publish the m68k and z80 crates
* do the Z80? Should that be another PR?
* fix the tests
* fix all the clippy issues
* it doesn't work when using debug due to math checks, so fix them
* change all the inspection and debugging things to return a struct which can then be printed by the frontend
-----
* there are many issues with the coprocessor address space, and the VDP
* I mapped the sn sound chip into 0xC00010, in the middle of the VDP's address space, and didn't get a runtime error!!! needs fixing