mirror of
https://github.com/transistorfet/moa.git
synced 2025-04-10 22:37:10 +00:00
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:
parent
a2372d1355
commit
7dac32d844
@ -1 +1 @@
|
||||
msrv = "1.60.0"
|
||||
msrv = "1.70.0"
|
||||
|
32
.github/workflows/clippy.yaml
vendored
Normal file
32
.github/workflows/clippy.yaml
vendored
Normal 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
32
.github/workflows/rustdoc.yaml
vendored
Normal 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
32
.github/workflows/rustfmt.yaml
vendored
Normal 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
44
.github/workflows/test.yaml
vendored
Normal 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
24
.rustfmt.toml
Normal 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
19
Cargo.lock
generated
@ -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"
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
|
||||
@ -43,4 +42,3 @@ impl InterruptController {
|
||||
Ok(acknowledge)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
|
||||
@ -24,4 +23,3 @@ fn main() {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
use femtos::Instant;
|
||||
use emulator_hal::bus::BusAccess;
|
||||
|
||||
@ -50,4 +49,3 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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> {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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() {
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
@ -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 },
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -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) },
|
||||
];
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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!();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))),
|
||||
];
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct Sample(pub f32, pub f32);
|
||||
|
||||
@ -22,4 +21,3 @@ impl AudioFrame {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ControllerDevice {
|
||||
A,
|
||||
@ -37,4 +36,3 @@ impl ControllerEvent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Key {
|
||||
A,
|
||||
@ -124,4 +123,3 @@ impl KeyEvent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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 => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]) {}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,4 +135,3 @@ pub trait SignalDriver<T> {
|
||||
|
||||
|
||||
//pub struct LevelTriggeredOutput
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,2 @@
|
||||
|
||||
mod ata;
|
||||
pub use crate::ata::AtaDevice;
|
||||
|
||||
|
@ -1,4 +1,2 @@
|
||||
|
||||
mod mos6522;
|
||||
pub use crate::mos6522::Mos6522;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,2 @@
|
||||
|
||||
mod mc68681;
|
||||
pub use crate::mc68681::MC68681;
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
|
||||
mod sn76489;
|
||||
pub use crate::sn76489::Sn76489;
|
||||
|
||||
|
||||
mod ym2612;
|
||||
pub use crate::ym2612::Ym2612;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,2 @@
|
||||
|
||||
mod z8530;
|
||||
pub use crate::z8530::Z8530;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,2 @@
|
||||
|
||||
mod system;
|
||||
pub use crate::system::{build_computie, build_computie_k30, launch_terminal_emulator, launch_slip_connection, ComputieOptions};
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
|
||||
pub mod peripherals;
|
||||
pub mod utils;
|
||||
|
||||
mod system;
|
||||
pub use crate::system::{SegaGenesisOptions, build_genesis};
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
|
||||
pub mod ym7101;
|
||||
pub mod controllers;
|
||||
pub mod coprocessor;
|
||||
|
||||
pub mod ym7101;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
|
||||
pub mod peripherals;
|
||||
|
||||
mod system;
|
||||
pub use crate::system::{build_macintosh_512k};
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
|
||||
pub mod iwm;
|
||||
pub mod video;
|
||||
pub mod mainboard;
|
||||
|
||||
pub mod video;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
|
||||
pub mod peripherals;
|
||||
|
||||
mod system;
|
||||
pub use crate::system::{Trs80Options, build_trs80};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
pub mod model1;
|
||||
#[rustfmt::skip]
|
||||
pub mod keymap;
|
||||
#[rustfmt::skip]
|
||||
pub mod charset;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
18
todo.txt
18
todo.txt
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user