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"
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -420,10 +420,6 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "emulator-hal"
|
name = "emulator-hal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
|
||||||
"femtos",
|
|
||||||
"fugit",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "emulator-hal-memory"
|
name = "emulator-hal-memory"
|
||||||
|
@ -491,21 +487,6 @@ dependencies = [
|
||||||
"miniz_oxide",
|
"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]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::{RefCell, RefMut, BorrowMutError};
|
use std::cell::{RefCell, RefMut, BorrowMutError};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
@ -17,7 +16,7 @@ pub type Address = u64;
|
||||||
/// information that might be helpful for debugging.
|
/// information that might be helpful for debugging.
|
||||||
pub trait Steppable {
|
pub trait Steppable {
|
||||||
fn step(&mut self, system: &System) -> Result<Duration, Error>;
|
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
|
/// A device that can receive an interrupt. The `interrupt_state_change()` method
|
||||||
|
@ -104,30 +103,22 @@ pub trait Addressable {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_beu16(data: &[u8]) -> u16 {
|
pub fn read_beu16(data: &[u8]) -> u16 {
|
||||||
(data[0] as u16) << 8 |
|
(data[0] as u16) << 8 | (data[1] as u16)
|
||||||
(data[1] as u16)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_leu16(data: &[u8]) -> u16 {
|
pub fn read_leu16(data: &[u8]) -> u16 {
|
||||||
(data[1] as u16) << 8 |
|
(data[1] as u16) << 8 | (data[0] as u16)
|
||||||
(data[0] as u16)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_beu32(data: &[u8]) -> u32 {
|
pub fn read_beu32(data: &[u8]) -> u32 {
|
||||||
(data[0] as u32) << 24 |
|
(data[0] as u32) << 24 | (data[1] as u32) << 16 | (data[2] as u32) << 8 | (data[3] as u32)
|
||||||
(data[1] as u32) << 16 |
|
|
||||||
(data[2] as u32) << 8 |
|
|
||||||
(data[3] as u32)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_leu32(data: &[u8]) -> u32 {
|
pub fn read_leu32(data: &[u8]) -> u32 {
|
||||||
(data[3] as u32) << 24 |
|
(data[3] as u32) << 24 | (data[2] as u32) << 16 | (data[1] as u32) << 8 | (data[0] as u32)
|
||||||
(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 {
|
impl Device {
|
||||||
pub fn new<T>(value: T) -> Self
|
pub fn new<T>(value: T) -> Self
|
||||||
where
|
where
|
||||||
T: Transmutable + 'static
|
T: Transmutable + 'static,
|
||||||
{
|
{
|
||||||
Self(DeviceId::new(), wrap_transmutable(value))
|
Self(DeviceId::new(), wrap_transmutable(value))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use moa_host::HostError;
|
use moa_host::HostError;
|
||||||
|
|
||||||
|
@ -52,10 +51,7 @@ impl Error {
|
||||||
|
|
||||||
pub fn msg(&self) -> &str {
|
pub fn msg(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Error::Assertion(msg) |
|
Error::Assertion(msg) | Error::Breakpoint(msg) | Error::Other(msg) | Error::Emulator(_, msg) => msg.as_str(),
|
||||||
Error::Breakpoint(msg) |
|
|
||||||
Error::Other(msg) |
|
|
||||||
Error::Emulator(_, msg) => msg.as_str(),
|
|
||||||
Error::Processor(_) => "native exception",
|
Error::Processor(_) => "native exception",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,10 +60,7 @@ impl Error {
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::Assertion(msg) |
|
Error::Assertion(msg) | Error::Breakpoint(msg) | Error::Other(msg) | Error::Emulator(_, msg) => write!(f, "{}", msg),
|
||||||
Error::Breakpoint(msg) |
|
|
||||||
Error::Other(msg) |
|
|
||||||
Error::Emulator(_, msg) => write!(f, "{}", msg),
|
|
||||||
Error::Processor(_) => write!(f, "native exception"),
|
Error::Processor(_) => write!(f, "native exception"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,4 +71,3 @@ impl<E> From<HostError<E>> for Error {
|
||||||
Self::Other("other".to_string())
|
Self::Other("other".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,4 +42,3 @@ impl InterruptController {
|
||||||
Ok(acknowledge)
|
Ok(acknowledge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
|
@ -7,12 +6,15 @@ mod interrupts;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod system;
|
mod system;
|
||||||
|
|
||||||
pub use crate::devices::{Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device};
|
pub use crate::devices::{
|
||||||
pub use crate::devices::{read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable};
|
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::error::Error;
|
||||||
pub use crate::interrupts::InterruptController;
|
pub use crate::interrupts::InterruptController;
|
||||||
pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory};
|
pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice, dump_memory};
|
||||||
pub use crate::system::System;
|
pub use crate::system::System;
|
||||||
|
|
||||||
pub use emulator_hal::bus::{BusAccess};
|
pub use emulator_hal::bus::{BusAccess};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -20,7 +19,7 @@ impl MemoryBlock {
|
||||||
pub fn new(contents: Vec<u8>) -> MemoryBlock {
|
pub fn new(contents: Vec<u8>) -> MemoryBlock {
|
||||||
MemoryBlock {
|
MemoryBlock {
|
||||||
read_only: false,
|
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> {
|
fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
|
||||||
if self.read_only {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,12 +101,20 @@ impl Addressable for AddressRepeater {
|
||||||
|
|
||||||
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
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;
|
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> {
|
fn write(&mut self, clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
|
||||||
let size = self.subdevice.borrow_mut().as_addressable().unwrap().size() as Address;
|
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 {
|
impl AddressTranslator {
|
||||||
pub fn new<F>(subdevice: Device, size: usize, func: F) -> Self
|
pub fn new<F>(subdevice: Device, size: usize, func: F) -> Self
|
||||||
where
|
where
|
||||||
F: Fn(Address) -> Address + 'static
|
F: Fn(Address) -> Address + 'static,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
subdevice,
|
subdevice,
|
||||||
|
@ -141,11 +151,19 @@ impl Addressable for AddressTranslator {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(&mut self, clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
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> {
|
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) {
|
pub fn insert(&mut self, base: Address, dev: Device) {
|
||||||
let size = dev.borrow_mut().as_addressable().unwrap().size();
|
let size = dev.borrow_mut().as_addressable().unwrap().size();
|
||||||
let block = Block { base, size, dev };
|
let block = Block {
|
||||||
let i = self.blocks.iter().position(|cur| cur.base > block.base).unwrap_or(self.blocks.len());
|
base,
|
||||||
|
size,
|
||||||
|
dev,
|
||||||
|
};
|
||||||
|
let i = self
|
||||||
|
.blocks
|
||||||
|
.iter()
|
||||||
|
.position(|cur| cur.base > block.base)
|
||||||
|
.unwrap_or(self.blocks.len());
|
||||||
self.blocks.insert(i, block);
|
self.blocks.insert(i, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +278,7 @@ impl Addressable for Bus {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(err) if self.ignore_unmapped => {
|
Err(err) if self.ignore_unmapped => {
|
||||||
log::info!("{:?}", err);
|
log::info!("{:?}", err);
|
||||||
return Ok(())
|
return Ok(());
|
||||||
},
|
},
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
|
@ -270,7 +296,7 @@ impl Addressable for Bus {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(err) if self.ignore_unmapped => {
|
Err(err) if self.ignore_unmapped => {
|
||||||
log::info!("{:?}", err);
|
log::info!("{:?}", err);
|
||||||
return Ok(())
|
return Ok(());
|
||||||
},
|
},
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
};
|
};
|
||||||
|
@ -300,7 +326,9 @@ impl BusPort {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump_memory(&mut self, clock: Instant, addr: Address, count: Address) {
|
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]
|
#[inline]
|
||||||
|
@ -401,4 +429,3 @@ impl BusAccess<u64, Instant> for &mut dyn Addressable {
|
||||||
Ok(data.len())
|
Ok(data.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -45,7 +44,10 @@ impl System {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_device(&self, name: &str) -> Result<Device, Error> {
|
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> {
|
pub fn add_device(&mut self, name: &str, device: Device) -> Result<(), Error> {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use moa_parsing::{self as parser, AssemblyLine, AssemblyOperand, AssemblyParser, ParserError};
|
use moa_parsing::{self as parser, AssemblyLine, AssemblyOperand, AssemblyParser, ParserError};
|
||||||
|
@ -24,6 +23,7 @@ impl From<ParserError> for Error {
|
||||||
|
|
||||||
#[repr(usize)]
|
#[repr(usize)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
#[rustfmt::skip]
|
||||||
pub enum Disallow {
|
pub enum Disallow {
|
||||||
None = 0x0000,
|
None = 0x0000,
|
||||||
NoDReg = 0x0001,
|
NoDReg = 0x0001,
|
||||||
|
@ -136,7 +136,10 @@ impl M68kAssembler {
|
||||||
match reloc.rtype {
|
match reloc.rtype {
|
||||||
RelocationType::Displacement => {
|
RelocationType::Displacement => {
|
||||||
// TODO this doesn't yet take into accound the origin
|
// 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;
|
self.output[reloc.index] |= ((self.output[reloc.index] as i8 * 2 + 2) - (location as i8 * 2)) as u16 & 0x00ff;
|
||||||
},
|
},
|
||||||
_ => panic!("relocation type unimplemented"),
|
_ => panic!("relocation type unimplemented"),
|
||||||
|
@ -166,12 +169,22 @@ impl M68kAssembler {
|
||||||
"bra" => {
|
"bra" => {
|
||||||
let label = parser::expect_label(lineno, args)?;
|
let label = parser::expect_label(lineno, args)?;
|
||||||
self.output.push(0x6000);
|
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" => {
|
"bsr" => {
|
||||||
let label = parser::expect_label(lineno, args)?;
|
let label = parser::expect_label(lineno, args)?;
|
||||||
self.output.push(0x6100);
|
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" => {
|
"illegal" => {
|
||||||
self.output.push(0x4AFC);
|
self.output.push(0x4AFC);
|
||||||
|
@ -180,7 +193,8 @@ impl M68kAssembler {
|
||||||
"lea" => {
|
"lea" => {
|
||||||
parser::expect_args(lineno, args, 2)?;
|
parser::expect_args(lineno, args, 2)?;
|
||||||
let reg = expect_address_register(lineno, &args[0])?;
|
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.push(0x41C0 | (reg << 9) | effective_address);
|
||||||
self.output.extend(additional_words);
|
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> {
|
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] {
|
match &mneumonic[..mneumonic.len() - 1] {
|
||||||
"addi" => {
|
"addi" => {
|
||||||
self.convert_common_immediate_instruction(lineno, 0x0600, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
self.convert_common_immediate_instruction(lineno, 0x0600, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
||||||
|
@ -229,7 +244,13 @@ impl M68kAssembler {
|
||||||
},
|
},
|
||||||
"andi" => {
|
"andi" => {
|
||||||
if !self.check_convert_flags_instruction(lineno, 0x23C, 0x27C, args)? {
|
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" => {
|
"and" => {
|
||||||
|
@ -240,7 +261,13 @@ impl M68kAssembler {
|
||||||
},
|
},
|
||||||
|
|
||||||
"clr" => {
|
"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" => {
|
"cmpi" => {
|
||||||
self.convert_common_immediate_instruction(lineno, 0x0C00, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
self.convert_common_immediate_instruction(lineno, 0x0C00, args, operation_size?, Disallow::NoARegImmediateOrPC)?;
|
||||||
|
@ -251,7 +278,13 @@ impl M68kAssembler {
|
||||||
|
|
||||||
"eori" => {
|
"eori" => {
|
||||||
if !self.check_convert_flags_instruction(lineno, 0x0A3C, 0x0A7C, args)? {
|
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" => {
|
"eor" => {
|
||||||
|
@ -265,27 +298,54 @@ impl M68kAssembler {
|
||||||
"move" | "movea" => {
|
"move" | "movea" => {
|
||||||
let operation_size = operation_size?;
|
let operation_size = operation_size?;
|
||||||
parser::expect_args(lineno, args, 2)?;
|
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_left, additional_words_left) =
|
||||||
let (effective_address_right, additional_words_right) = convert_target(lineno, &args[1], operation_size, Disallow::None)?;
|
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);
|
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_left);
|
||||||
self.output.extend(additional_words_right);
|
self.output.extend(additional_words_right);
|
||||||
},
|
},
|
||||||
|
|
||||||
"neg" => {
|
"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" => {
|
"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" => {
|
"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" => {
|
"ori" => {
|
||||||
if !self.check_convert_flags_instruction(lineno, 0x003C, 0x007C, args)? {
|
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" => {
|
"or" => {
|
||||||
|
@ -319,7 +379,14 @@ impl M68kAssembler {
|
||||||
Ok(())
|
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)?;
|
parser::expect_args(lineno, args, 2)?;
|
||||||
let immediate = parser::expect_immediate(lineno, &args[0])?;
|
let immediate = parser::expect_immediate(lineno, &args[0])?;
|
||||||
let (effective_address, additional_words) = convert_target(lineno, &args[1], operation_size, disallow)?;
|
let (effective_address, additional_words) = convert_target(lineno, &args[1], operation_size, disallow)?;
|
||||||
|
@ -329,27 +396,50 @@ impl M68kAssembler {
|
||||||
Ok(())
|
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)?;
|
parser::expect_args(lineno, args, 2)?;
|
||||||
let (direction, reg, operand) = convert_reg_and_other(lineno, args, Disallow::NoAReg)?;
|
let (direction, reg, operand) = convert_reg_and_other(lineno, args, Disallow::NoAReg)?;
|
||||||
let (effective_address, additional_words) = convert_target(lineno, operand, operation_size, disallow)?;
|
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);
|
self.output.extend(additional_words);
|
||||||
Ok(())
|
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)?;
|
let size_bit = expect_a_instruction_size(lineno, operation_size)?;
|
||||||
parser::expect_args(lineno, args, 2)?;
|
parser::expect_args(lineno, args, 2)?;
|
||||||
//let (_direction, reg, operand) = convert_reg_and_other(lineno, args, Disallow::NoDReg)?;
|
//let (_direction, reg, operand) = convert_reg_and_other(lineno, args, Disallow::NoDReg)?;
|
||||||
let reg = expect_address_register(lineno, &args[1])?;
|
let reg = expect_address_register(lineno, &args[1])?;
|
||||||
let (effective_address, additional_words) = convert_target(lineno, &args[0], operation_size, disallow)?;
|
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);
|
self.output.extend(additional_words);
|
||||||
Ok(())
|
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)?;
|
parser::expect_args(lineno, args, 1)?;
|
||||||
let (effective_address, additional_words) = convert_target(lineno, &args[0], operation_size, disallow)?;
|
let (effective_address, additional_words) = convert_target(lineno, &args[0], operation_size, disallow)?;
|
||||||
self.output.push(opcode | encode_size(operation_size) | effective_address);
|
self.output.push(opcode | encode_size(operation_size) | effective_address);
|
||||||
|
@ -357,7 +447,13 @@ impl M68kAssembler {
|
||||||
Ok(())
|
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] {
|
if let AssemblyOperand::Register(name) = &args[1] {
|
||||||
let opcode = match name.as_str() {
|
let opcode = match name.as_str() {
|
||||||
"ccr" => Some(opcode_ccr),
|
"ccr" => Some(opcode_ccr),
|
||||||
|
@ -376,32 +472,47 @@ impl M68kAssembler {
|
||||||
Ok(false)
|
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 dirstr = &mneumonic[mneumonic.len() - 2..mneumonic.len() - 1];
|
||||||
let direction = if dirstr == "r" {
|
let direction = if dirstr == "r" {
|
||||||
0
|
0
|
||||||
} else if dirstr == "l" {
|
} else if dirstr == "l" {
|
||||||
1 << 8
|
1 << 8
|
||||||
} else {
|
} 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 {
|
match &args {
|
||||||
[AssemblyOperand::Immediate(_), AssemblyOperand::Register(_)] => {
|
[AssemblyOperand::Immediate(_), AssemblyOperand::Register(_)] => {
|
||||||
let mut immediate = parser::expect_immediate(lineno, &args[0])?;
|
let mut immediate = parser::expect_immediate(lineno, &args[0])?;
|
||||||
if !(1..=8).contains(&immediate) {
|
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 {
|
} else if immediate == 8 {
|
||||||
immediate = 0;
|
immediate = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reg = expect_data_register(lineno, &args[1])?;
|
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(_)] => {
|
[AssemblyOperand::Register(_), AssemblyOperand::Register(_)] => {
|
||||||
let bit_reg = expect_data_register(lineno, &args[0])?;
|
let bit_reg = expect_data_register(lineno, &args[0])?;
|
||||||
let reg = expect_data_register(lineno, &args[1])?;
|
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)?;
|
// 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> {
|
fn convert_target(lineno: usize, operand: &AssemblyOperand, size: Size, disallow: Disallow) -> Result<(u16, Vec<u16>), Error> {
|
||||||
match operand {
|
match operand {
|
||||||
AssemblyOperand::Register(name) => {
|
AssemblyOperand::Register(name) => convert_register(lineno, name, disallow),
|
||||||
convert_register(lineno, name, disallow)
|
|
||||||
},
|
|
||||||
AssemblyOperand::Immediate(value) => {
|
AssemblyOperand::Immediate(value) => {
|
||||||
disallow.check(lineno, Disallow::NoImmediate)?;
|
disallow.check(lineno, Disallow::NoImmediate)?;
|
||||||
Ok((0b111100, convert_immediate(lineno, *value, size)?))
|
Ok((0b111100, convert_immediate(lineno, *value, size)?))
|
||||||
},
|
},
|
||||||
AssemblyOperand::Indirect(args) => {
|
AssemblyOperand::Indirect(args) => convert_indirect(lineno, args, disallow),
|
||||||
convert_indirect(lineno, args, disallow)
|
|
||||||
},
|
|
||||||
AssemblyOperand::IndirectPost(args, operator) => {
|
AssemblyOperand::IndirectPost(args, operator) => {
|
||||||
disallow.check(lineno, Disallow::NoIndirectPost)?;
|
disallow.check(lineno, Disallow::NoIndirectPost)?;
|
||||||
if args.len() == 1 && operator == "+" {
|
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) => {
|
AssemblyOperand::IndirectPre(operator, args) => {
|
||||||
disallow.check(lineno, Disallow::NoIndirectPre)?;
|
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))),
|
_ => 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
|
// TODO add the MC68020 address options
|
||||||
_ => {
|
_ => Err(Error::new(format!(
|
||||||
Err(Error::new(format!("error at line {}: expected valid indirect addressing mode, but found {:?}", lineno, args)))
|
"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 {
|
match &args {
|
||||||
[AssemblyOperand::Register(reg), effective_address] => {
|
[AssemblyOperand::Register(reg), effective_address] => Ok(((0b1 << 8), expect_reg_num(lineno, 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!(
|
||||||
[effective_address, AssemblyOperand::Register(reg)] => {
|
"error at line {}: expected register and effective address, but found {:?}",
|
||||||
Ok(((0b0 << 8), expect_reg_num(lineno, reg)?, effective_address))
|
lineno, args
|
||||||
},
|
))),
|
||||||
_ => {
|
|
||||||
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 {
|
if value <= u8::MAX as usize {
|
||||||
Ok(vec![value as u16])
|
Ok(vec![value as u16])
|
||||||
} else {
|
} 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 => {
|
Size::Word => {
|
||||||
if value <= u16::MAX as usize {
|
if value <= u16::MAX as usize {
|
||||||
Ok(vec![value as u16])
|
Ok(vec![value as u16])
|
||||||
} else {
|
} 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]),
|
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);
|
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> {
|
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),
|
'b' => Some(Size::Byte),
|
||||||
'w' => Some(Size::Word),
|
'w' => Some(Size::Word),
|
||||||
'l' => Some(Size::Long),
|
'l' => Some(Size::Long),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,7 +756,6 @@ fn encode_size_bit(size: Size) -> Result<u16, Error> {
|
||||||
match size {
|
match size {
|
||||||
Size::Word => Ok(0b01 << 6),
|
Size::Word => Ok(0b01 << 6),
|
||||||
Size::Long => Ok(0b10 << 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::fs;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
@ -24,4 +23,3 @@ fn main() {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::Instant;
|
use femtos::Instant;
|
||||||
use emulator_hal::bus::BusAccess;
|
use emulator_hal::bus::BusAccess;
|
||||||
|
|
||||||
|
@ -50,4 +49,3 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
|
|
||||||
use femtos::Instant;
|
use femtos::Instant;
|
||||||
use emulator_hal::bus::BusAccess;
|
use emulator_hal::bus::BusAccess;
|
||||||
|
|
||||||
use crate::state::{M68kType, M68kError, Exceptions};
|
use crate::state::{M68kType, M68kError, Exceptions};
|
||||||
use crate::memory::{M68kBusPort, M68kAddress};
|
use crate::memory::{M68kBusPort, M68kAddress};
|
||||||
use crate::instructions::{
|
use crate::instructions::{
|
||||||
Size,
|
Size, Sign, Direction, XRegister, BaseRegister, IndexRegister, RegOrImmediate, ControlRegister, Condition, Target, Instruction,
|
||||||
Sign,
|
|
||||||
Direction,
|
|
||||||
XRegister,
|
|
||||||
BaseRegister,
|
|
||||||
IndexRegister,
|
|
||||||
RegOrImmediate,
|
|
||||||
ControlRegister,
|
|
||||||
Condition,
|
|
||||||
Target,
|
|
||||||
Instruction,
|
|
||||||
sign_extend_to_long,
|
sign_extend_to_long,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,7 +67,13 @@ impl M68kDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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
|
where
|
||||||
Bus: BusAccess<M68kAddress, Instant>,
|
Bus: BusAccess<M68kAddress, Instant>,
|
||||||
{
|
{
|
||||||
|
@ -119,10 +114,9 @@ impl M68kDecoder {
|
||||||
where
|
where
|
||||||
Bus: BusAccess<M68kAddress, Instant>,
|
Bus: BusAccess<M68kAddress, Instant>,
|
||||||
{
|
{
|
||||||
let ins_data: Result<String, M68kError<Bus::Error>> =
|
let ins_data: Result<String, M68kError<Bus::Error>> = (0..((self.end - self.start) / 2))
|
||||||
(0..((self.end - self.start) / 2)).map(|offset|
|
.map(|offset| Ok(format!("{:04x} ", bus.read_beu16(clock, self.start + (offset * 2)).unwrap())))
|
||||||
Ok(format!("{:04x} ", bus.read_beu16(clock, self.start + (offset * 2)).unwrap()))
|
.collect();
|
||||||
).collect();
|
|
||||||
println!("{:#010x}: {}\n\t{}\n", self.start, ins_data.unwrap(), self.instruction);
|
println!("{:#010x}: {}\n\t{}\n", self.start, ins_data.unwrap(), self.instruction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +180,11 @@ where
|
||||||
} else if (ins & 0x138) == 0x108 {
|
} else if (ins & 0x138) == 0x108 {
|
||||||
let dreg = get_high_reg(ins);
|
let dreg = get_high_reg(ins);
|
||||||
let areg = get_low_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 size = if (ins & 0x0040) == 0 { Size::Word } else { Size::Long };
|
||||||
let offset = self.read_instruction_word()? as i16;
|
let offset = self.read_instruction_word()? as i16;
|
||||||
Ok(Instruction::MOVEP(dreg, areg, offset, size, dir))
|
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 size = if (ins & 0x0040) == 0 { Size::Word } else { Size::Long };
|
||||||
let data = self.read_instruction_word()?;
|
let data = self.read_instruction_word()?;
|
||||||
let target = self.decode_lower_effective_address(ins, None)?;
|
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))
|
Ok(Instruction::MOVEM(target, size, dir, data))
|
||||||
} else if (ins & 0xF80) == 0xC00 && self.decoder.cputype >= M68kType::MC68020 {
|
} else if (ins & 0xF80) == 0xC00 && self.decoder.cputype >= M68kType::MC68020 {
|
||||||
let extension = self.read_instruction_word()?;
|
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 reg_l = ((extension & 0x7000) >> 12) as u8;
|
||||||
let target = self.decode_lower_effective_address(ins, Some(Size::Long))?;
|
let target = self.decode_lower_effective_address(ins, Some(Size::Long))?;
|
||||||
let sign = if (ins & 0x0800) == 0 { Sign::Unsigned } else { Sign::Signed };
|
let sign = if (ins & 0x0800) == 0 { Sign::Unsigned } else { Sign::Signed };
|
||||||
|
@ -303,30 +309,22 @@ where
|
||||||
} else if (ins & 0x800) == 0 {
|
} else if (ins & 0x800) == 0 {
|
||||||
let target = self.decode_lower_effective_address(ins, Some(Size::Word))?;
|
let target = self.decode_lower_effective_address(ins, Some(Size::Word))?;
|
||||||
match (ins & 0x0700) >> 8 {
|
match (ins & 0x0700) >> 8 {
|
||||||
0b000 => {
|
0b000 => match get_size(ins) {
|
||||||
match get_size(ins) {
|
Some(size) => Ok(Instruction::NEGX(target, size)),
|
||||||
Some(size) => Ok(Instruction::NEGX(target, size)),
|
None => Ok(Instruction::MOVEfromSR(target)),
|
||||||
None => Ok(Instruction::MOVEfromSR(target)),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
0b010 => {
|
0b010 => match get_size(ins) {
|
||||||
match get_size(ins) {
|
Some(size) => Ok(Instruction::CLR(target, size)),
|
||||||
Some(size) => Ok(Instruction::CLR(target, size)),
|
None if self.decoder.cputype >= M68kType::MC68010 => Ok(Instruction::MOVEfromCCR(target)),
|
||||||
None if self.decoder.cputype >= M68kType::MC68010 => Ok(Instruction::MOVEfromCCR(target)),
|
None => Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
||||||
None => Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
0b100 => {
|
0b100 => match get_size(ins) {
|
||||||
match get_size(ins) {
|
Some(size) => Ok(Instruction::NEG(target, size)),
|
||||||
Some(size) => Ok(Instruction::NEG(target, size)),
|
None => Ok(Instruction::MOVEtoCCR(target)),
|
||||||
None => Ok(Instruction::MOVEtoCCR(target)),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
0b110 => {
|
0b110 => match get_size(ins) {
|
||||||
match get_size(ins) {
|
Some(size) => Ok(Instruction::NOT(target, size)),
|
||||||
Some(size) => Ok(Instruction::NOT(target, size)),
|
None => Ok(Instruction::MOVEtoSR(target)),
|
||||||
None => Ok(Instruction::MOVEtoSR(target)),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
_ => Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
_ => Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
||||||
}
|
}
|
||||||
|
@ -342,25 +340,15 @@ where
|
||||||
let target = self.decode_lower_effective_address(ins, Some(Size::Byte))?;
|
let target = self.decode_lower_effective_address(ins, Some(Size::Byte))?;
|
||||||
Ok(Instruction::NBCD(target))
|
Ok(Instruction::NBCD(target))
|
||||||
},
|
},
|
||||||
(0b001, 0b000) => {
|
(0b001, 0b000) => Ok(Instruction::SWAP(get_low_reg(ins))),
|
||||||
Ok(Instruction::SWAP(get_low_reg(ins)))
|
(0b001, 0b001) => Ok(Instruction::BKPT(get_low_reg(ins))),
|
||||||
},
|
|
||||||
(0b001, 0b001) => {
|
|
||||||
Ok(Instruction::BKPT(get_low_reg(ins)))
|
|
||||||
},
|
|
||||||
(0b001, _) => {
|
(0b001, _) => {
|
||||||
let target = self.decode_lower_effective_address(ins, None)?;
|
let target = self.decode_lower_effective_address(ins, None)?;
|
||||||
Ok(Instruction::PEA(target))
|
Ok(Instruction::PEA(target))
|
||||||
},
|
},
|
||||||
(0b010, 0b000) => {
|
(0b010, 0b000) => Ok(Instruction::EXT(get_low_reg(ins), Size::Byte, Size::Word)),
|
||||||
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)),
|
||||||
(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)),
|
_ => Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
||||||
}
|
}
|
||||||
} else if ins_0f00 == 0xA00 {
|
} else if ins_0f00 == 0xA00 {
|
||||||
|
@ -393,7 +381,11 @@ where
|
||||||
}
|
}
|
||||||
} else if ins_00f0 == 0x60 {
|
} else if ins_00f0 == 0x60 {
|
||||||
let reg = get_low_reg(ins);
|
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))
|
Ok(Instruction::MOVEUSP(Target::DirectAReg(reg), dir))
|
||||||
} else {
|
} else {
|
||||||
match ins & 0x00FF {
|
match ins & 0x00FF {
|
||||||
|
@ -412,7 +404,11 @@ where
|
||||||
0x76 => Ok(Instruction::TRAPV),
|
0x76 => Ok(Instruction::TRAPV),
|
||||||
0x77 => Ok(Instruction::RTR),
|
0x77 => Ok(Instruction::RTR),
|
||||||
0x7A | 0x7B if self.decoder.cputype >= M68kType::MC68010 => {
|
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 ins2 = self.read_instruction_word()?;
|
||||||
let target = match ins2 & 0x8000 {
|
let target = match ins2 & 0x8000 {
|
||||||
0 => Target::DirectDReg(((ins2 & 0x7000) >> 12) as u8),
|
0 => Target::DirectDReg(((ins2 & 0x7000) >> 12) as u8),
|
||||||
|
@ -511,7 +507,11 @@ where
|
||||||
} else if let Some(size) = size {
|
} else if let Some(size) = size {
|
||||||
let data_reg = Target::DirectDReg(get_high_reg(ins));
|
let data_reg = Target::DirectDReg(get_high_reg(ins));
|
||||||
let effective_addr = self.decode_lower_effective_address(ins, Some(size))?;
|
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))
|
Ok(Instruction::OR(from, to, size))
|
||||||
} else {
|
} else {
|
||||||
let sign = if (ins & 0x0100) == 0 { Sign::Unsigned } else { Sign::Signed };
|
let sign = if (ins & 0x0100) == 0 { Sign::Unsigned } else { Sign::Signed };
|
||||||
|
@ -602,7 +602,11 @@ where
|
||||||
} else if let Some(size) = size {
|
} else if let Some(size) = size {
|
||||||
let data_reg = Target::DirectDReg(get_high_reg(ins));
|
let data_reg = Target::DirectDReg(get_high_reg(ins));
|
||||||
let effective_addr = self.decode_lower_effective_address(ins, Some(size))?;
|
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))
|
Ok(Instruction::AND(from, to, size))
|
||||||
} else {
|
} else {
|
||||||
let sign = if (ins & 0x0100) == 0 { Sign::Unsigned } else { Sign::Signed };
|
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>> {
|
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;
|
self.decoder.end += 2;
|
||||||
Ok(word)
|
Ok(word)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_instruction_long(&mut self) -> Result<u32, M68kError<Bus::Error>> {
|
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;
|
self.decoder.end += 4;
|
||||||
Ok(word)
|
Ok(word)
|
||||||
}
|
}
|
||||||
|
@ -769,11 +777,23 @@ where
|
||||||
|
|
||||||
// Decode Index Register
|
// Decode Index Register
|
||||||
let xreg_num = ((brief_extension & 0x7000) >> 12) as u8;
|
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 xreg = if (brief_extension & 0x8000) == 0 {
|
||||||
let size = if (brief_extension & 0x0800) == 0 { Size::Word } else { Size::Long };
|
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 {
|
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);
|
let displacement = sign_extend_to_long((brief_extension & 0x00FF) as u32, Size::Byte);
|
||||||
|
|
||||||
match areg {
|
match areg {
|
||||||
|
@ -782,7 +802,11 @@ where
|
||||||
}
|
}
|
||||||
} else if use_brief {
|
} else if use_brief {
|
||||||
let scale = ((brief_extension & 0x0600) >> 9) as u8;
|
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);
|
let displacement = sign_extend_to_long((brief_extension & 0x00FF) as u32, Size::Byte);
|
||||||
|
|
||||||
match areg {
|
match areg {
|
||||||
|
@ -791,7 +815,11 @@ where
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let scale = ((brief_extension & 0x0600) >> 9) as u8;
|
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_base_reg = (brief_extension & 0x0080) == 0;
|
||||||
let use_index = (brief_extension & 0x0040) == 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);
|
let displacement = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word);
|
||||||
Target::IndirectRegOffset(BaseRegister::AReg(reg), None, displacement)
|
Target::IndirectRegOffset(BaseRegister::AReg(reg), None, displacement)
|
||||||
},
|
},
|
||||||
0b110 => {
|
0b110 => self.decode_extension_word(Some(reg))?,
|
||||||
self.decode_extension_word(Some(reg))?
|
0b111 => match reg {
|
||||||
},
|
0b000 => {
|
||||||
0b111 => {
|
let value = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word) as u32;
|
||||||
match reg {
|
Target::IndirectMemory(value, Size::Word)
|
||||||
0b000 => {
|
},
|
||||||
let value = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word) as u32;
|
0b001 => {
|
||||||
Target::IndirectMemory(value, Size::Word)
|
let value = self.read_instruction_long()?;
|
||||||
},
|
Target::IndirectMemory(value, Size::Long)
|
||||||
0b001 => {
|
},
|
||||||
let value = self.read_instruction_long()?;
|
0b010 => {
|
||||||
Target::IndirectMemory(value, Size::Long)
|
let displacement = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word);
|
||||||
},
|
Target::IndirectRegOffset(BaseRegister::PC, None, displacement)
|
||||||
0b010 => {
|
},
|
||||||
let displacement = sign_extend_to_long(self.read_instruction_word()? as u32, Size::Word);
|
0b011 => self.decode_extension_word(None)?,
|
||||||
Target::IndirectRegOffset(BaseRegister::PC, None, displacement)
|
0b100 => {
|
||||||
},
|
let data = match size {
|
||||||
0b011 => {
|
Some(Size::Byte) | Some(Size::Word) => self.read_instruction_word()? as u32,
|
||||||
self.decode_extension_word(None)?
|
Some(Size::Long) => self.read_instruction_long()?,
|
||||||
},
|
None => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
||||||
0b100 => {
|
};
|
||||||
let data = match size {
|
Target::Immediate(data)
|
||||||
Some(Size::Byte) | Some(Size::Word) => self.read_instruction_word()? as u32,
|
},
|
||||||
Some(Size::Long) => self.read_instruction_long()?,
|
_ => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
||||||
None => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
|
||||||
};
|
|
||||||
Target::Immediate(data)
|
|
||||||
},
|
|
||||||
_ => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
_ => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
_ => return Err(M68kError::Exception(Exceptions::IllegalInstruction)),
|
||||||
};
|
};
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -917,4 +937,3 @@ fn get_condition(ins: u16) -> Condition {
|
||||||
_ => Condition::True,
|
_ => Condition::True,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::Instant;
|
use femtos::Instant;
|
||||||
use emulator_hal::bus::{self, BusAccess};
|
use emulator_hal::bus::{self, BusAccess};
|
||||||
use emulator_hal::step::Step;
|
use emulator_hal::step::Step;
|
||||||
|
@ -9,19 +8,8 @@ use crate::decode::M68kDecoder;
|
||||||
use crate::debugger::M68kDebugger;
|
use crate::debugger::M68kDebugger;
|
||||||
use crate::timing::M68kInstructionTiming;
|
use crate::timing::M68kInstructionTiming;
|
||||||
use crate::instructions::{
|
use crate::instructions::{
|
||||||
Register,
|
Register, Size, Sign, Direction, XRegister, BaseRegister, IndexRegister, RegOrImmediate, ControlRegister, Condition, Target,
|
||||||
Size,
|
Instruction, sign_extend_to_long,
|
||||||
Sign,
|
|
||||||
Direction,
|
|
||||||
XRegister,
|
|
||||||
BaseRegister,
|
|
||||||
IndexRegister,
|
|
||||||
RegOrImmediate,
|
|
||||||
ControlRegister,
|
|
||||||
Condition,
|
|
||||||
Target,
|
|
||||||
Instruction,
|
|
||||||
sign_extend_to_long,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,7 +43,7 @@ impl M68kCycle {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(cpu: &M68k, clock: Instant) -> Self {
|
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 {
|
Self {
|
||||||
decoder: M68kDecoder::new(cpu.info.chip, is_supervisor, cpu.state.pc),
|
decoder: M68kDecoder::new(cpu.info.chip, is_supervisor, cpu.state.pc),
|
||||||
timing: M68kInstructionTiming::new(cpu.info.chip, cpu.info.data_width as u8),
|
timing: M68kInstructionTiming::new(cpu.info.chip, cpu.info.data_width as u8),
|
||||||
|
@ -174,7 +162,10 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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;
|
let ack_num;
|
||||||
(self.state.pending_ipl, ack_num) = match interrupt {
|
(self.state.pending_ipl, ack_num) = match interrupt {
|
||||||
(true, priority, ack) => (InterruptPriority::from_u8(priority), ack),
|
(true, priority, ack) => (InterruptPriority::from_u8(priority), ack),
|
||||||
|
@ -300,7 +291,9 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn decode_next(&mut self) -> Result<(), M68kError<Bus::Error>> {
|
pub fn decode_next(&mut self) -> Result<(), M68kError<Bus::Error>> {
|
||||||
let is_supervisor = self.is_supervisor();
|
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);
|
self.cycle.timing.add_instruction(&self.cycle.decoder.instruction);
|
||||||
|
|
||||||
|
@ -400,7 +393,9 @@ where
|
||||||
Instruction::UNLK(reg) => self.execute_unlk(reg),
|
Instruction::UNLK(reg) => self.execute_unlk(reg),
|
||||||
Instruction::UnimplementedA(value) => self.execute_unimplemented_a(value),
|
Instruction::UnimplementedA(value) => self.execute_unimplemented_a(value),
|
||||||
Instruction::UnimplementedF(value) => self.execute_unimplemented_f(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(())
|
Ok(())
|
||||||
|
@ -416,9 +411,13 @@ where
|
||||||
|
|
||||||
let binary_result = src_val.wrapping_add(dest_val).wrapping_add(extend_flag);
|
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);
|
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;
|
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;
|
let carry = (result & 0xFFFFFF00) != 0;
|
||||||
|
|
||||||
self.set_target_value(dest, result, Size::Byte, Used::Twice)?;
|
self.set_target_value(dest, result, Size::Byte, Used::Twice)?;
|
||||||
|
@ -607,7 +606,12 @@ where
|
||||||
Ok(())
|
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 (offset, width) = self.get_bit_field_args(offset, width);
|
||||||
let mask = get_bit_field_mask(offset, width);
|
let mask = get_bit_field_mask(offset, width);
|
||||||
let value = self.get_target_value(target, Size::Long, Used::Twice)?;
|
let value = self.get_target_value(target, Size::Long, Used::Twice)?;
|
||||||
|
@ -617,7 +621,12 @@ where
|
||||||
Ok(())
|
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 (offset, width) = self.get_bit_field_args(offset, width);
|
||||||
let mask = get_bit_field_mask(offset, width);
|
let mask = get_bit_field_mask(offset, width);
|
||||||
let value = self.get_target_value(target, Size::Long, Used::Twice)?;
|
let value = self.get_target_value(target, Size::Long, Used::Twice)?;
|
||||||
|
@ -627,7 +636,13 @@ where
|
||||||
Ok(())
|
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 (offset, width) = self.get_bit_field_args(offset, width);
|
||||||
let mask = get_bit_field_mask(offset, width);
|
let mask = get_bit_field_mask(offset, width);
|
||||||
let value = self.get_target_value(target, Size::Long, Used::Once)?;
|
let value = self.get_target_value(target, Size::Long, Used::Once)?;
|
||||||
|
@ -643,7 +658,13 @@ where
|
||||||
Ok(())
|
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 (offset, width) = self.get_bit_field_args(offset, width);
|
||||||
let mask = get_bit_field_mask(offset, width);
|
let mask = get_bit_field_mask(offset, width);
|
||||||
let value = self.get_target_value(target, Size::Long, Used::Once)?;
|
let value = self.get_target_value(target, Size::Long, Used::Once)?;
|
||||||
|
@ -653,7 +674,12 @@ where
|
||||||
Ok(())
|
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 (offset, width) = self.get_bit_field_args(offset, width);
|
||||||
let mask = get_bit_field_mask(offset, width);
|
let mask = get_bit_field_mask(offset, width);
|
||||||
let value = self.get_target_value(target, Size::Long, Used::Twice)?;
|
let value = self.get_target_value(target, Size::Long, Used::Twice)?;
|
||||||
|
@ -663,7 +689,12 @@ where
|
||||||
Ok(())
|
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 (offset, width) = self.get_bit_field_args(offset, width);
|
||||||
let mask = get_bit_field_mask(offset, width);
|
let mask = get_bit_field_mask(offset, width);
|
||||||
let value = self.get_target_value(target, Size::Long, Used::Once)?;
|
let value = self.get_target_value(target, Size::Long, Used::Once)?;
|
||||||
|
@ -749,16 +780,12 @@ where
|
||||||
(
|
(
|
||||||
(dest_val % src_val) as u32,
|
(dest_val % src_val) as u32,
|
||||||
quotient 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 => {
|
Sign::Unsigned => {
|
||||||
let quotient = dest_val / src_val;
|
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(())
|
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)?;
|
let src_val = self.get_target_value(src, Size::Long, Used::Once)?;
|
||||||
if src_val == 0 {
|
if src_val == 0 {
|
||||||
self.exception(Exceptions::ZeroDivide as u8, false)?;
|
self.exception(Exceptions::ZeroDivide as u8, false)?;
|
||||||
|
@ -988,8 +1021,8 @@ where
|
||||||
Target::IndirectARegInc(reg) | Target::IndirectARegDec(reg) => {
|
Target::IndirectARegInc(reg) | Target::IndirectARegDec(reg) => {
|
||||||
let a_reg_mut = self.get_a_reg_mut(reg);
|
let a_reg_mut = self.get_a_reg_mut(reg);
|
||||||
*a_reg_mut = addr + (mask.count_ones() * size.in_bytes());
|
*a_reg_mut = addr + (mask.count_ones() * size.in_bytes());
|
||||||
}
|
},
|
||||||
_ => { },
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1006,11 +1039,9 @@ where
|
||||||
}
|
}
|
||||||
self.move_registers_to_memory_reverse(addr, size, mask)?
|
self.move_registers_to_memory_reverse(addr, size, mask)?
|
||||||
},
|
},
|
||||||
_ => {
|
_ => match dir {
|
||||||
match dir {
|
Direction::ToTarget => self.move_registers_to_memory(addr, size, mask)?,
|
||||||
Direction::ToTarget => self.move_registers_to_memory(addr, size, mask)?,
|
Direction::FromTarget => self.move_memory_to_registers(addr, size, mask)?,
|
||||||
Direction::FromTarget => self.move_memory_to_registers(addr, size, mask)?,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1019,8 +1050,8 @@ where
|
||||||
Target::IndirectARegInc(reg) | Target::IndirectARegDec(reg) => {
|
Target::IndirectARegInc(reg) | Target::IndirectARegDec(reg) => {
|
||||||
let a_reg_mut = self.get_a_reg_mut(reg);
|
let a_reg_mut = self.get_a_reg_mut(reg);
|
||||||
*a_reg_mut = post_addr;
|
*a_reg_mut = post_addr;
|
||||||
}
|
},
|
||||||
_ => { },
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1082,7 +1113,14 @@ where
|
||||||
Ok(addr)
|
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 {
|
match dir {
|
||||||
Direction::ToTarget => {
|
Direction::ToTarget => {
|
||||||
let mut shift = (size.in_bits() as i32) - 8;
|
let mut shift = (size.in_bits() as i32) - 8;
|
||||||
|
@ -1119,7 +1157,9 @@ where
|
||||||
self.require_supervisor()?;
|
self.require_supervisor()?;
|
||||||
match dir {
|
match dir {
|
||||||
Direction::ToTarget => self.set_target_value(target, self.state.usp, Size::Long, Used::Once)?,
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1137,7 +1177,13 @@ where
|
||||||
Ok(())
|
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 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 dest_val = get_value_sized(self.state.d_reg[dest_l as usize], Size::Long);
|
||||||
let result = match sign {
|
let result = match sign {
|
||||||
|
@ -1353,10 +1399,14 @@ where
|
||||||
|
|
||||||
let binary_result = dest_val.wrapping_sub(src_val).wrapping_sub(extend_flag);
|
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);
|
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));
|
result = result.wrapping_add(dest_parts.0.wrapping_sub(src_parts.0));
|
||||||
let carry = (result & 0x1FF) > 0x99;
|
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::Negative, get_msb(result, Size::Byte));
|
||||||
self.set_flag(Flags::Zero, (result & 0xFF) == 0);
|
self.set_flag(Flags::Zero, (result & 0xFF) == 0);
|
||||||
|
@ -1489,7 +1539,8 @@ where
|
||||||
Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => {
|
Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => {
|
||||||
let base_value = self.get_base_reg_value(base_reg);
|
let base_value = self.get_base_reg_value(base_reg);
|
||||||
let index_value = self.get_index_reg_value(&index_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)
|
self.get_address_sized(intermediate.wrapping_add(outer_disp as u32), size)
|
||||||
},
|
},
|
||||||
Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => {
|
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)?;
|
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)
|
self.get_address_sized(intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32), size)
|
||||||
},
|
},
|
||||||
Target::IndirectMemory(addr, _) => {
|
Target::IndirectMemory(addr, _) => self.get_address_sized(addr, size),
|
||||||
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 {
|
match target {
|
||||||
Target::DirectDReg(reg) => {
|
Target::DirectDReg(reg) => {
|
||||||
set_value_sized(&mut self.state.d_reg[reg as usize], value, size);
|
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) => {
|
Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => {
|
||||||
let base_value = self.get_base_reg_value(base_reg);
|
let base_value = self.get_base_reg_value(base_reg);
|
||||||
let index_value = self.get_index_reg_value(&index_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)?;
|
self.set_address_sized(intermediate.wrapping_add(outer_disp as u32), value, size)?;
|
||||||
},
|
},
|
||||||
Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => {
|
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) => {
|
Target::IndirectMemoryPreindexed(base_reg, index_reg, base_disp, outer_disp) => {
|
||||||
let base_value = self.get_base_reg_value(base_reg);
|
let base_value = self.get_base_reg_value(base_reg);
|
||||||
let index_value = self.get_index_reg_value(&index_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)
|
intermediate.wrapping_add(outer_disp as u32)
|
||||||
},
|
},
|
||||||
Target::IndirectMemoryPostindexed(base_reg, index_reg, base_disp, outer_disp) => {
|
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)?;
|
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)
|
intermediate.wrapping_add(index_value as u32).wrapping_add(outer_disp as u32)
|
||||||
},
|
},
|
||||||
Target::IndirectMemory(addr, _) => {
|
Target::IndirectMemory(addr, _) => addr,
|
||||||
addr
|
|
||||||
},
|
|
||||||
_ => return Err(M68kError::InvalidTarget(target)),
|
_ => return Err(M68kError::InvalidTarget(target)),
|
||||||
};
|
};
|
||||||
Ok(addr)
|
Ok(addr)
|
||||||
|
@ -1611,21 +1666,28 @@ where
|
||||||
|
|
||||||
fn set_address_sized(&mut self, addr: M68kAddress, value: u32, size: Size) -> Result<(), M68kError<Bus::Error>> {
|
fn set_address_sized(&mut self, addr: M68kAddress, value: u32, size: Size) -> Result<(), M68kError<Bus::Error>> {
|
||||||
let is_supervisor = self.is_supervisor();
|
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>> {
|
fn push_word(&mut self, value: u16) -> Result<(), M68kError<Bus::Error>> {
|
||||||
let is_supervisor = self.is_supervisor();
|
let is_supervisor = self.is_supervisor();
|
||||||
*self.get_stack_pointer_mut() -= 2;
|
*self.get_stack_pointer_mut() -= 2;
|
||||||
let addr = *self.get_stack_pointer_mut();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_word(&mut self) -> Result<u16, M68kError<Bus::Error>> {
|
fn pop_word(&mut self) -> Result<u16, M68kError<Bus::Error>> {
|
||||||
let is_supervisor = self.is_supervisor();
|
let is_supervisor = self.is_supervisor();
|
||||||
let addr = *self.get_stack_pointer_mut();
|
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;
|
*self.get_stack_pointer_mut() += 2;
|
||||||
Ok(value as u16)
|
Ok(value as u16)
|
||||||
}
|
}
|
||||||
|
@ -1634,21 +1696,33 @@ where
|
||||||
let is_supervisor = self.is_supervisor();
|
let is_supervisor = self.is_supervisor();
|
||||||
*self.get_stack_pointer_mut() -= 4;
|
*self.get_stack_pointer_mut() -= 4;
|
||||||
let addr = *self.get_stack_pointer_mut();
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_long(&mut self) -> Result<u32, M68kError<Bus::Error>> {
|
fn pop_long(&mut self) -> Result<u32, M68kError<Bus::Error>> {
|
||||||
let is_supervisor = self.is_supervisor();
|
let is_supervisor = self.is_supervisor();
|
||||||
let addr = *self.get_stack_pointer_mut();
|
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;
|
*self.get_stack_pointer_mut() += 4;
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_pc(&mut self, value: u32) -> Result<(), M68kError<Bus::Error>> {
|
fn set_pc(&mut self, value: u32) -> Result<(), M68kError<Bus::Error>> {
|
||||||
self.state.pc = value;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1679,7 +1753,13 @@ where
|
||||||
match base_reg {
|
match base_reg {
|
||||||
BaseRegister::None => 0,
|
BaseRegister::None => 0,
|
||||||
BaseRegister::PC => self.cycle.decoder.start + 2,
|
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],
|
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 {
|
fn get_index_reg_value(&self, index_reg: &Option<IndexRegister>) -> i32 {
|
||||||
match index_reg {
|
match index_reg {
|
||||||
None => 0,
|
None => 0,
|
||||||
Some(IndexRegister { xreg, scale, size }) => {
|
Some(IndexRegister {
|
||||||
sign_extend_to_long(self.get_x_reg_value(*xreg), *size) << scale
|
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 {
|
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 {
|
fn get_a_reg(&self, reg: Register) -> u32 {
|
||||||
if reg == 7 {
|
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 {
|
} else {
|
||||||
self.state.a_reg[reg as usize]
|
self.state.a_reg[reg as usize]
|
||||||
}
|
}
|
||||||
|
@ -1713,14 +1803,18 @@ where
|
||||||
|
|
||||||
fn get_a_reg_mut(&mut self, reg: Register) -> &mut u32 {
|
fn get_a_reg_mut(&mut self, reg: Register) -> &mut u32 {
|
||||||
if reg == 7 {
|
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 {
|
} else {
|
||||||
&mut self.state.a_reg[reg as usize]
|
&mut self.state.a_reg[reg as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_supervisor(&self) -> bool {
|
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>> {
|
fn require_supervisor(&self) -> Result<(), M68kError<Bus::Error>> {
|
||||||
|
@ -1732,7 +1826,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_sr(&mut self, value: u16) {
|
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;
|
self.state.sr = value & mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1805,15 +1903,23 @@ where
|
||||||
Condition::OverflowSet => self.get_flag(Flags::Overflow),
|
Condition::OverflowSet => self.get_flag(Flags::Overflow),
|
||||||
Condition::Plus => !self.get_flag(Flags::Negative),
|
Condition::Plus => !self.get_flag(Flags::Negative),
|
||||||
Condition::Minus => 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::GreaterThanOrEqual => {
|
||||||
Condition::LessThan => (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))
|
||||||
Condition::GreaterThan =>
|
|| (!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))
|
||||||
|| (!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 =>
|
},
|
||||||
|
Condition::LessThanOrEqual => {
|
||||||
self.get_flag(Flags::Zero)
|
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) {
|
fn rotate_right(value: u32, size: Size, use_extend: Option<bool>) -> (u32, bool) {
|
||||||
let bit = (value & 0x01) != 0;
|
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)
|
((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 {
|
fn get_value_sized(value: u32, size: Size) -> u32 {
|
||||||
match size {
|
match size {
|
||||||
Size::Byte => { 0x000000FF & value },
|
Size::Byte => 0x000000FF & value,
|
||||||
Size::Word => { 0x0000FFFF & value },
|
Size::Word => 0x0000FFFF & value,
|
||||||
Size::Long => { value },
|
Size::Long => value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_value_sized(addr: &mut u32, value: u32, size: Size) {
|
fn set_value_sized(addr: &mut u32, value: u32, size: Size) {
|
||||||
match size {
|
match size {
|
||||||
Size::Byte => { *addr = (*addr & 0xFFFFFF00) | (0x000000FF & value); }
|
Size::Byte => {
|
||||||
Size::Word => { *addr = (*addr & 0xFFFF0000) | (0x0000FFFF & value); }
|
*addr = (*addr & 0xFFFFFF00) | (0x000000FF & value);
|
||||||
Size::Long => { *addr = 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 {
|
fn get_bit_field_msb(offset: u32) -> u32 {
|
||||||
0x80000000 >> offset
|
0x80000000 >> offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
|
|
||||||
|
@ -336,10 +335,12 @@ impl fmt::Display for Target {
|
||||||
let index_str = fmt_index_disp(index_reg);
|
let index_str = fmt_index_disp(index_reg);
|
||||||
write!(f, "([{}#{:08x}]{} + #{:08x})", base_reg, base_disp, index_str, outer_disp)
|
write!(f, "([{}#{:08x}]{} + #{:08x})", base_reg, base_disp, index_str, outer_disp)
|
||||||
},
|
},
|
||||||
Target::IndirectMemory(value, size) => if *size == Size::Word {
|
Target::IndirectMemory(value, size) => {
|
||||||
write!(f, "(#{:04x})", value)
|
if *size == Size::Word {
|
||||||
} else {
|
write!(f, "(#{:04x})", value)
|
||||||
write!(f, "(#{:08x})", 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::EORtoCCR(value) => write!(f, "eorib\t#{:#02x}, %ccr", value),
|
||||||
Instruction::EORtoSR(value) => write!(f, "eoriw\t#{:#04x}, %sr", value),
|
Instruction::EORtoSR(value) => write!(f, "eoriw\t#{:#04x}, %sr", value),
|
||||||
Instruction::EXG(src, dest) => write!(f, "exg\t{}, {}", src, dest),
|
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"),
|
Instruction::ILLEGAL => write!(f, "illegal"),
|
||||||
|
|
||||||
|
@ -517,4 +528,3 @@ impl fmt::Display for Instruction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
|
|
||||||
pub mod assembler;
|
pub mod assembler;
|
||||||
pub mod state;
|
pub mod debugger;
|
||||||
pub mod decode;
|
pub mod decode;
|
||||||
pub mod execute;
|
pub mod execute;
|
||||||
pub mod debugger;
|
|
||||||
pub mod instructions;
|
pub mod instructions;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod timing;
|
pub mod state;
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
|
pub mod timing;
|
||||||
|
|
||||||
#[cfg(feature = "moa")]
|
#[cfg(feature = "moa")]
|
||||||
pub mod moa;
|
pub mod moa;
|
||||||
|
|
||||||
pub use crate::state::{M68k, M68kType, M68kError};
|
pub use crate::state::{M68k, M68kType, M68kError};
|
||||||
pub use crate::memory::{M68kAddress, M68kAddressSpace};
|
pub use crate::memory::{M68kAddress, M68kAddressSpace};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use femtos::Instant;
|
use femtos::Instant;
|
||||||
|
@ -10,6 +9,7 @@ use crate::instructions::Size;
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[rustfmt::skip]
|
||||||
pub enum FunctionCode {
|
pub enum FunctionCode {
|
||||||
Reserved0 = 0,
|
Reserved0 = 0,
|
||||||
UserData = 1,
|
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
|
where
|
||||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
||||||
{
|
{
|
||||||
|
@ -162,7 +168,13 @@ impl M68kBusPort {
|
||||||
Ok(())
|
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
|
where
|
||||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
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::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::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]),
|
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
|
where
|
||||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
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
|
where
|
||||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
||||||
{
|
{
|
||||||
|
@ -208,7 +233,14 @@ impl M68kBusPort {
|
||||||
self.read_sized(bus, addr, size)
|
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
|
where
|
||||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
||||||
{
|
{
|
||||||
|
@ -216,7 +248,12 @@ impl M68kBusPort {
|
||||||
self.write_sized(bus, addr, size, value)
|
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
|
where
|
||||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
||||||
{
|
{
|
||||||
|
@ -224,7 +261,12 @@ impl M68kBusPort {
|
||||||
Ok(self.read_sized(bus, addr, Size::Word)? as u16)
|
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
|
where
|
||||||
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
Bus: BusAccess<M68kAddress, Instant, Error = BusError>,
|
||||||
{
|
{
|
||||||
|
@ -232,7 +274,15 @@ impl M68kBusPort {
|
||||||
self.read_sized(bus, addr, Size::Long)
|
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.i_n_bit = i_n_bit;
|
||||||
self.request.code = match mtype {
|
self.request.code = match mtype {
|
||||||
MemType::Program => FunctionCode::program(is_supervisor),
|
MemType::Program => FunctionCode::program(is_supervisor),
|
||||||
|
@ -314,7 +364,7 @@ impl TargetAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
|
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +407,7 @@ impl ReadOnceAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
|
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,7 +426,7 @@ impl ReadUpdateAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
|
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,7 +441,7 @@ impl WriteOnceAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
|
pub(crate) fn complete(&self) -> Result<Self, M68kError> {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration};
|
use femtos::{Instant, Duration};
|
||||||
use emulator_hal::bus;
|
use emulator_hal::bus;
|
||||||
|
|
||||||
|
@ -13,11 +12,8 @@ impl Steppable for M68k {
|
||||||
let cycle = M68kCycle::new(self, system.clock);
|
let cycle = M68kCycle::new(self, system.clock);
|
||||||
|
|
||||||
let mut bus = system.bus.borrow_mut();
|
let mut bus = system.bus.borrow_mut();
|
||||||
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> = bus::BusAdapter::new(
|
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> =
|
||||||
&mut *bus,
|
bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err);
|
||||||
|addr| addr as u64,
|
|
||||||
|err| err,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut executor = cycle.begin(self, &mut adapter);
|
let mut executor = cycle.begin(self, &mut adapter);
|
||||||
executor.check_breakpoints()?;
|
executor.check_breakpoints()?;
|
||||||
|
@ -40,7 +36,7 @@ impl Steppable for M68k {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interruptable for M68k { }
|
impl Interruptable for M68k {}
|
||||||
|
|
||||||
impl Transmutable for M68k {
|
impl Transmutable for M68k {
|
||||||
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
|
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 decoder = M68kDecoder::new(self.info.chip, true, 0);
|
||||||
|
|
||||||
let mut bus = system.bus.borrow_mut();
|
let mut bus = system.bus.borrow_mut();
|
||||||
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> = bus::BusAdapter::new(
|
let mut adapter: bus::BusAdapter<u32, u64, Instant, &mut dyn Addressable, Error> =
|
||||||
&mut *bus,
|
bus::BusAdapter::new(&mut *bus, |addr| addr as u64, |err| err);
|
||||||
|addr| addr as u64,
|
|
||||||
|err| err,
|
|
||||||
);
|
|
||||||
|
|
||||||
decoder.dump_disassembly(&mut adapter, addr as u32, count as u32);
|
decoder.dump_disassembly(&mut adapter, addr as u32, count as u32);
|
||||||
}
|
}
|
||||||
|
@ -124,9 +117,10 @@ impl Debuggable for M68k {
|
||||||
"so" | "stepout" => {
|
"so" | "stepout" => {
|
||||||
self.debugger.step_until_return = Some(self.debugger.stack_tracer.calls.len() - 1);
|
self.debugger.step_until_return = Some(self.debugger.stack_tracer.calls.len() - 1);
|
||||||
},
|
},
|
||||||
_ => { return Ok(true); },
|
_ => {
|
||||||
|
return Ok(true);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use core::fmt::{self, Write};
|
use core::fmt::{self, Write};
|
||||||
use femtos::{Duration, Frequency};
|
use femtos::{Duration, Frequency};
|
||||||
|
|
||||||
|
@ -14,10 +13,10 @@ pub type ClockCycles = u16;
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum AddressWidth {
|
pub enum AddressWidth {
|
||||||
A32 = 32, // MC68020+
|
A32 = 32, // MC68020+
|
||||||
A24 = 24, // MC68000 64-Pin, MC68010
|
A24 = 24, // MC68000 64-Pin, MC68010
|
||||||
A22 = 22, // MC68008 52-Pin
|
A22 = 22, // MC68008 52-Pin
|
||||||
A20 = 20, // MC68008 48-Pin
|
A20 = 20, // MC68008 48-Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -99,7 +98,7 @@ impl CpuInfo {
|
||||||
address_width: AddressWidth::A32,
|
address_width: AddressWidth::A32,
|
||||||
data_width: DataWidth::D32,
|
data_width: DataWidth::D32,
|
||||||
frequency,
|
frequency,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +108,7 @@ const FLAGS_ON_RESET: u16 = 0x2700;
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[rustfmt::skip]
|
||||||
pub enum Flags {
|
pub enum Flags {
|
||||||
Carry = 0x0001,
|
Carry = 0x0001,
|
||||||
Overflow = 0x0002,
|
Overflow = 0x0002,
|
||||||
|
@ -124,6 +124,7 @@ pub enum Flags {
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[rustfmt::skip]
|
||||||
pub enum Exceptions {
|
pub enum Exceptions {
|
||||||
BusError = 2,
|
BusError = 2,
|
||||||
AddressError = 3,
|
AddressError = 3,
|
||||||
|
@ -298,4 +299,3 @@ impl InterruptPriority {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod decode_unit_tests {
|
mod decode_unit_tests {
|
||||||
use femtos::Instant;
|
use femtos::Instant;
|
||||||
|
@ -96,7 +95,10 @@ mod decode_unit_tests {
|
||||||
let size = Size::Long;
|
let size = Size::Long;
|
||||||
let offset = -8;
|
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();
|
let target = decoder.get_mode_as_target(0b101, 0b100, Some(size)).unwrap();
|
||||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::AReg(4), None, offset));
|
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);
|
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, 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();
|
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();
|
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();
|
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();
|
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();
|
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 size = Size::Long;
|
||||||
let offset = -8;
|
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();
|
let target = decoder.get_mode_as_target(0b111, 0b010, Some(size)).unwrap();
|
||||||
assert_eq!(target, Target::IndirectRegOffset(BaseRegister::PC, None, offset));
|
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);
|
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, 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();
|
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();
|
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();
|
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
|
// Insert basic initialization
|
||||||
let len = 0x10_0000;
|
let len = 0x10_0000;
|
||||||
let mut data = Vec::with_capacity(len);
|
let mut data = Vec::with_capacity(len);
|
||||||
unsafe { data.set_len(len); }
|
unsafe {
|
||||||
|
data.set_len(len);
|
||||||
|
}
|
||||||
let mut memory = MemoryBlock::from(data);
|
let mut memory = MemoryBlock::from(data);
|
||||||
memory.write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap();
|
memory.write_beu32(Instant::START, 0, INIT_STACK as u32).unwrap();
|
||||||
memory.write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
memory.write_beu32(Instant::START, 4, INIT_ADDR as u32).unwrap();
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use crate::M68kType;
|
use crate::M68kType;
|
||||||
use crate::state::ClockCycles;
|
use crate::state::ClockCycles;
|
||||||
use crate::instructions::{Size, Sign, Direction, Target, Instruction};
|
use crate::instructions::{Size, Sign, Direction, Target, Instruction};
|
||||||
|
@ -69,8 +68,7 @@ impl M68kInstructionTiming {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_reg_v_mem(&mut self, target: &Target, reg: u8, mem: u8) -> &mut Self {
|
pub fn add_reg_v_mem(&mut self, target: &Target, reg: u8, mem: u8) -> &mut Self {
|
||||||
self.internal += match target {
|
self.internal += match target {
|
||||||
Target::DirectDReg(_) |
|
Target::DirectDReg(_) | Target::DirectAReg(_) => reg,
|
||||||
Target::DirectAReg(_) => reg,
|
|
||||||
_ => mem,
|
_ => mem,
|
||||||
};
|
};
|
||||||
self
|
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 {
|
pub fn add_immediate_set(&mut self, size: Size, target: &Target, reg: (u8, u8), mem: (u8, u8)) -> &mut Self {
|
||||||
match target {
|
match target {
|
||||||
Target::DirectDReg(_) | Target::DirectAReg(_) => self.add_word_v_long(size, reg.0, reg.1),
|
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 {
|
pub fn add_reg_mem_set(&mut self, size: Size, target: &Target, reg: (u8, u8), mem: (u8, u8)) -> &mut Self {
|
||||||
match target {
|
match target {
|
||||||
Target::DirectDReg(_) | Target::DirectAReg(_) => self.add_word_v_long(size, reg.0, reg.1),
|
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::IndirectAReg(_) => self.add_access(size),
|
||||||
Target::IndirectARegInc(_) => self.add_access(size),
|
Target::IndirectARegInc(_) => self.add_access(size),
|
||||||
Target::IndirectARegDec(_) => self.add_access(size).add_internal(2),
|
Target::IndirectARegDec(_) => self.add_access(size).add_internal(2),
|
||||||
Target::IndirectRegOffset(_, index_reg, _) => {
|
Target::IndirectRegOffset(_, index_reg, _) => match index_reg {
|
||||||
match index_reg {
|
None => self.add_access(size).add_internal(4),
|
||||||
None => self.add_access(size).add_internal(4),
|
Some(_) => self.add_access(size).add_internal(6),
|
||||||
Some(_) => self.add_access(size).add_internal(6),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Target::IndirectMemoryPreindexed(_, index_reg, _, _) |
|
Target::IndirectMemoryPreindexed(_, index_reg, _, _) | Target::IndirectMemoryPostindexed(_, index_reg, _, _) => {
|
||||||
Target::IndirectMemoryPostindexed(_, index_reg, _, _) => {
|
|
||||||
// TODO this is very wrong, but the 68020 timings are complicated
|
// TODO this is very wrong, but the 68020 timings are complicated
|
||||||
match index_reg {
|
match index_reg {
|
||||||
None => self.add_access(size).add_internal(4),
|
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 {
|
pub fn add_two_targets(&mut self, size: Size, src: &Target, dest: &Target) -> &mut Self {
|
||||||
match (src, dest) {
|
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),
|
_ => self.add_target(size, src).add_target(size, dest),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,20 +196,31 @@ impl M68kInstructionTiming {
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::ABCD(_, dest) => self.add_reg_v_mem(dest, 6, 18),
|
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(x), dest, size) if *x <= 8 => {
|
||||||
Instruction::ADD(Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest), // ADDI
|
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::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::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(Target::Immediate(_), dest, size) => {
|
||||||
Instruction::AND(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12)).add_two_targets(*size, src, dest),
|
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::ANDtoCCR(_) => self.add_internal(20),
|
||||||
Instruction::ANDtoSR(_) => self.add_internal(20),
|
Instruction::ANDtoSR(_) => self.add_internal(20),
|
||||||
|
|
||||||
Instruction::ASL(_, target, size) |
|
Instruction::ASL(_, target, size) | Instruction::ASR(_, target, size) => {
|
||||||
Instruction::ASR(_, target, size) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target),
|
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::Bcc(_, _) => self.add_internal(8).add_on_branch(2),
|
||||||
Instruction::BRA(_) => self.add_internal(10),
|
Instruction::BRA(_) => self.add_internal(10),
|
||||||
|
@ -221,25 +229,36 @@ impl M68kInstructionTiming {
|
||||||
Instruction::BCHG(bit, target, size) => match bit {
|
Instruction::BCHG(bit, target, size) => match bit {
|
||||||
Target::Immediate(_) => self.add_reg_v_mem(target, 12, 12),
|
Target::Immediate(_) => self.add_reg_v_mem(target, 12, 12),
|
||||||
_ => self.add_reg_v_mem(target, 8, 8),
|
_ => self.add_reg_v_mem(target, 8, 8),
|
||||||
}.add_target(*size, target),
|
}
|
||||||
|
.add_target(*size, target),
|
||||||
Instruction::BCLR(bit, target, size) => match bit {
|
Instruction::BCLR(bit, target, size) => match bit {
|
||||||
Target::Immediate(_) => self.add_reg_v_mem(target, 14, 12),
|
Target::Immediate(_) => self.add_reg_v_mem(target, 14, 12),
|
||||||
_ => self.add_reg_v_mem(target, 10, 8),
|
_ => self.add_reg_v_mem(target, 10, 8),
|
||||||
}.add_target(*size, target),
|
}
|
||||||
|
.add_target(*size, target),
|
||||||
Instruction::BSET(bit, target, size) => match bit {
|
Instruction::BSET(bit, target, size) => match bit {
|
||||||
Target::Immediate(_) => self.add_reg_v_mem(target, 12, 12),
|
Target::Immediate(_) => self.add_reg_v_mem(target, 12, 12),
|
||||||
_ => self.add_reg_v_mem(target, 8, 8),
|
_ => self.add_reg_v_mem(target, 8, 8),
|
||||||
}.add_target(*size, target),
|
}
|
||||||
|
.add_target(*size, target),
|
||||||
Instruction::BTST(bit, target, size) => match bit {
|
Instruction::BTST(bit, target, size) => match bit {
|
||||||
Target::Immediate(_) => self.add_reg_v_mem(target, 10, 8),
|
Target::Immediate(_) => self.add_reg_v_mem(target, 10, 8),
|
||||||
_ => self.add_reg_v_mem(target, 6, 4),
|
_ => self.add_reg_v_mem(target, 6, 4),
|
||||||
}.add_target(*size, target),
|
}
|
||||||
|
.add_target(*size, target),
|
||||||
|
|
||||||
Instruction::CHK(_, _, _) => self.add_internal(10),
|
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(Target::Immediate(_), dest, size) => {
|
||||||
Instruction::CMP(src, dest, size) => self.add_standard_set(*size, dest, (6, 6), (4, 6), (0, 0)).add_two_targets(*size, src, dest),
|
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::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),
|
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),
|
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(Target::Immediate(_), dest, size) => {
|
||||||
Instruction::EOR(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 8), (8, 12)).add_two_targets(*size, src, dest),
|
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::EORtoCCR(_) => self.add_internal(20),
|
||||||
Instruction::EORtoSR(_) => 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::LEA(target, _) => self.add_indirect_set(target, 4, 8, 12, 8, 12),
|
||||||
Instruction::LINK(_, _) => self.add_internal(16),
|
Instruction::LINK(_, _) => self.add_internal(16),
|
||||||
Instruction::LSL(_, target, size) |
|
Instruction::LSL(_, target, size) | Instruction::LSR(_, target, size) => {
|
||||||
Instruction::LSR(_, target, size) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target),
|
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::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),
|
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::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::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(Target::Immediate(_), dest, size) => {
|
||||||
Instruction::OR(src, dest, size) => self.add_standard_set(*size, dest, (0, 0), (4, 6), (8, 12)).add_two_targets(*size, src, dest),
|
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::ORtoCCR(_) => self.add_internal(20),
|
||||||
Instruction::ORtoSR(_) => self.add_internal(20),
|
Instruction::ORtoSR(_) => self.add_internal(20),
|
||||||
|
|
||||||
|
@ -296,26 +324,40 @@ impl M68kInstructionTiming {
|
||||||
|
|
||||||
Instruction::RESET => self.add_internal(132),
|
Instruction::RESET => self.add_internal(132),
|
||||||
|
|
||||||
Instruction::ROL(_, target, size) |
|
Instruction::ROL(_, target, size) | Instruction::ROR(_, target, size) => {
|
||||||
Instruction::ROR(_, target, size) => self.add_word_v_long(*size, 6, 8).add_per_rep(2).add_target(*size, target),
|
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::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::RTE => self.add_internal(20),
|
||||||
Instruction::RTR => self.add_internal(20),
|
Instruction::RTR => self.add_internal(20),
|
||||||
Instruction::RTS => self.add_internal(16),
|
Instruction::RTS => self.add_internal(16),
|
||||||
//Instruction::RTD(offset) => ,
|
//Instruction::RTD(offset) => ,
|
||||||
|
|
||||||
Instruction::SBCD(_, dest) => self.add_reg_v_mem(dest, 6, 18),
|
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::STOP(_) => self.add_internal(4),
|
||||||
|
|
||||||
Instruction::SUB(Target::Immediate(x), Target::DirectAReg(_), Size::Byte)
|
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), Target::DirectAReg(_), Size::Word)
|
||||||
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
|
if *x <= 8 =>
|
||||||
Instruction::SUB(Target::Immediate(_), dest, size) => self.add_immediate_set(*size, dest, (8, 16), (12, 20)).add_target(*size, dest), // SUBI
|
{
|
||||||
|
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::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)),
|
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 {
|
pub fn calculate_clocks(&self) -> ClockCycles {
|
||||||
//println!("{:?}", self);
|
//println!("{:?}", self);
|
||||||
(self.accesses as ClockCycles * 4)
|
(self.accesses as ClockCycles * 4)
|
||||||
+ self.internal as ClockCycles
|
+ self.internal as ClockCycles
|
||||||
+ (if self.branched { self.on_branch as ClockCycles } else { 0 })
|
+ (if self.branched { self.on_branch as ClockCycles } else { 0 })
|
||||||
+ self.per_rep as ClockCycles * self.reps
|
+ self.per_rep as ClockCycles * self.reps
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -376,6 +418,3 @@ impl M68kInstructionTiming {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Frequency};
|
use femtos::{Instant, Frequency};
|
||||||
use emulator_hal::bus::BusAccess;
|
use emulator_hal::bus::BusAccess;
|
||||||
use emulator_hal_memory::MemoryBlock;
|
use emulator_hal_memory::MemoryBlock;
|
||||||
|
@ -69,7 +68,9 @@ fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Ins
|
||||||
// Insert basic initialization
|
// Insert basic initialization
|
||||||
let len = 0x2000;
|
let len = 0x2000;
|
||||||
let mut data = Vec::with_capacity(len);
|
let mut data = Vec::with_capacity(len);
|
||||||
unsafe { data.set_len(len); }
|
unsafe {
|
||||||
|
data.set_len(len);
|
||||||
|
}
|
||||||
let mut memory = MemoryBlock::from(data);
|
let mut memory = MemoryBlock::from(data);
|
||||||
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
|
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
|
||||||
memory.write_beu32(Instant::START, 4, INIT_ADDR).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 femtos::{Instant, Frequency};
|
||||||
use emulator_hal::bus::BusAccess;
|
use emulator_hal::bus::BusAccess;
|
||||||
use emulator_hal::step::Step;
|
use emulator_hal::step::Step;
|
||||||
|
@ -44,7 +43,9 @@ where
|
||||||
// Insert basic initialization
|
// Insert basic initialization
|
||||||
let len = 0x10_0000;
|
let len = 0x10_0000;
|
||||||
let mut data = Vec::with_capacity(len);
|
let mut data = Vec::with_capacity(len);
|
||||||
unsafe { data.set_len(len); }
|
unsafe {
|
||||||
|
data.set_len(len);
|
||||||
|
}
|
||||||
let mut memory = MemoryBlock::from(data);
|
let mut memory = MemoryBlock::from(data);
|
||||||
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
|
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
|
||||||
memory.write_beu32(Instant::START, 4, INIT_ADDR).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]) {
|
fn load_memory<Bus: BusAccess<u32, Instant>>(bus: &mut Bus, data: &[u16]) {
|
||||||
for i in 0..data.len() {
|
for i in 0..data.len() {
|
||||||
bus.write_beu16(Instant::START, (i << 1) as u32, data[i]).unwrap();
|
bus.write_beu16(Instant::START, (i << 1) as u32, data[i]).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_test(case: &TestCase) {
|
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 },
|
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 femtos::{Instant, Frequency};
|
||||||
use emulator_hal::bus::BusAccess;
|
use emulator_hal::bus::BusAccess;
|
||||||
use emulator_hal_memory::MemoryBlock;
|
use emulator_hal_memory::MemoryBlock;
|
||||||
|
@ -17,7 +16,9 @@ fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Ins
|
||||||
// Insert basic initialization
|
// Insert basic initialization
|
||||||
let len = 0x10_0000;
|
let len = 0x10_0000;
|
||||||
let mut data = Vec::with_capacity(len);
|
let mut data = Vec::with_capacity(len);
|
||||||
unsafe { data.set_len(len); }
|
unsafe {
|
||||||
|
data.set_len(len);
|
||||||
|
}
|
||||||
let mut memory = MemoryBlock::from(data);
|
let mut memory = MemoryBlock::from(data);
|
||||||
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
|
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
|
||||||
memory.write_beu32(Instant::START, 4, INIT_ADDR).unwrap();
|
memory.write_beu32(Instant::START, 4, INIT_ADDR).unwrap();
|
||||||
|
@ -75,7 +76,10 @@ pub fn run_timing_tests() {
|
||||||
print!("Testing for {:?}...", case.ins);
|
print!("Testing for {:?}...", case.ins);
|
||||||
match run_timing_test(case) {
|
match run_timing_test(case) {
|
||||||
Ok(()) => println!("ok"),
|
Ok(()) => println!("ok"),
|
||||||
Err(err) => { println!("{:?}", err); errors += 1 },
|
Err(err) => {
|
||||||
|
println!("{:?}", err);
|
||||||
|
errors += 1
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(_) = run_timing_test(case) {
|
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: &[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) },
|
TimingCase { cpu: M68kType::MC68000, data: &[0x4E58], timing: ( 12, 12, 6), ins: Instruction::UNLK(0) },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Frequency};
|
use femtos::{Instant, Frequency};
|
||||||
use emulator_hal::bus::BusAccess;
|
use emulator_hal::bus::BusAccess;
|
||||||
use emulator_hal_memory::MemoryBlock;
|
use emulator_hal_memory::MemoryBlock;
|
||||||
|
@ -19,16 +18,21 @@ struct TimingCase {
|
||||||
ins: Instruction,
|
ins: Instruction,
|
||||||
}
|
}
|
||||||
|
|
||||||
const TIMING_TESTS: &'static [TimingCase] = &[
|
const TIMING_TESTS: &'static [TimingCase] = &[TimingCase {
|
||||||
TimingCase { cpu: M68kType::MC68000, data: &[0xD090], timing: ( 14, 14, 6), ins: Instruction::ADD(Target::IndirectAReg(0), Target::DirectDReg(0), Size::Long) },
|
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>) {
|
fn init_decode_test(cputype: M68kType) -> (M68k, M68kCycle, MemoryBlock<u32, Instant>) {
|
||||||
// Insert basic initialization
|
// Insert basic initialization
|
||||||
let len = 0x10_0000;
|
let len = 0x10_0000;
|
||||||
let mut data = Vec::with_capacity(len);
|
let mut data = Vec::with_capacity(len);
|
||||||
unsafe { data.set_len(len); }
|
unsafe {
|
||||||
|
data.set_len(len);
|
||||||
|
}
|
||||||
let mut memory = MemoryBlock::from(data);
|
let mut memory = MemoryBlock::from(data);
|
||||||
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
|
memory.write_beu32(Instant::START, 0, INIT_STACK).unwrap();
|
||||||
memory.write_beu32(Instant::START, 4, INIT_ADDR).unwrap();
|
memory.write_beu32(Instant::START, 4, INIT_ADDR).unwrap();
|
||||||
|
@ -85,7 +89,10 @@ pub fn run_timing_tests() {
|
||||||
print!("Testing for {:?}...", case.ins);
|
print!("Testing for {:?}...", case.ins);
|
||||||
match run_timing_test(case) {
|
match run_timing_test(case) {
|
||||||
Ok(()) => println!("ok"),
|
Ok(()) => println!("ok"),
|
||||||
Err(err) => { println!("{:?}", err); errors += 1 },
|
Err(err) => {
|
||||||
|
println!("{:?}", err);
|
||||||
|
errors += 1
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(_) = run_timing_test(case) {
|
if let Err(_) = run_timing_test(case) {
|
||||||
|
@ -97,4 +104,3 @@ pub fn run_timing_tests() {
|
||||||
panic!("{} errors", errors);
|
panic!("{} errors", errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Debuggable};
|
use moa_core::{System, Error, Address, Debuggable};
|
||||||
|
|
||||||
use crate::state::{Z80, Z80Error};
|
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> {
|
fn run_command(&mut self, _system: &System, args: &[&str]) -> Result<bool, Error> {
|
||||||
match args[0] {
|
match args[0] {
|
||||||
"l" => {
|
"l" => self.state.reg[Register::L as usize] = 0x05,
|
||||||
self.state.reg[Register::L as usize] = 0x05
|
_ => {
|
||||||
|
return Ok(true);
|
||||||
},
|
},
|
||||||
_ => { return Ok(true); },
|
|
||||||
}
|
}
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
@ -62,4 +61,3 @@ impl Z80 {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use femtos::Instant;
|
use femtos::Instant;
|
||||||
|
|
||||||
use moa_core::{Address, Addressable};
|
use moa_core::{Address, Addressable};
|
||||||
|
|
||||||
use crate::state::Z80Error;
|
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;
|
//use emulator_hal::bus::BusAccess;
|
||||||
//
|
//
|
||||||
|
@ -57,177 +59,169 @@ impl Z80Decoder {
|
||||||
self.decode_bare(memory, ins, 0)
|
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;
|
self.extra_instruction_bytes = extra_instruction_bytes;
|
||||||
match get_ins_x(ins) {
|
match get_ins_x(ins) {
|
||||||
0 => {
|
0 => match get_ins_z(ins) {
|
||||||
match get_ins_z(ins) {
|
0 => match get_ins_y(ins) {
|
||||||
0 => {
|
0 => Ok(Instruction::NOP),
|
||||||
match get_ins_y(ins) {
|
1 => Ok(Instruction::EXafaf),
|
||||||
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))))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
2 => {
|
2 => {
|
||||||
if (ins & 0x20) == 0 {
|
let offset = self.read_instruction_byte(memory)? as i8;
|
||||||
let target = match (ins & 0x10) != 0 {
|
Ok(Instruction::DJNZ(offset))
|
||||||
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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
3 => {
|
3 => {
|
||||||
if get_ins_q(ins) == 0 {
|
let offset = self.read_instruction_byte(memory)? as i8;
|
||||||
Ok(Instruction::INC16(get_register_pair(get_ins_p(ins))))
|
Ok(Instruction::JR(offset))
|
||||||
} else {
|
},
|
||||||
Ok(Instruction::DEC16(get_register_pair(get_ins_p(ins))))
|
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)),
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
4 => {
|
let addr = self.read_instruction_word(memory)?;
|
||||||
Ok(Instruction::INC8(get_register(get_ins_y(ins))))
|
match (ins >> 3) & 0x03 {
|
||||||
},
|
0 => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(RegisterPair::HL))),
|
||||||
5 => {
|
1 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(addr))),
|
||||||
Ok(Instruction::DEC8(get_register(get_ins_y(ins))))
|
2 => Ok(Instruction::LD(LoadTarget::IndirectByte(addr), LoadTarget::DirectRegByte(Register::A))),
|
||||||
},
|
3 => Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), LoadTarget::IndirectByte(addr))),
|
||||||
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"),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
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"),
|
||||||
}
|
},
|
||||||
|
_ => panic!("InternalError: impossible value"),
|
||||||
},
|
},
|
||||||
1 => {
|
1 => {
|
||||||
if ins == 0x76 {
|
if ins == 0x76 {
|
||||||
Ok(Instruction::HALT)
|
Ok(Instruction::HALT)
|
||||||
} else {
|
} 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 => {
|
2 => Ok(get_alu_instruction(get_ins_y(ins), get_register(get_ins_z(ins)))),
|
||||||
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)))),
|
||||||
3 => {
|
1 => {
|
||||||
match get_ins_z(ins) {
|
if get_ins_q(ins) == 0 {
|
||||||
0 => {
|
Ok(Instruction::POP(get_register_pair_alt(get_ins_p(ins))))
|
||||||
Ok(Instruction::RETcc(get_condition(get_ins_y(ins))))
|
} else {
|
||||||
},
|
match get_ins_p(ins) {
|
||||||
1 => {
|
0 => Ok(Instruction::RET),
|
||||||
if get_ins_q(ins) == 0 {
|
1 => Ok(Instruction::EXX),
|
||||||
Ok(Instruction::POP(get_register_pair_alt(get_ins_p(ins))))
|
2 => Ok(Instruction::JPIndirect(RegisterPair::HL)),
|
||||||
} else {
|
3 => Ok(Instruction::LD(
|
||||||
match get_ins_p(ins) {
|
LoadTarget::DirectRegWord(RegisterPair::SP),
|
||||||
0 => Ok(Instruction::RET),
|
LoadTarget::DirectRegWord(RegisterPair::HL),
|
||||||
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),
|
|
||||||
_ => panic!("InternalError: impossible value"),
|
_ => 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 {
|
2 => {
|
||||||
Ok(Instruction::PUSH(get_register_pair_alt(get_ins_p(ins))))
|
let addr = self.read_instruction_word(memory)?;
|
||||||
} else {
|
Ok(Instruction::JPcc(get_condition(get_ins_y(ins)), addr))
|
||||||
match get_ins_p(ins) {
|
},
|
||||||
0 => {
|
3 => match get_ins_y(ins) {
|
||||||
let addr = self.read_instruction_word(memory)?;
|
0 => {
|
||||||
Ok(Instruction::CALL(addr))
|
let addr = self.read_instruction_word(memory)?;
|
||||||
},
|
Ok(Instruction::JP(addr))
|
||||||
1 => self.decode_prefix_dd_fd(memory, IndexRegister::IX),
|
},
|
||||||
2 => self.decode_prefix_ed(memory),
|
1 => self.decode_prefix_cb(memory),
|
||||||
3 => self.decode_prefix_dd_fd(memory, IndexRegister::IY),
|
2 => {
|
||||||
_ => panic!("InternalError: impossible value"),
|
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)?;
|
6 => {
|
||||||
Ok(get_alu_instruction(get_ins_y(ins), Target::Immediate(data)))
|
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))
|
7 => Ok(Instruction::RST(get_ins_y(ins) * 8)),
|
||||||
},
|
_ => panic!("InternalError: impossible value"),
|
||||||
_ => panic!("InternalError: impossible value"),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
_ => panic!("InternalError: impossible value"),
|
_ => panic!("InternalError: impossible value"),
|
||||||
}
|
}
|
||||||
|
@ -266,92 +260,88 @@ impl Z80Decoder {
|
||||||
|
|
||||||
match get_ins_x(ins) {
|
match get_ins_x(ins) {
|
||||||
0 => Ok(Instruction::NOP),
|
0 => Ok(Instruction::NOP),
|
||||||
1 => {
|
1 => match get_ins_z(ins) {
|
||||||
match get_ins_z(ins) {
|
0 => {
|
||||||
0 => {
|
let target = get_register(get_ins_y(ins));
|
||||||
let target = get_register(get_ins_y(ins));
|
if let Target::DirectReg(reg) = target {
|
||||||
if let Target::DirectReg(reg) = target {
|
Ok(Instruction::INic(reg))
|
||||||
Ok(Instruction::INic(reg))
|
} else {
|
||||||
} else {
|
Ok(Instruction::INicz)
|
||||||
Ok(Instruction::INicz)
|
}
|
||||||
}
|
},
|
||||||
},
|
1 => {
|
||||||
1 => {
|
let target = get_register(get_ins_y(ins));
|
||||||
let target = get_register(get_ins_y(ins));
|
if let Target::DirectReg(reg) = target {
|
||||||
if let Target::DirectReg(reg) = target {
|
Ok(Instruction::OUTic(reg))
|
||||||
Ok(Instruction::OUTic(reg))
|
} else {
|
||||||
} else {
|
Ok(Instruction::OUTicz)
|
||||||
Ok(Instruction::OUTicz)
|
}
|
||||||
}
|
},
|
||||||
},
|
2 => {
|
||||||
2 => {
|
if get_ins_q(ins) == 0 {
|
||||||
if get_ins_q(ins) == 0 {
|
Ok(Instruction::SBC16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
|
||||||
Ok(Instruction::SBC16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
|
} else {
|
||||||
} else {
|
Ok(Instruction::ADC16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
|
||||||
Ok(Instruction::ADC16(RegisterPair::HL, get_register_pair(get_ins_p(ins))))
|
}
|
||||||
}
|
},
|
||||||
},
|
3 => {
|
||||||
3 => {
|
let addr = self.read_instruction_word(memory)?;
|
||||||
let addr = self.read_instruction_word(memory)?;
|
if get_ins_q(ins) == 0 {
|
||||||
if get_ins_q(ins) == 0 {
|
Ok(Instruction::LD(
|
||||||
Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins)))))
|
LoadTarget::IndirectWord(addr),
|
||||||
} else {
|
LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))),
|
||||||
Ok(Instruction::LD(LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))), LoadTarget::IndirectWord(addr)))
|
))
|
||||||
}
|
} else {
|
||||||
},
|
Ok(Instruction::LD(
|
||||||
4 => {
|
LoadTarget::DirectRegWord(get_register_pair(get_ins_p(ins))),
|
||||||
Ok(Instruction::NEG)
|
LoadTarget::IndirectWord(addr),
|
||||||
},
|
))
|
||||||
5 => {
|
}
|
||||||
if get_ins_y(ins) == 1 {
|
},
|
||||||
Ok(Instruction::RETI)
|
4 => Ok(Instruction::NEG),
|
||||||
} else {
|
5 => {
|
||||||
Ok(Instruction::RETN)
|
if get_ins_y(ins) == 1 {
|
||||||
}
|
Ok(Instruction::RETI)
|
||||||
},
|
} else {
|
||||||
6 => {
|
Ok(Instruction::RETN)
|
||||||
match get_ins_y(ins) & 0x03 {
|
}
|
||||||
0 => Ok(Instruction::IM(InterruptMode::Mode0)),
|
},
|
||||||
1 => Ok(Instruction::IM(InterruptMode::Mode0)),
|
6 => match get_ins_y(ins) & 0x03 {
|
||||||
2 => Ok(Instruction::IM(InterruptMode::Mode1)),
|
0 => Ok(Instruction::IM(InterruptMode::Mode0)),
|
||||||
3 => Ok(Instruction::IM(InterruptMode::Mode2)),
|
1 => Ok(Instruction::IM(InterruptMode::Mode0)),
|
||||||
_ => panic!("InternalError: impossible value"),
|
2 => Ok(Instruction::IM(InterruptMode::Mode1)),
|
||||||
}
|
3 => Ok(Instruction::IM(InterruptMode::Mode2)),
|
||||||
},
|
|
||||||
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"),
|
_ => panic!("InternalError: impossible value"),
|
||||||
}
|
},
|
||||||
},
|
7 => match get_ins_y(ins) {
|
||||||
2 => {
|
0 => Ok(Instruction::LDsr(SpecialRegister::I, Direction::FromAcc)),
|
||||||
match ins {
|
1 => Ok(Instruction::LDsr(SpecialRegister::R, Direction::FromAcc)),
|
||||||
0xA0 => Ok(Instruction::LDI),
|
2 => Ok(Instruction::LDsr(SpecialRegister::I, Direction::ToAcc)),
|
||||||
0xA1 => Ok(Instruction::CPI),
|
3 => Ok(Instruction::LDsr(SpecialRegister::R, Direction::ToAcc)),
|
||||||
0xA2 => Ok(Instruction::INI),
|
4 => Ok(Instruction::RRD),
|
||||||
0xA3 => Ok(Instruction::OUTI),
|
5 => Ok(Instruction::RLD),
|
||||||
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),
|
_ => 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),
|
3 => Ok(Instruction::NOP),
|
||||||
_ => panic!("InternalError: impossible value"),
|
_ => panic!("InternalError: impossible value"),
|
||||||
|
@ -372,125 +362,120 @@ impl Z80Decoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
match get_ins_p(ins) {
|
match get_ins_p(ins) {
|
||||||
2 => {
|
2 => match get_ins_z(ins) {
|
||||||
match get_ins_z(ins) {
|
1 => {
|
||||||
1 => {
|
let data = self.read_instruction_word(memory)?;
|
||||||
let data = self.read_instruction_word(memory)?;
|
Ok(Instruction::LD(LoadTarget::DirectRegWord(index_reg.into()), LoadTarget::ImmediateWord(data)))
|
||||||
Ok(Instruction::LD(LoadTarget::DirectRegWord(index_reg.into()), LoadTarget::ImmediateWord(data)))
|
},
|
||||||
},
|
2 => {
|
||||||
2 => {
|
let addr = self.read_instruction_word(memory)?;
|
||||||
let addr = self.read_instruction_word(memory)?;
|
let regpair = index_reg.into();
|
||||||
let regpair = index_reg.into();
|
match get_ins_q(ins) != 0 {
|
||||||
match get_ins_q(ins) != 0 {
|
false => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(regpair))),
|
||||||
false => Ok(Instruction::LD(LoadTarget::IndirectWord(addr), LoadTarget::DirectRegWord(regpair))),
|
true => Ok(Instruction::LD(LoadTarget::DirectRegWord(regpair), LoadTarget::IndirectWord(addr))),
|
||||||
true => Ok(Instruction::LD(LoadTarget::DirectRegWord(regpair), LoadTarget::IndirectWord(addr))),
|
}
|
||||||
}
|
},
|
||||||
},
|
3 => match get_ins_q(ins) != 0 {
|
||||||
3 => {
|
false => Ok(Instruction::INC16(index_reg.into())),
|
||||||
match get_ins_q(ins) != 0 {
|
true => Ok(Instruction::DEC16(index_reg.into())),
|
||||||
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)));
|
||||||
4 => {
|
Ok(Instruction::INC8(half_target))
|
||||||
self.extra_instruction_bytes = 4;
|
},
|
||||||
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
|
5 => {
|
||||||
Ok(Instruction::INC8(half_target))
|
self.extra_instruction_bytes = 4;
|
||||||
},
|
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
|
||||||
5 => {
|
Ok(Instruction::DEC8(half_target))
|
||||||
self.extra_instruction_bytes = 4;
|
},
|
||||||
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
|
6 => {
|
||||||
Ok(Instruction::DEC8(half_target))
|
self.extra_instruction_bytes = 4;
|
||||||
},
|
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
|
||||||
6 => {
|
let data = self.read_instruction_byte(memory)?;
|
||||||
self.extra_instruction_bytes = 4;
|
Ok(Instruction::LD(to_load_target(half_target), LoadTarget::ImmediateByte(data)))
|
||||||
let half_target = Target::DirectRegHalf(get_index_register_half(index_reg, get_ins_q(ins)));
|
},
|
||||||
let data = self.read_instruction_byte(memory)?;
|
_ => self.decode_bare(memory, ins, 4),
|
||||||
Ok(Instruction::LD(to_load_target(half_target), LoadTarget::ImmediateByte(data)))
|
|
||||||
},
|
|
||||||
_ => self.decode_bare(memory, ins, 4),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
3 => {
|
3 => match ins {
|
||||||
match ins {
|
0x34 => {
|
||||||
0x34 => {
|
let offset = self.read_instruction_byte(memory)? as i8;
|
||||||
let offset = self.read_instruction_byte(memory)? as i8;
|
Ok(Instruction::INC8(Target::IndirectOffset(index_reg, offset)))
|
||||||
Ok(Instruction::INC8(Target::IndirectOffset(index_reg, offset)))
|
},
|
||||||
},
|
0x35 => {
|
||||||
0x35 => {
|
let offset = self.read_instruction_byte(memory)? as i8;
|
||||||
let offset = self.read_instruction_byte(memory)? as i8;
|
Ok(Instruction::DEC8(Target::IndirectOffset(index_reg, offset)))
|
||||||
Ok(Instruction::DEC8(Target::IndirectOffset(index_reg, offset)))
|
},
|
||||||
},
|
0x36 => {
|
||||||
0x36 => {
|
let offset = self.read_instruction_byte(memory)? as i8;
|
||||||
let offset = self.read_instruction_byte(memory)? as i8;
|
let immediate = self.read_instruction_byte(memory)?;
|
||||||
let immediate = self.read_instruction_byte(memory)?;
|
Ok(Instruction::LD(
|
||||||
Ok(Instruction::LD(LoadTarget::IndirectOffsetByte(index_reg, offset), LoadTarget::ImmediateByte(immediate)))
|
LoadTarget::IndirectOffsetByte(index_reg, offset),
|
||||||
},
|
LoadTarget::ImmediateByte(immediate),
|
||||||
_ => self.decode_bare(memory, ins, 4),
|
))
|
||||||
}
|
},
|
||||||
|
_ => self.decode_bare(memory, ins, 4),
|
||||||
},
|
},
|
||||||
_ => self.decode_bare(memory, ins, 4),
|
_ => self.decode_bare(memory, ins, 4),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
1 => {
|
1 => match get_ins_p(ins) {
|
||||||
match get_ins_p(ins) {
|
0 | 1 => {
|
||||||
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))? {
|
let target = match self.decode_index_target(memory, index_reg, get_ins_z(ins))? {
|
||||||
Some(target) => target,
|
Some(target) => target,
|
||||||
None => return self.decode_bare(memory, ins, 4),
|
None => return self.decode_bare(memory, ins, 4),
|
||||||
};
|
};
|
||||||
|
|
||||||
match (ins & 0x18) >> 3 {
|
Ok(Instruction::LD(LoadTarget::DirectRegByte(Register::A), to_load_target(target)))
|
||||||
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))),
|
_ => panic!("InternalError: impossible value"),
|
||||||
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"),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
2 => {
|
2 => {
|
||||||
self.extra_instruction_bytes = 4;
|
self.extra_instruction_bytes = 4;
|
||||||
|
@ -512,21 +497,27 @@ impl Z80Decoder {
|
||||||
_ => panic!("InternalError: impossible value"),
|
_ => panic!("InternalError: impossible value"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
3 => {
|
3 => match ins {
|
||||||
match ins {
|
0xE1 => Ok(Instruction::POP(index_reg.into())),
|
||||||
0xE1 => Ok(Instruction::POP(index_reg.into())),
|
0xE3 => Ok(Instruction::EXsp(index_reg.into())),
|
||||||
0xE3 => Ok(Instruction::EXsp(index_reg.into())),
|
0xE5 => Ok(Instruction::PUSH(index_reg.into())),
|
||||||
0xE5 => Ok(Instruction::PUSH(index_reg.into())),
|
0xE9 => Ok(Instruction::JPIndirect(index_reg.into())),
|
||||||
0xE9 => Ok(Instruction::JPIndirect(index_reg.into())),
|
0xF9 => Ok(Instruction::LD(
|
||||||
0xF9 => Ok(Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::SP), LoadTarget::DirectRegWord(index_reg.into()))),
|
LoadTarget::DirectRegWord(RegisterPair::SP),
|
||||||
_ => self.decode_bare(memory, ins, 4),
|
LoadTarget::DirectRegWord(index_reg.into()),
|
||||||
}
|
)),
|
||||||
|
_ => self.decode_bare(memory, ins, 4),
|
||||||
},
|
},
|
||||||
_ => panic!("InternalError: impossible value"),
|
_ => 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 {
|
let result = match z {
|
||||||
4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))),
|
4 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 0))),
|
||||||
5 => Some(Target::DirectRegHalf(get_index_register_half(index_reg, 1))),
|
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 {
|
fn get_index_register_half(reg: IndexRegister, q: u8) -> IndexRegisterHalf {
|
||||||
match reg {
|
match reg {
|
||||||
IndexRegister::IX => if q == 0 { IndexRegisterHalf::IXH } else { IndexRegisterHalf::IXL },
|
IndexRegister::IX => {
|
||||||
IndexRegister::IY => if q == 0 { IndexRegisterHalf::IYH } else { IndexRegisterHalf::IYL },
|
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 {
|
fn get_ins_q(ins: u8) -> u8 {
|
||||||
(ins >> 3) & 0x01
|
(ins >> 3) & 0x01
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration};
|
use femtos::{Instant, Duration};
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Steppable, Addressable, Interruptable, Debuggable, Transmutable, read_beu16, write_beu16};
|
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::state::{Z80, Z80Error, Status, Flags};
|
||||||
use crate::timing::Z80InstructionCycles;
|
use crate::timing::Z80InstructionCycles;
|
||||||
|
|
||||||
|
|
||||||
const FLAGS_NUMERIC: u8 = 0xC0;
|
const FLAGS_NUMERIC: u8 = 0xC0;
|
||||||
const FLAGS_ARITHMETIC: u8 = 0x17;
|
const FLAGS_ARITHMETIC: u8 = 0x17;
|
||||||
const FLAGS_CARRY_HALF_CARRY: u8 = 0x11;
|
const FLAGS_CARRY_HALF_CARRY: u8 = 0x11;
|
||||||
|
|
||||||
|
|
||||||
enum RotateType {
|
enum RotateType {
|
||||||
|
@ -36,7 +38,7 @@ impl Steppable for Z80 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interruptable for Z80 { }
|
impl Interruptable for Z80 {}
|
||||||
|
|
||||||
|
|
||||||
impl Transmutable 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()),
|
Error::Other(msg) | Error::Assertion(msg) | Error::Emulator(_, msg) => Z80Error::BusError(msg.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Z80Executor {
|
pub struct Z80Executor {
|
||||||
|
@ -95,11 +96,9 @@ impl Z80 {
|
||||||
match self.state.status {
|
match self.state.status {
|
||||||
Status::Init => self.init(),
|
Status::Init => self.init(),
|
||||||
Status::Halted => Err(Z80Error::Halted),
|
Status::Halted => Err(Z80Error::Halted),
|
||||||
Status::Running => {
|
Status::Running => match self.cycle_one() {
|
||||||
match self.cycle_one() {
|
Ok(clocks) => Ok(clocks),
|
||||||
Ok(clocks) => Ok(clocks),
|
Err(err) => Err(err),
|
||||||
Err(err) => Err(err),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,12 +119,15 @@ impl Z80 {
|
||||||
|
|
||||||
self.decode_next()?;
|
self.decode_next()?;
|
||||||
self.execute_current()?;
|
self.execute_current()?;
|
||||||
Ok(Z80InstructionCycles::from_instruction(&self.decoder.instruction, self.decoder.extra_instruction_bytes)?
|
Ok(
|
||||||
.calculate_cycles(self.executor.took_branch))
|
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> {
|
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.increment_refresh(self.decoder.end.saturating_sub(self.decoder.start) as u8);
|
||||||
self.state.pc = self.decoder.end;
|
self.state.pc = self.decoder.end;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -186,9 +188,7 @@ impl Z80 {
|
||||||
Instruction::LDsr(special_reg, dir) => self.execute_ldsr(special_reg, dir),
|
Instruction::LDsr(special_reg, dir) => self.execute_ldsr(special_reg, dir),
|
||||||
Instruction::LDD | Instruction::LDDR | Instruction::LDI | Instruction::LDIR => self.execute_ldx(),
|
Instruction::LDD | Instruction::LDDR | Instruction::LDI | Instruction::LDIR => self.execute_ldx(),
|
||||||
Instruction::NEG => self.execute_neg(),
|
Instruction::NEG => self.execute_neg(),
|
||||||
Instruction::NOP => {
|
Instruction::NOP => Ok(()),
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
Instruction::OR(target) => self.execute_or(target),
|
Instruction::OR(target) => self.execute_or(target),
|
||||||
//Instruction::OTDR => {
|
//Instruction::OTDR => {
|
||||||
//},
|
//},
|
||||||
|
@ -230,9 +230,7 @@ impl Z80 {
|
||||||
Instruction::SRL(target, opt_copy) => self.execute_srl(target, opt_copy),
|
Instruction::SRL(target, opt_copy) => self.execute_srl(target, opt_copy),
|
||||||
Instruction::SUB(target) => self.execute_sub(target),
|
Instruction::SUB(target) => self.execute_sub(target),
|
||||||
Instruction::XOR(target) => self.execute_xor(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 (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);
|
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);
|
self.set_register_value(Register::A, result2);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -410,7 +415,7 @@ impl Z80 {
|
||||||
let value = self.get_target_value(target)?;
|
let value = self.get_target_value(target)?;
|
||||||
|
|
||||||
let (result, _, overflow, half_carry) = sub_bytes(value, 1);
|
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_arithmetic_op_flags(result as u16, Size::Byte, true, carry, overflow, half_carry);
|
||||||
|
|
||||||
self.set_target_value(target, result)?;
|
self.set_target_value(target, result)?;
|
||||||
|
@ -498,7 +503,7 @@ impl Z80 {
|
||||||
fn execute_inc8(&mut self, target: Target) -> Result<(), Z80Error> {
|
fn execute_inc8(&mut self, target: Target) -> Result<(), Z80Error> {
|
||||||
let value = self.get_target_value(target)?;
|
let value = self.get_target_value(target)?;
|
||||||
let (result, _, overflow, half_carry) = add_bytes(value, 1);
|
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_arithmetic_op_flags(result as u16, Size::Byte, false, carry, overflow, half_carry);
|
||||||
|
|
||||||
self.set_target_value(target, result)?;
|
self.set_target_value(target, result)?;
|
||||||
|
@ -626,9 +631,7 @@ impl Z80 {
|
||||||
let parity = if count != 0 { Flags::Parity as u8 } else { 0 };
|
let parity = if count != 0 { Flags::Parity as u8 } else { 0 };
|
||||||
self.set_flags(mask, parity);
|
self.set_flags(mask, parity);
|
||||||
|
|
||||||
if (self.decoder.instruction == Instruction::LDIR || self.decoder.instruction == Instruction::LDDR)
|
if (self.decoder.instruction == Instruction::LDIR || self.decoder.instruction == Instruction::LDDR) && count != 0 {
|
||||||
&& count != 0
|
|
||||||
{
|
|
||||||
self.executor.took_branch = true;
|
self.executor.took_branch = true;
|
||||||
self.state.pc -= 2;
|
self.state.pc -= 2;
|
||||||
}
|
}
|
||||||
|
@ -860,7 +863,14 @@ impl Z80 {
|
||||||
|
|
||||||
let (result1, carry1, overflow1, half_carry1) = sub_bytes(acc, src);
|
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);
|
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);
|
self.set_register_value(Register::A, result2);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1041,12 +1051,8 @@ impl Z80 {
|
||||||
let addr = self.get_register_pair_value(regpair);
|
let addr = self.get_register_pair_value(regpair);
|
||||||
self.read_port_u16(addr)?
|
self.read_port_u16(addr)?
|
||||||
},
|
},
|
||||||
LoadTarget::IndirectByte(addr) => {
|
LoadTarget::IndirectByte(addr) => self.read_port_u8(addr)? as u16,
|
||||||
self.read_port_u8(addr)? as u16
|
LoadTarget::IndirectWord(addr) => self.read_port_u16(addr)?,
|
||||||
},
|
|
||||||
LoadTarget::IndirectWord(addr) => {
|
|
||||||
self.read_port_u16(addr)?
|
|
||||||
},
|
|
||||||
LoadTarget::ImmediateByte(data) => data as u16,
|
LoadTarget::ImmediateByte(data) => data as u16,
|
||||||
LoadTarget::ImmediateWord(data) => data,
|
LoadTarget::ImmediateWord(data) => data,
|
||||||
_ => panic!("Unsupported LoadTarget for set"),
|
_ => panic!("Unsupported LoadTarget for set"),
|
||||||
|
@ -1176,10 +1182,18 @@ impl Z80 {
|
||||||
|
|
||||||
fn set_index_register_half_value(&mut self, reg: IndexRegisterHalf, value: u8) {
|
fn set_index_register_half_value(&mut self, reg: IndexRegisterHalf, value: u8) {
|
||||||
match reg {
|
match reg {
|
||||||
IndexRegisterHalf::IXH => { self.state.ix = (self.state.ix & 0x00FF) | (value as u16) << 8; },
|
IndexRegisterHalf::IXH => {
|
||||||
IndexRegisterHalf::IXL => { self.state.ix = (self.state.ix & 0xFF00) | value as u16; },
|
self.state.ix = (self.state.ix & 0x00FF) | (value as u16) << 8;
|
||||||
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::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) {
|
fn set_register_pair_value(&mut self, regpair: RegisterPair, value: u16) {
|
||||||
match regpair {
|
match regpair {
|
||||||
RegisterPair::BC => { write_beu16(&mut self.state.reg[0..2], value); },
|
RegisterPair::BC => {
|
||||||
RegisterPair::DE => { write_beu16(&mut self.state.reg[2..4], value); },
|
write_beu16(&mut self.state.reg[0..2], value);
|
||||||
RegisterPair::HL => { write_beu16(&mut self.state.reg[4..6], value); },
|
},
|
||||||
RegisterPair::AF => { write_beu16(&mut self.state.reg[6..8], value); },
|
RegisterPair::DE => {
|
||||||
RegisterPair::SP => { self.state.sp = value; },
|
write_beu16(&mut self.state.reg[2..4], value);
|
||||||
RegisterPair::IX => { self.state.ix = value; },
|
},
|
||||||
RegisterPair::IY => { self.state.iy = 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) {
|
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);
|
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,
|
Size::Word => (value & 0x8000) != 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
ToAcc,
|
ToAcc,
|
||||||
|
@ -206,5 +205,3 @@ impl RegisterPair {
|
||||||
matches!(self, RegisterPair::IX | RegisterPair::IY)
|
matches!(self, RegisterPair::IX | RegisterPair::IY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
pub mod debugger;
|
pub mod debugger;
|
||||||
pub mod decode;
|
pub mod decode;
|
||||||
pub mod execute;
|
pub mod execute;
|
||||||
|
@ -7,4 +6,3 @@ pub mod state;
|
||||||
pub mod timing;
|
pub mod timing;
|
||||||
|
|
||||||
pub use self::state::{Z80, Z80Type, Z80Error};
|
pub use self::state::{Z80, Z80Type, Z80Error};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use femtos::{Instant, Frequency};
|
use femtos::{Instant, Frequency};
|
||||||
|
@ -28,6 +27,7 @@ pub enum Status {
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[rustfmt::skip]
|
||||||
pub enum Flags {
|
pub enum Flags {
|
||||||
Carry = 0x01,
|
Carry = 0x01,
|
||||||
AddSubtract = 0x02,
|
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 {
|
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!("IX: {:#06x}", self.state.ix);
|
||||||
println!("IY: {:#06x}", self.state.iy);
|
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!(
|
||||||
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]);
|
"A: {:#04x} F: {:#04x} A': {:#04x} F': {:#04x}",
|
||||||
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]);
|
self.state.reg[Register::A 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]);
|
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!("I: {:#04x} R: {:#04x}", self.state.i, self.state.r);
|
||||||
println!("IM: {:?} IFF1: {:?} IFF2: {:?}", self.state.im, self.state.iff1, self.state.iff2);
|
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!();
|
println!();
|
||||||
self.port.dump_memory(clock, self.state.sp as Address, 0x40);
|
self.port.dump_memory(clock, self.state.sp as Address, 0x40);
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
|
|
||||||
use moa_core::Error;
|
use moa_core::Error;
|
||||||
|
|
||||||
use crate::instructions::{Instruction, Target, LoadTarget, RegisterPair};
|
use crate::instructions::{Instruction, Target, LoadTarget, RegisterPair};
|
||||||
|
|
||||||
pub enum Z80InstructionCycles {
|
pub enum Z80InstructionCycles {
|
||||||
Single(u16),
|
Single(u16),
|
||||||
Branch {
|
Branch { taken: u16, not_taken: u16 },
|
||||||
taken: u16,
|
Repeating { repeating: u16, terminating: u16 },
|
||||||
not_taken: u16
|
|
||||||
},
|
|
||||||
Repeating {
|
|
||||||
repeating: u16,
|
|
||||||
terminating: u16,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Z80InstructionCycles {
|
impl Z80InstructionCycles {
|
||||||
|
@ -20,33 +13,47 @@ impl Z80InstructionCycles {
|
||||||
match self {
|
match self {
|
||||||
Z80InstructionCycles::Single(cycles) => *cycles,
|
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> {
|
pub fn from_instruction(instruction: &Instruction, extra: u16) -> Result<Z80InstructionCycles, Error> {
|
||||||
let cycles = match instruction {
|
let cycles = match instruction {
|
||||||
Instruction::ADCa(target) |
|
Instruction::ADCa(target)
|
||||||
Instruction::ADDa(target) |
|
| Instruction::ADDa(target)
|
||||||
Instruction::AND(target) |
|
| Instruction::AND(target)
|
||||||
Instruction::CP(target) |
|
| Instruction::CP(target)
|
||||||
Instruction::SBCa(target) |
|
| Instruction::SBCa(target)
|
||||||
Instruction::SUB(target) |
|
| Instruction::SUB(target)
|
||||||
Instruction::OR(target) |
|
| Instruction::OR(target)
|
||||||
Instruction::XOR(target) => {
|
| Instruction::XOR(target) => match target {
|
||||||
match target {
|
Target::DirectReg(_) | Target::DirectRegHalf(_) => 4,
|
||||||
Target::DirectReg(_) |
|
Target::IndirectReg(_) => 7,
|
||||||
Target::DirectRegHalf(_) => 4,
|
Target::Immediate(_) => 7,
|
||||||
Target::IndirectReg(_) => 7,
|
Target::IndirectOffset(_, _) => 19,
|
||||||
Target::Immediate(_) => 7,
|
|
||||||
Target::IndirectOffset(_, _) => 19,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::ADC16(_, _) |
|
Instruction::ADC16(_, _) | Instruction::SBC16(_, _) => 15,
|
||||||
Instruction::SBC16(_, _) => 15,
|
|
||||||
|
|
||||||
Instruction::ADD16(dest_pair, _) => {
|
Instruction::ADD16(dest_pair, _) => {
|
||||||
if !dest_pair.is_index_reg() {
|
if !dest_pair.is_index_reg() {
|
||||||
|
@ -56,13 +63,11 @@ impl Z80InstructionCycles {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::BIT(_, target) => {
|
Instruction::BIT(_, target) => match target {
|
||||||
match target {
|
Target::DirectReg(_) => 8,
|
||||||
Target::DirectReg(_) => 8,
|
Target::IndirectReg(_) => 12,
|
||||||
Target::IndirectReg(_) => 12,
|
Target::IndirectOffset(_, _) => 20,
|
||||||
Target::IndirectOffset(_, _) => 20,
|
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
||||||
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::CALL(_) => 17,
|
Instruction::CALL(_) => 17,
|
||||||
|
@ -76,45 +81,40 @@ impl Z80InstructionCycles {
|
||||||
|
|
||||||
Instruction::CCF => 4,
|
Instruction::CCF => 4,
|
||||||
|
|
||||||
Instruction::CPD |
|
Instruction::CPD
|
||||||
Instruction::CPI |
|
| Instruction::CPI
|
||||||
Instruction::IND |
|
| Instruction::IND
|
||||||
Instruction::INI |
|
| Instruction::INI
|
||||||
Instruction::LDD |
|
| Instruction::LDD
|
||||||
Instruction::LDI |
|
| Instruction::LDI
|
||||||
Instruction::OUTD |
|
| Instruction::OUTD
|
||||||
Instruction::OUTI => 16,
|
| Instruction::OUTI => 16,
|
||||||
|
|
||||||
Instruction::CPDR |
|
Instruction::CPDR
|
||||||
Instruction::CPIR |
|
| Instruction::CPIR
|
||||||
Instruction::INDR |
|
| Instruction::INDR
|
||||||
Instruction::INIR |
|
| Instruction::INIR
|
||||||
Instruction::LDDR |
|
| Instruction::LDDR
|
||||||
Instruction::LDIR |
|
| Instruction::LDIR
|
||||||
Instruction::OTDR |
|
| Instruction::OTDR
|
||||||
Instruction::OTIR => {
|
| Instruction::OTIR => {
|
||||||
return Ok(Z80InstructionCycles::Repeating {
|
return Ok(Z80InstructionCycles::Repeating {
|
||||||
repeating: 21 + extra,
|
repeating: 21 + extra,
|
||||||
terminating: 16 + extra,
|
terminating: 16 + extra,
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::CPL => 4,
|
Instruction::CPL => 4,
|
||||||
Instruction::DAA => 4,
|
Instruction::DAA => 4,
|
||||||
|
|
||||||
Instruction::DEC8(target) |
|
Instruction::DEC8(target) | Instruction::INC8(target) => match target {
|
||||||
Instruction::INC8(target) => {
|
Target::DirectReg(_) | Target::DirectRegHalf(_) => 4,
|
||||||
match target {
|
Target::IndirectReg(_) => 11,
|
||||||
Target::DirectReg(_) |
|
Target::IndirectOffset(_, _) => 23,
|
||||||
Target::DirectRegHalf(_) => 4,
|
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
||||||
Target::IndirectReg(_) => 11,
|
|
||||||
Target::IndirectOffset(_, _) => 23,
|
|
||||||
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::DEC16(regpair) |
|
Instruction::DEC16(regpair) | Instruction::INC16(regpair) => {
|
||||||
Instruction::INC16(regpair) => {
|
|
||||||
if !regpair.is_index_reg() {
|
if !regpair.is_index_reg() {
|
||||||
6
|
6
|
||||||
} else {
|
} else {
|
||||||
|
@ -122,8 +122,7 @@ impl Z80InstructionCycles {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::DI |
|
Instruction::DI | Instruction::EI => 4,
|
||||||
Instruction::EI => 4,
|
|
||||||
|
|
||||||
Instruction::DJNZ(_) => {
|
Instruction::DJNZ(_) => {
|
||||||
return Ok(Z80InstructionCycles::Branch {
|
return Ok(Z80InstructionCycles::Branch {
|
||||||
|
@ -146,13 +145,9 @@ impl Z80InstructionCycles {
|
||||||
Instruction::HALT => 4,
|
Instruction::HALT => 4,
|
||||||
Instruction::IM(_) => 8,
|
Instruction::IM(_) => 8,
|
||||||
|
|
||||||
Instruction::INic(_) |
|
Instruction::INic(_) | Instruction::INicz | Instruction::OUTic(_) | Instruction::OUTicz => 12,
|
||||||
Instruction::INicz |
|
|
||||||
Instruction::OUTic(_) |
|
|
||||||
Instruction::OUTicz => 12,
|
|
||||||
|
|
||||||
Instruction::INx(_) |
|
Instruction::INx(_) | Instruction::OUTx(_) => 11,
|
||||||
Instruction::OUTx(_) => 11,
|
|
||||||
|
|
||||||
Instruction::JP(_) => 10,
|
Instruction::JP(_) => 10,
|
||||||
Instruction::JR(_) => 12,
|
Instruction::JR(_) => 12,
|
||||||
|
@ -177,29 +172,24 @@ impl Z80InstructionCycles {
|
||||||
Instruction::LD(dest, src) => {
|
Instruction::LD(dest, src) => {
|
||||||
match (dest, src) {
|
match (dest, src) {
|
||||||
// 8-Bit Operations
|
// 8-Bit Operations
|
||||||
|
|
||||||
(LoadTarget::DirectRegByte(_), LoadTarget::DirectRegByte(_)) => 4,
|
(LoadTarget::DirectRegByte(_), LoadTarget::DirectRegByte(_)) => 4,
|
||||||
|
|
||||||
(LoadTarget::DirectRegHalfByte(_), LoadTarget::DirectRegByte(_)) |
|
(LoadTarget::DirectRegHalfByte(_), LoadTarget::DirectRegByte(_))
|
||||||
(LoadTarget::DirectRegByte(_), LoadTarget::DirectRegHalfByte(_)) |
|
| (LoadTarget::DirectRegByte(_), LoadTarget::DirectRegHalfByte(_))
|
||||||
(LoadTarget::DirectRegHalfByte(_), LoadTarget::DirectRegHalfByte(_)) => 8,
|
| (LoadTarget::DirectRegHalfByte(_), LoadTarget::DirectRegHalfByte(_)) => 8,
|
||||||
|
|
||||||
(LoadTarget::DirectRegByte(_) | LoadTarget::DirectRegHalfByte(_), LoadTarget::ImmediateByte(_)) => 7,
|
(LoadTarget::DirectRegByte(_) | LoadTarget::DirectRegHalfByte(_), LoadTarget::ImmediateByte(_)) => 7,
|
||||||
(LoadTarget::IndirectRegByte(_), LoadTarget::ImmediateByte(_)) => 10,
|
(LoadTarget::IndirectRegByte(_), LoadTarget::ImmediateByte(_)) => 10,
|
||||||
|
|
||||||
(LoadTarget::IndirectOffsetByte(_, _), _) |
|
(LoadTarget::IndirectOffsetByte(_, _), _) | (_, LoadTarget::IndirectOffsetByte(_, _)) => 19,
|
||||||
(_, LoadTarget::IndirectOffsetByte(_, _)) => 19,
|
|
||||||
|
|
||||||
(_, LoadTarget::IndirectRegByte(_)) |
|
(_, LoadTarget::IndirectRegByte(_)) | (LoadTarget::IndirectRegByte(_), _) => 7,
|
||||||
(LoadTarget::IndirectRegByte(_), _) => 7,
|
|
||||||
|
|
||||||
(_, LoadTarget::IndirectByte(_)) |
|
(_, LoadTarget::IndirectByte(_)) | (LoadTarget::IndirectByte(_), _) => 13,
|
||||||
(LoadTarget::IndirectByte(_), _) => 13,
|
|
||||||
|
|
||||||
// 16-Bit Operations
|
// 16-Bit Operations
|
||||||
|
(LoadTarget::DirectRegWord(regpair), LoadTarget::ImmediateWord(_))
|
||||||
(LoadTarget::DirectRegWord(regpair), LoadTarget::ImmediateWord(_)) |
|
| (LoadTarget::ImmediateWord(_), LoadTarget::DirectRegWord(regpair)) => {
|
||||||
(LoadTarget::ImmediateWord(_), LoadTarget::DirectRegWord(regpair)) => {
|
|
||||||
if !regpair.is_index_reg() {
|
if !regpair.is_index_reg() {
|
||||||
10
|
10
|
||||||
} else {
|
} else {
|
||||||
|
@ -215,11 +205,10 @@ impl Z80InstructionCycles {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
(LoadTarget::IndirectWord(_), LoadTarget::DirectRegWord(RegisterPair::HL)) |
|
(LoadTarget::IndirectWord(_), LoadTarget::DirectRegWord(RegisterPair::HL))
|
||||||
(LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(_)) => 16,
|
| (LoadTarget::DirectRegWord(RegisterPair::HL), LoadTarget::IndirectWord(_)) => 16,
|
||||||
|
|
||||||
(LoadTarget::IndirectWord(_), _) |
|
(LoadTarget::IndirectWord(_), _) | (_, LoadTarget::IndirectWord(_)) => 20,
|
||||||
(_, LoadTarget::IndirectWord(_)) => 20,
|
|
||||||
|
|
||||||
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
||||||
}
|
}
|
||||||
|
@ -230,7 +219,6 @@ impl Z80InstructionCycles {
|
||||||
Instruction::NEG => 8,
|
Instruction::NEG => 8,
|
||||||
Instruction::NOP => 4,
|
Instruction::NOP => 4,
|
||||||
|
|
||||||
|
|
||||||
Instruction::POP(regpair) => {
|
Instruction::POP(regpair) => {
|
||||||
if !regpair.is_index_reg() {
|
if !regpair.is_index_reg() {
|
||||||
10
|
10
|
||||||
|
@ -246,14 +234,11 @@ impl Z80InstructionCycles {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::RES(_, target, _) |
|
Instruction::RES(_, target, _) | Instruction::SET(_, target, _) => match target {
|
||||||
Instruction::SET(_, target, _) => {
|
Target::DirectReg(_) => 8,
|
||||||
match target {
|
Target::IndirectReg(_) => 15,
|
||||||
Target::DirectReg(_) => 8,
|
Target::IndirectOffset(_, _) => 23,
|
||||||
Target::IndirectReg(_) => 15,
|
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
||||||
Target::IndirectOffset(_, _) => 23,
|
|
||||||
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::RET => 10,
|
Instruction::RET => 10,
|
||||||
|
@ -267,26 +252,21 @@ impl Z80InstructionCycles {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::RL(target, _) |
|
Instruction::RL(target, _)
|
||||||
Instruction::RLC(target, _) |
|
| Instruction::RLC(target, _)
|
||||||
Instruction::RR(target, _) |
|
| Instruction::RR(target, _)
|
||||||
Instruction::RRC(target, _) |
|
| Instruction::RRC(target, _)
|
||||||
Instruction::SLA(target, _) |
|
| Instruction::SLA(target, _)
|
||||||
Instruction::SLL(target, _) |
|
| Instruction::SLL(target, _)
|
||||||
Instruction::SRA(target, _) |
|
| Instruction::SRA(target, _)
|
||||||
Instruction::SRL(target, _) => {
|
| Instruction::SRL(target, _) => match target {
|
||||||
match target {
|
Target::DirectReg(_) => 8,
|
||||||
Target::DirectReg(_) => 8,
|
Target::IndirectReg(_) => 15,
|
||||||
Target::IndirectReg(_) => 15,
|
Target::IndirectOffset(_, _) => 23,
|
||||||
Target::IndirectOffset(_, _) => 23,
|
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
||||||
_ => return Err(Error::new(format!("unexpected instruction: {:?}", instruction))),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Instruction::RLA |
|
Instruction::RLA | Instruction::RLCA | Instruction::RRA | Instruction::RRCA => 4,
|
||||||
Instruction::RLCA |
|
|
||||||
Instruction::RRA |
|
|
||||||
Instruction::RRCA => 4,
|
|
||||||
|
|
||||||
Instruction::RLD => 18,
|
Instruction::RLD => 18,
|
||||||
Instruction::RRD => 18,
|
Instruction::RRD => 18,
|
||||||
|
@ -298,4 +278,3 @@ impl Z80InstructionCycles {
|
||||||
Ok(Z80InstructionCycles::Single(cycles + extra))
|
Ok(Z80InstructionCycles::Single(cycles + extra))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::Frequency;
|
use femtos::Frequency;
|
||||||
|
|
||||||
use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device};
|
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)] = &[
|
const DECODE_TESTS: &'static [(&[u8], Instruction)] = &[
|
||||||
(&[0x00], Instruction::NOP),
|
(&[0x00], Instruction::NOP),
|
||||||
(&[0x01, 0x01, 0x02], Instruction::LD(LoadTarget::DirectRegWord(RegisterPair::BC), LoadTarget::ImmediateWord(0x0201))),
|
(&[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, 0x84], Instruction::ADDa(Target::DirectRegHalf(IndexRegisterHalf::IXH))),
|
||||||
(&[0xDD, 0x85], Instruction::ADDa(Target::DirectRegHalf(IndexRegisterHalf::IXL))),
|
(&[0xDD, 0x85], Instruction::ADDa(Target::DirectRegHalf(IndexRegisterHalf::IXL))),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::Frequency;
|
use femtos::Frequency;
|
||||||
|
|
||||||
use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device};
|
use moa_core::{System, MemoryBlock, BusPort, Address, Addressable, Device};
|
||||||
|
@ -26,6 +25,7 @@ struct TestCase {
|
||||||
fini: TestState,
|
fini: TestState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
const TEST_CASES: &'static [TestCase] = &[
|
const TEST_CASES: &'static [TestCase] = &[
|
||||||
/*
|
/*
|
||||||
TestCase {
|
TestCase {
|
||||||
|
@ -551,4 +551,3 @@ pub fn run_execute_tests() {
|
||||||
run_test(case);
|
run_test(case);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex, MutexGuard};
|
use std::sync::{Arc, Mutex, MutexGuard};
|
||||||
use femtos::{Instant, Duration};
|
use femtos::{Instant, Duration};
|
||||||
|
|
||||||
|
@ -21,10 +20,7 @@ impl AudioSource {
|
||||||
let (id, sample_rate) = {
|
let (id, sample_rate) = {
|
||||||
let mut mixer = mixer.borrow_mut();
|
let mut mixer = mixer.borrow_mut();
|
||||||
let id = mixer.add_source(queue.clone());
|
let id = mixer.add_source(queue.clone());
|
||||||
(
|
(id, mixer.sample_rate())
|
||||||
id,
|
|
||||||
mixer.sample_rate(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -122,7 +118,9 @@ impl AudioMixerInner {
|
||||||
if let Some((clock, mut frame)) = source.pop_next() {
|
if let Some((clock, mut frame)) = source.pop_next() {
|
||||||
index = (clock.duration_since(frame_start) / sample_duration) as usize;
|
index = (clock.duration_since(frame_start) / sample_duration) as usize;
|
||||||
let size = frame.data.len().min(data.len() - index);
|
let size = frame.data.len().min(data.len() - index);
|
||||||
frame.data.iter()
|
frame
|
||||||
|
.data
|
||||||
|
.iter()
|
||||||
.zip(&mut data[index..index + size])
|
.zip(&mut data[index..index + size])
|
||||||
.for_each(|(source, dest)| {
|
.for_each(|(source, dest)| {
|
||||||
dest.0 += source.0;
|
dest.0 += source.0;
|
||||||
|
@ -198,4 +196,3 @@ impl AudioOutput {
|
||||||
self.queue.is_empty()
|
self.queue.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
use cpal::{
|
||||||
use cpal::{Stream, SampleRate, SampleFormat, StreamConfig, OutputCallbackInfo, traits::{DeviceTrait, HostTrait, StreamTrait}};
|
Stream, SampleRate, SampleFormat, StreamConfig, OutputCallbackInfo,
|
||||||
|
traits::{DeviceTrait, HostTrait, StreamTrait},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::audio::{AudioOutput, SAMPLE_RATE};
|
use crate::audio::{AudioOutput, SAMPLE_RATE};
|
||||||
|
|
||||||
|
@ -27,7 +29,9 @@ impl CpalAudioOutput {
|
||||||
while index < data.len() {
|
while index < data.len() {
|
||||||
if let Some((clock, mut frame)) = output.receive() {
|
if let Some((clock, mut frame)) = output.receive() {
|
||||||
let size = (frame.data.len() * 2).min(data.len() - index);
|
let size = (frame.data.len() * 2).min(data.len() - index);
|
||||||
frame.data.iter()
|
frame
|
||||||
|
.data
|
||||||
|
.iter()
|
||||||
.zip(data[index..index + size].chunks_mut(2))
|
.zip(data[index..index + size].chunks_mut(2))
|
||||||
.for_each(|(sample, location)| {
|
.for_each(|(sample, location)| {
|
||||||
location[0] = sample.0;
|
location[0] = sample.0;
|
||||||
|
@ -45,13 +49,11 @@ impl CpalAudioOutput {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let stream = device.build_output_stream(
|
let stream = device
|
||||||
&config,
|
.build_output_stream(&config, data_callback, move |err| {
|
||||||
data_callback,
|
|
||||||
move |err| {
|
|
||||||
log::error!("ERROR: {:?}", err);
|
log::error!("ERROR: {:?}", err);
|
||||||
},
|
})
|
||||||
).unwrap();
|
.unwrap();
|
||||||
|
|
||||||
stream.play().unwrap();
|
stream.play().unwrap();
|
||||||
|
|
||||||
|
@ -68,4 +70,3 @@ impl CpalAudioOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#[cfg(feature = "tty")]
|
#[cfg(feature = "tty")]
|
||||||
pub mod tty;
|
pub mod tty;
|
||||||
|
|
||||||
|
@ -9,4 +8,3 @@ pub use crate::audio::{AudioMixer, AudioSource};
|
||||||
pub mod cpal;
|
pub mod cpal;
|
||||||
#[cfg(feature = "audio")]
|
#[cfg(feature = "audio")]
|
||||||
pub use crate::cpal::CpalAudioOutput;
|
pub use crate::cpal::CpalAudioOutput;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -34,11 +33,13 @@ impl SimplePty {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open() -> Result<SimplePty, SimplePtyError> {
|
pub fn open() -> Result<SimplePty, SimplePtyError> {
|
||||||
let pty = pty::posix_openpt(OFlag::O_RDWR).and_then(|pty| {
|
let pty = pty::posix_openpt(OFlag::O_RDWR)
|
||||||
pty::grantpt(&pty)?;
|
.and_then(|pty| {
|
||||||
pty::unlockpt(&pty)?;
|
pty::grantpt(&pty)?;
|
||||||
Ok(pty)
|
pty::unlockpt(&pty)?;
|
||||||
}).map_err(|_| SimplePtyError::Open)?;
|
Ok(pty)
|
||||||
|
})
|
||||||
|
.map_err(|_| SimplePtyError::Open)?;
|
||||||
|
|
||||||
let name = unsafe { pty::ptsname(&pty).map_err(|_| SimplePtyError::PtsName)? };
|
let name = unsafe { pty::ptsname(&pty).map_err(|_| SimplePtyError::PtsName)? };
|
||||||
let (input_tx, input_rx) = mpsc::channel();
|
let (input_tx, input_rx) = mpsc::channel();
|
||||||
|
@ -61,8 +62,10 @@ impl SimplePty {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
input_tx.send(buf[0]).unwrap();
|
input_tx.send(buf[0]).unwrap();
|
||||||
},
|
},
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => { },
|
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {},
|
||||||
Err(err) => { println!("ERROR: {:?}", err); }
|
Err(err) => {
|
||||||
|
println!("ERROR: {:?}", err);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Ok(data) = output_rx.try_recv() {
|
while let Ok(data) = output_rx.try_recv() {
|
||||||
|
@ -92,4 +95,3 @@ impl Tty for SimplePty {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use femtos::Frequency;
|
use femtos::Frequency;
|
||||||
|
@ -45,4 +44,3 @@ fn main() {
|
||||||
});
|
});
|
||||||
thread::sleep(Duration::from_secs(10));
|
thread::sleep(Duration::from_secs(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use clap::Arg;
|
use clap::Arg;
|
||||||
|
|
||||||
use moa_console::ConsoleFrontend;
|
use moa_console::ConsoleFrontend;
|
||||||
|
@ -6,11 +5,13 @@ use moa_systems_computie::{build_computie, ComputieOptions};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = ConsoleFrontend::args("Computie68k Emulator")
|
let matches = ConsoleFrontend::args("Computie68k Emulator")
|
||||||
.arg(Arg::new("ROM")
|
.arg(
|
||||||
.short('r')
|
Arg::new("ROM")
|
||||||
.long("rom")
|
.short('r')
|
||||||
.value_name("FILE")
|
.long("rom")
|
||||||
.help("ROM file to load at the start of memory"))
|
.value_name("FILE")
|
||||||
|
.help("ROM file to load at the start of memory"),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let mut options = ComputieOptions::default();
|
let mut options = ComputieOptions::default();
|
||||||
|
@ -23,4 +24,3 @@ fn main() {
|
||||||
let system = build_computie(&frontend, options).unwrap();
|
let system = build_computie(&frontend, options).unwrap();
|
||||||
frontend.start(matches, system);
|
frontend.start(matches, system);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use clap::{Arg};
|
use clap::{Arg};
|
||||||
|
|
||||||
use moa_console::ConsoleFrontend;
|
use moa_console::ConsoleFrontend;
|
||||||
|
@ -6,8 +5,7 @@ use moa_systems_genesis::{build_genesis, SegaGenesisOptions};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = ConsoleFrontend::args("Sega Genesis/Mega Drive Emulator")
|
let matches = ConsoleFrontend::args("Sega Genesis/Mega Drive Emulator")
|
||||||
.arg(Arg::new("ROM")
|
.arg(Arg::new("ROM").help("ROM file to load (must be flat binary)"))
|
||||||
.help("ROM file to load (must be flat binary)"))
|
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let mut frontend = ConsoleFrontend;
|
let mut frontend = ConsoleFrontend;
|
||||||
|
@ -20,4 +18,3 @@ fn main() {
|
||||||
let system = build_genesis(&mut frontend, options).unwrap();
|
let system = build_genesis(&mut frontend, options).unwrap();
|
||||||
frontend.start(matches, system);
|
frontend.start(matches, system);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use clap::{Command, Arg, ArgAction, ArgMatches};
|
use clap::{Command, Arg, ArgAction, ArgMatches};
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use femtos::Duration;
|
use femtos::Duration;
|
||||||
|
@ -14,7 +13,8 @@ impl Host for ConsoleFrontend {
|
||||||
|
|
||||||
fn add_pty(&self) -> Result<Box<dyn Tty>, HostError<Self::Error>> {
|
fn add_pty(&self) -> Result<Box<dyn Tty>, HostError<Self::Error>> {
|
||||||
use moa_common::tty::SimplePty;
|
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>> {
|
fn add_video_source(&mut self, _receiver: FrameReceiver) -> Result<(), HostError<Self::Error>> {
|
||||||
|
@ -42,15 +42,19 @@ impl Default for ConsoleFrontend {
|
||||||
impl ConsoleFrontend {
|
impl ConsoleFrontend {
|
||||||
pub fn args(application_name: &'static str) -> Command {
|
pub fn args(application_name: &'static str) -> Command {
|
||||||
Command::new(application_name)
|
Command::new(application_name)
|
||||||
.arg(Arg::new("log-level")
|
.arg(
|
||||||
.short('l')
|
Arg::new("log-level")
|
||||||
.long("log-level")
|
.short('l')
|
||||||
.help("Set the type of log messages to print"))
|
.long("log-level")
|
||||||
.arg(Arg::new("debugger")
|
.help("Set the type of log messages to print"),
|
||||||
.short('d')
|
)
|
||||||
.long("debugger")
|
.arg(
|
||||||
.action(ArgAction::SetTrue)
|
Arg::new("debugger")
|
||||||
.help("Start the debugger before running machine"))
|
.short('d')
|
||||||
|
.long("debugger")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.help("Start the debugger before running machine"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(self, matches: ArgMatches, mut system: System) {
|
pub fn start(self, matches: ArgMatches, mut system: System) {
|
||||||
|
@ -108,4 +112,3 @@ impl ConsoleFrontend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
|
|
||||||
use clap::Arg;
|
use clap::Arg;
|
||||||
|
|
||||||
use moa_systems_genesis::{build_genesis, SegaGenesisOptions};
|
use moa_systems_genesis::{build_genesis, SegaGenesisOptions};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = moa_minifb::new("Sega Genesis/Mega Drive Emulator")
|
let matches = moa_minifb::new("Sega Genesis/Mega Drive Emulator")
|
||||||
.arg(Arg::new("ROM")
|
.arg(Arg::new("ROM").help("ROM file to load (must be flat binary)"))
|
||||||
.help("ROM file to load (must be flat binary)"))
|
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let mut options = SegaGenesisOptions::default();
|
let mut options = SegaGenesisOptions::default();
|
||||||
|
@ -14,8 +12,5 @@ fn main() {
|
||||||
options.rom = filename.to_string();
|
options.rom = filename.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
moa_minifb::run(matches, |frontend| {
|
moa_minifb::run(matches, |frontend| build_genesis(frontend, options));
|
||||||
build_genesis(frontend, options)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
|
|
||||||
use moa_systems_macintosh::build_macintosh_512k;
|
use moa_systems_macintosh::build_macintosh_512k;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = moa_minifb::new("Macintosh 512k Emulator")
|
let matches = moa_minifb::new("Macintosh 512k Emulator").get_matches();
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
moa_minifb::run(matches, |frontend| {
|
moa_minifb::run(matches, |frontend| build_macintosh_512k(frontend));
|
||||||
build_macintosh_512k(frontend)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration, Frequency};
|
use femtos::{Instant, Duration, Frequency};
|
||||||
|
|
||||||
use moa_peripherals_yamaha::{Ym2612, Sn76489};
|
use moa_peripherals_yamaha::{Ym2612, Sn76489};
|
||||||
|
@ -26,20 +25,23 @@ impl SynthControl {
|
||||||
impl Steppable for SynthControl {
|
impl Steppable for SynthControl {
|
||||||
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
fn step(&mut self, system: &System) -> Result<Duration, Error> {
|
||||||
if let Some(event) = self.key_receiver.receive() {
|
if let Some(event) = self.key_receiver.receive() {
|
||||||
|
|
||||||
match event.key {
|
match event.key {
|
||||||
Key::Enter => {
|
Key::Enter => {
|
||||||
system.get_bus().write_u8(system.clock, 0x00, 0x28)?;
|
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 => {
|
Key::A => {
|
||||||
system.get_bus().write_u8(system.clock, 0x10, 0x84)?;
|
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, 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() {
|
fn main() {
|
||||||
let matches = moa_minifb::new("YM2612 Tester/Synth")
|
let matches = moa_minifb::new("YM2612 Tester/Synth").get_matches();
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
moa_minifb::run(matches, |host| {
|
moa_minifb::run(matches, |host| {
|
||||||
let mut system = System::default();
|
let mut system = System::default();
|
||||||
|
@ -103,5 +104,3 @@ fn main() {
|
||||||
Ok(system)
|
Ok(system)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
|
||||||
use clap::{Arg, ArgAction};
|
use clap::{Arg, ArgAction};
|
||||||
|
|
||||||
use moa_systems_trs80::{build_trs80, Trs80Options};
|
use moa_systems_trs80::{build_trs80, Trs80Options};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = moa_minifb::new("TRS-80 Emulator")
|
let matches = moa_minifb::new("TRS-80 Emulator")
|
||||||
.arg(Arg::new("ROM")
|
.arg(
|
||||||
.short('r')
|
Arg::new("ROM")
|
||||||
.long("rom")
|
.short('r')
|
||||||
.action(ArgAction::SetTrue)
|
.long("rom")
|
||||||
.value_name("FILE")
|
.action(ArgAction::SetTrue)
|
||||||
.help("ROM file to load at the start of memory"))
|
.value_name("FILE")
|
||||||
|
.help("ROM file to load at the start of memory"),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let mut options = Trs80Options::default();
|
let mut options = Trs80Options::default();
|
||||||
|
@ -18,8 +19,5 @@ fn main() {
|
||||||
options.rom = filename.to_string();
|
options.rom = filename.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
moa_minifb::run(matches, |frontend| {
|
moa_minifb::run(matches, |frontend| build_trs80(frontend, options));
|
||||||
build_trs80(frontend, options)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
|
|
||||||
use minifb::Key as MiniKey;
|
use minifb::Key as MiniKey;
|
||||||
use moa_host::ControllerInput;
|
use moa_host::ControllerInput;
|
||||||
|
|
||||||
pub fn map_controller_a(key: MiniKey, state: bool) -> Option<ControllerInput> {
|
pub fn map_controller_a(key: MiniKey, state: bool) -> Option<ControllerInput> {
|
||||||
match key {
|
match key {
|
||||||
MiniKey::A => { Some(ControllerInput::ButtonA(state)) },
|
MiniKey::A => Some(ControllerInput::ButtonA(state)),
|
||||||
MiniKey::O => { Some(ControllerInput::ButtonB(state)) },
|
MiniKey::O => Some(ControllerInput::ButtonB(state)),
|
||||||
MiniKey::E => { Some(ControllerInput::ButtonC(state)) },
|
MiniKey::E => Some(ControllerInput::ButtonC(state)),
|
||||||
MiniKey::Up => { Some(ControllerInput::DpadUp(state)) },
|
MiniKey::Up => Some(ControllerInput::DpadUp(state)),
|
||||||
MiniKey::Down => { Some(ControllerInput::DpadDown(state)) },
|
MiniKey::Down => Some(ControllerInput::DpadDown(state)),
|
||||||
MiniKey::Left => { Some(ControllerInput::DpadLeft(state)) },
|
MiniKey::Left => Some(ControllerInput::DpadLeft(state)),
|
||||||
MiniKey::Right => { Some(ControllerInput::DpadRight(state)) },
|
MiniKey::Right => Some(ControllerInput::DpadRight(state)),
|
||||||
MiniKey::Enter => { Some(ControllerInput::Start(state)) },
|
MiniKey::Enter => Some(ControllerInput::Start(state)),
|
||||||
MiniKey::M => { Some(ControllerInput::Mode(state)) },
|
MiniKey::M => Some(ControllerInput::Mode(state)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use minifb::Key as MiniKey;
|
use minifb::Key as MiniKey;
|
||||||
use moa_host::Key;
|
use moa_host::Key;
|
||||||
|
|
||||||
|
@ -109,4 +108,3 @@ pub fn map_key(key: MiniKey) -> Key {
|
||||||
_ => Key::Unknown,
|
_ => Key::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
@ -10,13 +9,16 @@ use femtos::{Duration as FemtosDuration};
|
||||||
|
|
||||||
use moa_core::{System, Error, Device};
|
use moa_core::{System, Error, Device};
|
||||||
use moa_debugger::{Debugger, DebugControl};
|
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::{AudioMixer, AudioSource};
|
||||||
use moa_common::CpalAudioOutput;
|
use moa_common::CpalAudioOutput;
|
||||||
|
|
||||||
mod keys;
|
|
||||||
mod controllers;
|
mod controllers;
|
||||||
|
mod keys;
|
||||||
|
|
||||||
use crate::keys::map_key;
|
use crate::keys::map_key;
|
||||||
use crate::controllers::map_controller_a;
|
use crate::controllers::map_controller_a;
|
||||||
|
@ -28,36 +30,46 @@ const HEIGHT: u32 = 224;
|
||||||
|
|
||||||
pub fn new(name: &'static str) -> Command {
|
pub fn new(name: &'static str) -> Command {
|
||||||
Command::new(name)
|
Command::new(name)
|
||||||
.arg(Arg::new("scale")
|
.arg(Arg::new("scale").short('s').long("scale").help("Scale the screen"))
|
||||||
.short('s')
|
.arg(
|
||||||
.long("scale")
|
Arg::new("speed")
|
||||||
.help("Scale the screen"))
|
.short('x')
|
||||||
.arg(Arg::new("speed")
|
.long("speed")
|
||||||
.short('x')
|
.help("Adjust the speed of the simulation"),
|
||||||
.long("speed")
|
)
|
||||||
.help("Adjust the speed of the simulation"))
|
.arg(
|
||||||
.arg(Arg::new("threaded")
|
Arg::new("threaded")
|
||||||
.short('t')
|
.short('t')
|
||||||
.long("threaded")
|
.long("threaded")
|
||||||
.action(ArgAction::SetTrue)
|
.action(ArgAction::SetTrue)
|
||||||
.help("Run the simulation in a separate thread"))
|
.help("Run the simulation in a separate thread"),
|
||||||
.arg(Arg::new("log-level")
|
)
|
||||||
.short('l')
|
.arg(
|
||||||
.long("log-level")
|
Arg::new("log-level")
|
||||||
.help("Set the type of log messages to print"))
|
.short('l')
|
||||||
.arg(Arg::new("debugger")
|
.long("log-level")
|
||||||
.short('d')
|
.help("Set the type of log messages to print"),
|
||||||
.long("debugger")
|
)
|
||||||
.action(ArgAction::SetTrue)
|
.arg(
|
||||||
.help("Start the debugger before running machine"))
|
Arg::new("debugger")
|
||||||
.arg(Arg::new("disable-audio")
|
.short('d')
|
||||||
.short('a')
|
.long("debugger")
|
||||||
.long("disable-audio")
|
.action(ArgAction::SetTrue)
|
||||||
.action(ArgAction::SetTrue)
|
.help("Start the debugger before running machine"),
|
||||||
.help("Disable audio output"))
|
)
|
||||||
|
.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") {
|
if matches.get_flag("threaded") {
|
||||||
run_threaded(matches, init);
|
run_threaded(matches, init);
|
||||||
} else {
|
} 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 mut frontend = MiniFrontendBuilder::default();
|
||||||
let system = init(&mut frontend).unwrap();
|
let system = init(&mut frontend).unwrap();
|
||||||
|
|
||||||
frontend
|
frontend.build().start(matches, Some(system));
|
||||||
.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()));
|
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());
|
wait_until_initialized(frontend.clone());
|
||||||
|
|
||||||
frontend
|
frontend.lock().unwrap().build().start(matches, None);
|
||||||
.lock().unwrap()
|
|
||||||
.build()
|
|
||||||
.start(matches, None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_until_initialized(frontend: Arc<Mutex<MiniFrontendBuilder>>) {
|
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>> {
|
fn register_controllers(&mut self, sender: EventSender<ControllerEvent>) -> Result<(), HostError<Self::Error>> {
|
||||||
if self.controllers.is_some() {
|
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);
|
self.controllers = Some(sender);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -253,13 +268,7 @@ impl MiniFrontend {
|
||||||
queue.request_encoding(PixelEncoding::ARGB);
|
queue.request_encoding(PixelEncoding::ARGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut window = minifb::Window::new(
|
let mut window = minifb::Window::new("Test - ESC to exit", size.0 as usize, size.1 as usize, options).unwrap_or_else(|e| {
|
||||||
"Test - ESC to exit",
|
|
||||||
size.0 as usize,
|
|
||||||
size.1 as usize,
|
|
||||||
options,
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
panic!("{}", e);
|
panic!("{}", e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -345,7 +354,9 @@ impl MiniFrontend {
|
||||||
if let Some((_clock, frame)) = queue.latest() {
|
if let Some((_clock, frame)) = queue.latest() {
|
||||||
last_frame = frame
|
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;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,7 +71,11 @@ impl Iterator for SquareWave {
|
||||||
fn next(&mut self) -> Option<f32> {
|
fn next(&mut self) -> Option<f32> {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
let samples_per_hz = self.sample_rate as f32 / self.frequency;
|
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)
|
Some(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,4 +165,3 @@ impl Iterator for SkewedSquareWave {
|
||||||
pub fn db_to_gain(db: f32) -> f32 {
|
pub fn db_to_gain(db: f32) -> f32 {
|
||||||
(10.0_f32).powf(db / 20.0)
|
(10.0_f32).powf(db / 20.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use moa_core::{Error, System, Address, Addressable};
|
use moa_core::{Error, System, Address, Addressable};
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,11 +55,7 @@ impl Debugger {
|
||||||
let args: Vec<&str> = command.split_whitespace().collect();
|
let args: Vec<&str> = command.split_whitespace().collect();
|
||||||
|
|
||||||
// If no command given, then run the `step` command
|
// If no command given, then run the `step` command
|
||||||
let args = if args.is_empty() {
|
let args = if args.is_empty() { vec!["step"] } else { args };
|
||||||
vec!["step"]
|
|
||||||
} else {
|
|
||||||
args
|
|
||||||
};
|
|
||||||
|
|
||||||
match args[0] {
|
match args[0] {
|
||||||
"b" | "break" | "breakpoint" => {
|
"b" | "break" | "breakpoint" => {
|
||||||
|
@ -123,7 +118,11 @@ impl Debugger {
|
||||||
"d" | "dump" => {
|
"d" | "dump" => {
|
||||||
if args.len() > 1 {
|
if args.len() > 1 {
|
||||||
let addr = u32::from_str_radix(args[1], 16).map_err(|_| Error::new("Unable to parse address"))?;
|
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);
|
system.get_bus().dump_memory(system.clock, addr as Address, len as Address);
|
||||||
} else {
|
} else {
|
||||||
//self.port.dump_memory(self.state.ssp as Address, 0x40 as Address);
|
//self.port.dump_memory(self.state.ssp as Address, 0x40 as Address);
|
||||||
|
@ -135,7 +134,9 @@ impl Debugger {
|
||||||
} else {
|
} else {
|
||||||
let device = system.get_device(args[1])?;
|
let device = system.get_device(args[1])?;
|
||||||
let subargs = if args.len() > 2 { &args[2..] } else { &[""] };
|
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"))?
|
.ok_or_else(|| Error::new("That device is not inspectable"))?
|
||||||
.inspect(system, subargs)?;
|
.inspect(system, subargs)?;
|
||||||
}
|
}
|
||||||
|
@ -154,7 +155,11 @@ impl Debugger {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(device) = system.get_next_debuggable_device() {
|
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" => {
|
"c" | "continue" => {
|
||||||
|
@ -170,7 +175,7 @@ impl Debugger {
|
||||||
self.trace_only = true;
|
self.trace_only = true;
|
||||||
system.step_until_debuggable()?;
|
system.step_until_debuggable()?;
|
||||||
return Ok(DebugControl::Continue);
|
return Ok(DebugControl::Continue);
|
||||||
}
|
},
|
||||||
"setb" | "setw" | "setl" => {
|
"setb" | "setw" | "setl" => {
|
||||||
if args.len() != 3 {
|
if args.len() != 3 {
|
||||||
println!("Usage: set[b|w|l] <addr> <data>");
|
println!("Usage: set[b|w|l] <addr> <data>");
|
||||||
|
@ -208,7 +213,9 @@ impl Debugger {
|
||||||
|
|
||||||
fn check_repeat_arg(&mut self, args: &[&str]) -> Result<(), Error> {
|
fn check_repeat_arg(&mut self, args: &[&str]) -> Result<(), Error> {
|
||||||
if args.len() > 1 {
|
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()));
|
self.repeat_command = Some((count, args[0].to_string()));
|
||||||
}
|
}
|
||||||
Ok(())
|
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"))?;
|
let addr = Address::from_str_radix(addrstr, 16).map_err(|_| Error::new("Unable to parse address"))?;
|
||||||
Ok((name, addr))
|
Ok((name, addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
pub struct Sample(pub f32, pub f32);
|
pub struct Sample(pub f32, pub f32);
|
||||||
|
|
||||||
|
@ -22,4 +21,3 @@ impl AudioFrame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum ControllerDevice {
|
pub enum ControllerDevice {
|
||||||
A,
|
A,
|
||||||
|
@ -37,4 +36,3 @@ impl ControllerEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,9 @@ impl Pixel {
|
||||||
};
|
};
|
||||||
|
|
||||||
match encoding {
|
match encoding {
|
||||||
PixelEncoding::RGBA =>
|
PixelEncoding::RGBA => (r << 24) | (g << 16) | (b << 8) | a,
|
||||||
(r << 24) | (g << 16) | (b << 8) | a,
|
PixelEncoding::ARGB => (a << 24) | (r << 16) | (g << 8) | b,
|
||||||
PixelEncoding::ARGB =>
|
PixelEncoding::ABGR => (a << 24) | (b << 16) | (g << 8) | r,
|
||||||
(a << 24) | (r << 16) | (g << 8) | b,
|
|
||||||
PixelEncoding::ABGR =>
|
|
||||||
(a << 24) | (b << 16) | (g << 8) | r,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,22 +68,22 @@ impl Frame {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: Pixel) {
|
pub fn set_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: Pixel) {
|
||||||
match pixel {
|
match pixel {
|
||||||
Pixel::Mask => {}
|
Pixel::Mask => {},
|
||||||
value if pos_x < self.width && pos_y < self.height => {
|
value if pos_x < self.width && pos_y < self.height => {
|
||||||
self.bitmap[(pos_x + (pos_y * self.width)) as usize] = value.encode(self.encoding);
|
self.bitmap[(pos_x + (pos_y * self.width)) as usize] = value.encode(self.encoding);
|
||||||
}
|
},
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_encoded_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: u32) {
|
pub fn set_encoded_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: u32) {
|
||||||
match pixel {
|
match pixel {
|
||||||
MASK_COLOUR => { },
|
MASK_COLOUR => {},
|
||||||
value if pos_x < self.width && pos_y < self.height => {
|
value if pos_x < self.width && pos_y < self.height => {
|
||||||
self.bitmap[(pos_x + (pos_y * self.width)) as usize] = value;
|
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 y in pos_y..(pos_y + height) {
|
||||||
for x in pos_x..(pos_x + width) {
|
for x in pos_x..(pos_x + width) {
|
||||||
match bitmap.next().unwrap() {
|
match bitmap.next().unwrap() {
|
||||||
Pixel::Mask => {}
|
Pixel::Mask => {},
|
||||||
value if x < self.width && y < self.height => {
|
value if x < self.width && y < self.height => {
|
||||||
self.bitmap[(x + (y * self.width)) as usize] = value.encode(self.encoding);
|
self.bitmap[(x + (y * self.width)) as usize] = value.encode(self.encoding);
|
||||||
}
|
},
|
||||||
_ => {}
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,4 +156,3 @@ impl FrameReceiver {
|
||||||
self.queue.pop_latest()
|
self.queue.pop_latest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
@ -35,4 +34,3 @@ impl<T> EventReceiver<T> {
|
||||||
self.queue.lock().unwrap().pop_front()
|
self.queue.lock().unwrap().pop_front()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Key {
|
pub enum Key {
|
||||||
A,
|
A,
|
||||||
|
@ -124,4 +123,3 @@ impl KeyEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
mod audio;
|
mod audio;
|
||||||
mod controllers;
|
mod controllers;
|
||||||
mod gfx;
|
mod gfx;
|
||||||
|
@ -14,4 +13,3 @@ pub use crate::mouse::{MouseButton, MouseEventType, MouseEvent, MouseState};
|
||||||
pub use crate::controllers::{ControllerDevice, ControllerInput, ControllerEvent};
|
pub use crate::controllers::{ControllerDevice, ControllerInput, ControllerEvent};
|
||||||
pub use crate::input::{EventSender, EventReceiver, event_queue};
|
pub use crate::input::{EventSender, EventReceiver, event_queue};
|
||||||
pub use crate::traits::{Host, HostError, Tty, Audio, ClockedQueue, DummyAudio};
|
pub use crate::traits::{Host, HostError, Tty, Audio, ClockedQueue, DummyAudio};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum MouseButton {
|
pub enum MouseButton {
|
||||||
Left,
|
Left,
|
||||||
|
@ -68,7 +67,8 @@ impl MouseState {
|
||||||
self.pos = next_state.pos;
|
self.pos = next_state.pos;
|
||||||
|
|
||||||
let events: Vec<MouseEvent> = self
|
let events: Vec<MouseEvent> = self
|
||||||
.buttons.into_iter()
|
.buttons
|
||||||
|
.into_iter()
|
||||||
.zip(next_state.buttons)
|
.zip(next_state.buttons)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, (prev, next))| {
|
.filter_map(|(i, (prev, next))| {
|
||||||
|
@ -102,8 +102,7 @@ impl MouseState {
|
||||||
match event.etype {
|
match event.etype {
|
||||||
MouseEventType::Up(button) => self.buttons[usize::from(button)] = false,
|
MouseEventType::Up(button) => self.buttons[usize::from(button)] = false,
|
||||||
MouseEventType::Down(button) => self.buttons[usize::from(button)] = true,
|
MouseEventType::Down(button) => self.buttons[usize::from(button)] = true,
|
||||||
MouseEventType::Move => { },
|
MouseEventType::Move => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -131,4 +130,3 @@ impl Audio for DummyAudio {
|
||||||
|
|
||||||
fn write_samples(&mut self, _clock: Instant, _buffer: &[Sample]) {}
|
fn write_samples(&mut self, _clock: Instant, _buffer: &[Sample]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::str::Chars;
|
use std::str::Chars;
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
@ -56,9 +55,13 @@ impl<'input> AssemblyParser<'input> {
|
||||||
fn parse_line(&mut self) -> Result<Option<AssemblyLine>, ParserError> {
|
fn parse_line(&mut self) -> Result<Option<AssemblyLine>, ParserError> {
|
||||||
let token = loop {
|
let token = loop {
|
||||||
match self.lexer.get_next() {
|
match self.lexer.get_next() {
|
||||||
Some(token) if token == "\n" => { },
|
Some(token) if token == "\n" => {},
|
||||||
Some(token) => { break token; }
|
Some(token) => {
|
||||||
None => { return Ok(None); },
|
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 {
|
} else {
|
||||||
Ok(AssemblyOperand::Label(token))
|
Ok(AssemblyOperand::Label(token))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,7 +244,8 @@ impl<'input> AssemblyLexer<'input> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_next(&mut self) -> Result<String, ParserError> {
|
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> {
|
pub fn expect_token(&mut self, expected: &str) -> Result<(), ParserError> {
|
||||||
|
@ -245,7 +253,10 @@ impl<'input> AssemblyLexer<'input> {
|
||||||
if token == expected {
|
if token == expected {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} 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 {
|
if args.len() == expected {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} 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 {
|
if let AssemblyOperand::Immediate(value) = operand {
|
||||||
Ok(*value)
|
Ok(*value)
|
||||||
} else {
|
} 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
|
//pub struct LevelTriggeredOutput
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,40 @@
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use femtos::Instant;
|
use femtos::Instant;
|
||||||
|
|
||||||
use moa_core::{Error, Address, Addressable, Transmutable};
|
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;
|
#[rustfmt::skip]
|
||||||
const ATA_REG_DATA_BYTE: Address = 0x21;
|
mod cmd {
|
||||||
const ATA_REG_FEATURE: Address = 0x23;
|
pub(super) const READ_SECTORS: u8 = 0x20;
|
||||||
const ATA_REG_ERROR: Address = 0x23;
|
pub(super) const WRITE_SECTORS: u8 = 0x30;
|
||||||
const ATA_REG_SECTOR_COUNT: Address = 0x25;
|
pub(super) const IDENTIFY: u8 = 0xEC;
|
||||||
const ATA_REG_SECTOR_NUM: Address = 0x27;
|
pub(super) const SET_FEATURE: u8 = 0xEF;
|
||||||
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;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const ATA_ST_BUSY: u8 = 0x80;
|
const ATA_ST_BUSY: u8 = 0x80;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const ATA_ST_DATA_READY: u8 = 0x08;
|
const ATA_ST_DATA_READY: u8 = 0x08;
|
||||||
#[allow(dead_code)]
|
#[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";
|
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> {
|
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
||||||
match addr {
|
match addr {
|
||||||
ATA_REG_DATA_WORD => {
|
reg::DATA_WORD => {
|
||||||
self.selected_count -= 2;
|
self.selected_count -= 2;
|
||||||
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize;
|
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize;
|
||||||
data[0] = self.contents[offset];
|
data[0] = self.contents[offset];
|
||||||
|
@ -70,7 +75,7 @@ impl Addressable for AtaDevice {
|
||||||
self.selected_count = 0;
|
self.selected_count = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ATA_REG_DATA_BYTE => {
|
reg::DATA_BYTE => {
|
||||||
self.selected_count -= 1;
|
self.selected_count -= 1;
|
||||||
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize;
|
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize;
|
||||||
data[0] = self.contents[offset];
|
data[0] = self.contents[offset];
|
||||||
|
@ -79,13 +84,15 @@ impl Addressable for AtaDevice {
|
||||||
self.selected_count = 0;
|
self.selected_count = 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ATA_REG_STATUS => {
|
reg::STATUS => {
|
||||||
data[0] = ATA_ST_DATA_READY;
|
data[0] = ATA_ST_DATA_READY;
|
||||||
},
|
},
|
||||||
ATA_REG_ERROR => {
|
reg::ERROR => {
|
||||||
data[0] = self.last_error;
|
data[0] = self.last_error;
|
||||||
},
|
},
|
||||||
_ => { log::debug!("{}: reading from {:0x}", DEV_NAME, addr); },
|
_ => {
|
||||||
|
log::debug!("{}: reading from {:0x}", DEV_NAME, addr);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -94,27 +101,43 @@ impl Addressable for AtaDevice {
|
||||||
fn write(&mut self, _clock: Instant, addr: Address, data: &[u8]) -> Result<(), Error> {
|
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]);
|
log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
||||||
match addr {
|
match addr {
|
||||||
ATA_REG_DRIVE_HEAD => { self.selected_sector |= ((data[0] & 0x1F) as u32) << 24; },
|
reg::DRIVE_HEAD => {
|
||||||
ATA_REG_CYL_HIGH => { self.selected_sector |= (data[0] as u32) << 16; },
|
self.selected_sector |= ((data[0] & 0x1F) as u32) << 24;
|
||||||
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]); },
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
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
|
// TODO implement features
|
||||||
},
|
},
|
||||||
ATA_REG_DATA_BYTE => {
|
reg::DATA_BYTE => {
|
||||||
// TODO implement writing
|
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -125,4 +148,3 @@ impl Transmutable for AtaDevice {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
|
|
||||||
mod ata;
|
mod ata;
|
||||||
pub use crate::ata::AtaDevice;
|
pub use crate::ata::AtaDevice;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
|
|
||||||
mod mos6522;
|
mod mos6522;
|
||||||
pub use crate::mos6522::Mos6522;
|
pub use crate::mos6522::Mos6522;
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration};
|
use femtos::{Instant, Duration};
|
||||||
|
|
||||||
use moa_core::{Error, System, Address, Addressable, Steppable, Transmutable};
|
use moa_core::{Error, System, Address, Addressable, Steppable, Transmutable};
|
||||||
use moa_signals::{Signal, ObservableSignal, Observable};
|
use moa_signals::{Signal, ObservableSignal, Observable};
|
||||||
|
|
||||||
const REG_OUTPUT_B: Address = 0x00;
|
#[rustfmt::skip]
|
||||||
const REG_OUTPUT_A: Address = 0x01;
|
mod reg {
|
||||||
const REG_DDR_B: Address = 0x02;
|
use super::Address;
|
||||||
const REG_DDR_A: Address = 0x03;
|
pub(super) const OUTPUT_B: Address = 0x00;
|
||||||
const REG_PERIPH_CTRL: Address = 0x0C;
|
pub(super) const OUTPUT_A: Address = 0x01;
|
||||||
const REG_INT_FLAGS: Address = 0x0D;
|
pub(super) const DDR_B: Address = 0x02;
|
||||||
const REG_INT_ENABLE: Address = 0x0E;
|
pub(super) const DDR_A: Address = 0x03;
|
||||||
const REG_OUTPUT_A_NHS: Address = 0x0F;
|
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";
|
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> {
|
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
||||||
match addr {
|
match addr {
|
||||||
REG_OUTPUT_B => { data[0] = self.port_b.borrow_mut().data; },
|
reg::OUTPUT_B => {
|
||||||
REG_OUTPUT_A => { data[0] = self.port_a.borrow_mut().data; },
|
data[0] = self.port_b.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::OUTPUT_A => {
|
||||||
REG_INT_FLAGS => { data[0] = self.interrupt_flags; },
|
data[0] = self.port_a.borrow_mut().data;
|
||||||
REG_INT_ENABLE => { data[0] = self.interrupt_enable | 0x80; },
|
},
|
||||||
|
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);
|
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> {
|
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]);
|
log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
||||||
match addr {
|
match addr {
|
||||||
REG_OUTPUT_B => { self.port_b.borrow_mut().data = data[0]; self.port_b.notify(); },
|
reg::OUTPUT_B => {
|
||||||
REG_OUTPUT_A => { self.port_a.borrow_mut().data = data[0]; self.port_a.notify(); },
|
self.port_b.borrow_mut().data = data[0];
|
||||||
REG_DDR_B => { self.port_b.borrow_mut().ddr = data[0]; self.port_b.notify(); },
|
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::OUTPUT_A => {
|
||||||
REG_INT_FLAGS => { self.interrupt_flags &= !data[0] & 0x7F; },
|
self.port_a.borrow_mut().data = data[0];
|
||||||
REG_INT_ENABLE => {
|
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 {
|
if (data[0] & 0x80) == 0 {
|
||||||
self.interrupt_flags &= !data[0];
|
self.interrupt_flags &= !data[0];
|
||||||
} else {
|
} 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);
|
log::warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr);
|
||||||
},
|
},
|
||||||
|
@ -102,7 +137,6 @@ impl Addressable for Mos6522 {
|
||||||
|
|
||||||
impl Steppable for Mos6522 {
|
impl Steppable for Mos6522 {
|
||||||
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
||||||
|
|
||||||
Ok(Duration::from_micros(16_600))
|
Ok(Duration::from_micros(16_600))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,4 +151,3 @@ impl Transmutable for Mos6522 {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
|
|
||||||
mod mc68681;
|
mod mc68681;
|
||||||
pub use crate::mc68681::MC68681;
|
pub use crate::mc68681::MC68681;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration, Frequency};
|
use femtos::{Instant, Duration, Frequency};
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Steppable, Addressable, Transmutable};
|
use moa_core::{System, Error, Address, Steppable, Addressable, Transmutable};
|
||||||
|
@ -95,15 +94,23 @@ impl MC68681Port {
|
||||||
|
|
||||||
pub fn set_tx_status(&mut self, value: bool) {
|
pub fn set_tx_status(&mut self, value: bool) {
|
||||||
match value {
|
match value {
|
||||||
true => { self.status |= SR_TX_READY | SR_TX_EMPTY; },
|
true => {
|
||||||
false => { self.status &= !(SR_TX_READY | SR_TX_EMPTY); },
|
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) {
|
pub fn set_rx_status(&mut self, value: bool) {
|
||||||
match value {
|
match value {
|
||||||
true => { self.status |= SR_RX_READY; },
|
true => {
|
||||||
false => { self.status &= !SR_RX_READY; },
|
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> {
|
pub fn handle_command(&mut self, data: u8) -> Option<bool> {
|
||||||
let rx_cmd = data& 0x03;
|
let rx_cmd = data & 0x03;
|
||||||
if rx_cmd == 0b01 {
|
if rx_cmd == 0b01 {
|
||||||
self.rx_enabled = true;
|
self.rx_enabled = true;
|
||||||
} else if rx_cmd == 0b10 {
|
} else if rx_cmd == 0b10 {
|
||||||
|
@ -202,7 +209,9 @@ impl MC68681 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_interrupt_state(&mut self, system: &System) -> Result<(), Error> {
|
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> {
|
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
||||||
match addr {
|
match addr {
|
||||||
REG_SRA_RD => {
|
REG_SRA_RD => data[0] = self.port_a.status,
|
||||||
data[0] = self.port_a.status
|
|
||||||
},
|
|
||||||
REG_RBA_RD => {
|
REG_RBA_RD => {
|
||||||
data[0] = self.port_a.input;
|
data[0] = self.port_a.input;
|
||||||
self.port_a.set_rx_status(false);
|
self.port_a.set_rx_status(false);
|
||||||
self.set_interrupt_flag(ISR_CH_A_RX_READY_FULL, false);
|
self.set_interrupt_flag(ISR_CH_A_RX_READY_FULL, false);
|
||||||
},
|
},
|
||||||
REG_SRB_RD => {
|
REG_SRB_RD => data[0] = self.port_b.status,
|
||||||
data[0] = self.port_b.status
|
|
||||||
},
|
|
||||||
REG_RBB_RD => {
|
REG_RBB_RD => {
|
||||||
data[0] = self.port_b.input;
|
data[0] = self.port_b.input;
|
||||||
self.port_b.set_rx_status(false);
|
self.port_b.set_rx_status(false);
|
||||||
|
@ -294,7 +299,7 @@ impl Addressable for MC68681 {
|
||||||
}
|
}
|
||||||
self.set_interrupt_flag(ISR_TIMER_CHANGE, false);
|
self.set_interrupt_flag(ISR_TIMER_CHANGE, false);
|
||||||
},
|
},
|
||||||
_ => { },
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
if addr != REG_SRA_RD && addr != REG_SRB_RD {
|
if addr != REG_SRA_RD && addr != REG_SRB_RD {
|
||||||
|
@ -312,7 +317,7 @@ impl Addressable for MC68681 {
|
||||||
},
|
},
|
||||||
REG_ACR_WR => {
|
REG_ACR_WR => {
|
||||||
self.acr = data[0];
|
self.acr = data[0];
|
||||||
}
|
},
|
||||||
REG_TBA_WR => {
|
REG_TBA_WR => {
|
||||||
log::debug!("{}a: write {}", DEV_NAME, data[0] as char);
|
log::debug!("{}a: write {}", DEV_NAME, data[0] as char);
|
||||||
self.port_a.send_byte(data[0]);
|
self.port_a.send_byte(data[0]);
|
||||||
|
@ -354,7 +359,7 @@ impl Addressable for MC68681 {
|
||||||
REG_OUT_RESET => {
|
REG_OUT_RESET => {
|
||||||
self.output_state &= !data[0];
|
self.output_state &= !data[0];
|
||||||
},
|
},
|
||||||
_ => { },
|
_ => {},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
mod sn76489;
|
mod sn76489;
|
||||||
pub use crate::sn76489::Sn76489;
|
pub use crate::sn76489::Sn76489;
|
||||||
|
|
||||||
mod ym2612;
|
mod ym2612;
|
||||||
pub use crate::ym2612::Ym2612;
|
pub use crate::ym2612::Ym2612;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration, Frequency};
|
use femtos::{Instant, Duration, Frequency};
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
||||||
|
@ -96,7 +95,7 @@ pub struct Sn76489 {
|
||||||
impl Sn76489 {
|
impl Sn76489 {
|
||||||
pub fn new<H, E>(host: &mut H, _clock_frequency: Frequency) -> Result<Self, HostError<E>>
|
pub fn new<H, E>(host: &mut H, _clock_frequency: Frequency) -> Result<Self, HostError<E>>
|
||||||
where
|
where
|
||||||
H: Host<Error = E>
|
H: Host<Error = E>,
|
||||||
{
|
{
|
||||||
let source = host.add_audio_source()?;
|
let source = host.add_audio_source()?;
|
||||||
let sample_rate = source.samples_per_second();
|
let sample_rate = source.samples_per_second();
|
||||||
|
@ -134,7 +133,7 @@ impl Steppable for Sn76489 {
|
||||||
}
|
}
|
||||||
self.source.write_samples(system.clock, &buffer);
|
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),
|
5 => self.tones[2].set_attenuation(value),
|
||||||
6 => self.noise.set_control(value),
|
6 => self.noise.set_control(value),
|
||||||
7 => self.noise.set_attenuation(value),
|
7 => self.noise.set_attenuation(value),
|
||||||
_ => { self.first_byte = Some(data[0]); },
|
_ => {
|
||||||
|
self.first_byte = Some(data[0]);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let first = self.first_byte.unwrap_or(0);
|
let first = self.first_byte.unwrap_or(0);
|
||||||
|
@ -173,7 +174,7 @@ impl Addressable for Sn76489 {
|
||||||
0 => self.tones[0].set_counter(value),
|
0 => self.tones[0].set_counter(value),
|
||||||
2 => self.tones[1].set_counter(value),
|
2 => self.tones[1].set_counter(value),
|
||||||
4 => self.tones[2].set_counter(value),
|
4 => self.tones[2].set_counter(value),
|
||||||
_ => { },
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
log::debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
||||||
|
@ -190,4 +191,3 @@ impl Transmutable for Sn76489 {
|
||||||
Some(self)
|
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
|
/// 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
|
/// of the envelope attenuation, based on the rate that's currently active
|
||||||
|
#[rustfmt::skip]
|
||||||
const COUNTER_SHIFT_VALUES: &[u16] = &[
|
const COUNTER_SHIFT_VALUES: &[u16] = &[
|
||||||
11, 11, 11, 11,
|
11, 11, 11, 11,
|
||||||
10, 10, 10, 10,
|
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
|
/// 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
|
/// possible angle values to a sequence of 8 cycles, and the amount to increment the attenuation
|
||||||
/// at each point in that cycle
|
/// at each point in that cycle
|
||||||
|
#[rustfmt::skip]
|
||||||
const RATE_TABLE: &[u16] = &[
|
const RATE_TABLE: &[u16] = &[
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
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,
|
8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
const DETUNE_TABLE: &[u8] = &[
|
const DETUNE_TABLE: &[u8] = &[
|
||||||
0, 0, 1, 2,
|
0, 0, 1, 2,
|
||||||
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 rate = self.get_scaled_rate(self.envelope_state, rate_adjust);
|
||||||
let counter_shift = COUNTER_SHIFT_VALUES[rate];
|
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 {
|
if envelope_clock % (1 << counter_shift) == 0 {
|
||||||
let update_cycle = (envelope_clock >> counter_shift) & 0x07;
|
let update_cycle = (envelope_clock >> counter_shift) & 0x07;
|
||||||
let increment = RATE_TABLE[rate * 8 + update_cycle as usize];
|
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
|
// 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
|
// clamped to MAX_ENVELOPE will produce the same results
|
||||||
let new_envelope = self.envelope + (((!self.envelope * increment) as i16) >> 4) as u16;
|
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 {
|
if new_envelope > self.envelope {
|
||||||
self.envelope_state = EnvelopeState::Decay;
|
self.envelope_state = EnvelopeState::Decay;
|
||||||
self.envelope = 0;
|
self.envelope = 0;
|
||||||
|
@ -296,19 +293,16 @@ impl EnvelopeGenerator {
|
||||||
self.envelope = new_envelope.min(MAX_ENVELOPE);
|
self.envelope = new_envelope.min(MAX_ENVELOPE);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EnvelopeState::Decay |
|
EnvelopeState::Decay | EnvelopeState::Sustain | EnvelopeState::Release => {
|
||||||
EnvelopeState::Sustain |
|
|
||||||
EnvelopeState::Release => {
|
|
||||||
// Convert it to a fixed point decimal number of 4 bit : 8 bits, which will be the output
|
// Convert it to a fixed point decimal number of 4 bit : 8 bits, which will be the output
|
||||||
self.envelope += increment << 2;
|
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;
|
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)
|
increment.saturating_add(detune)
|
||||||
} else {
|
} else {
|
||||||
increment.saturating_sub(detune)
|
increment.saturating_sub(detune)
|
||||||
}.min(0x1FFFF);
|
}
|
||||||
|
.min(0x1FFFF);
|
||||||
|
|
||||||
// Apply multiple
|
// Apply multiple
|
||||||
let increment = if self.multiple == 0 {
|
let increment = if self.multiple == 0 {
|
||||||
|
@ -440,6 +435,7 @@ impl PhaseGenerator {
|
||||||
///
|
///
|
||||||
/// K1 = F11
|
/// K1 = F11
|
||||||
/// K0 = F11 & (F10 | F9 | F8) | !F11 & (F10 & F9 & F8)
|
/// K0 = F11 & (F10 | F9 | F8) | !F11 & (F10 & F9 & F8)
|
||||||
|
#[rustfmt::skip]
|
||||||
const FNUMBER_TO_KEYCODE: &[u8] = &[
|
const FNUMBER_TO_KEYCODE: &[u8] = &[
|
||||||
0, 0, 0, 0, 0, 0, 0, 1,
|
0, 0, 0, 0, 0, 0, 0, 1,
|
||||||
2, 3, 3, 3, 3, 3, 3, 3,
|
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) {
|
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();
|
self.phase.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,10 +516,6 @@ impl Operator {
|
||||||
|
|
||||||
let mod_phase = phase + modulator;
|
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
|
// 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
|
// 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
|
// attenuation from the envelope, to get the total attenuation at this point
|
||||||
|
@ -567,7 +560,9 @@ impl Channel {
|
||||||
Self {
|
Self {
|
||||||
debug_name: debug_name.clone(),
|
debug_name: debug_name.clone(),
|
||||||
enabled: (true, true),
|
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,
|
algorithm: OperatorAlgorithm::A0,
|
||||||
feedback: 0,
|
feedback: 0,
|
||||||
|
|
||||||
|
@ -617,10 +612,6 @@ impl Channel {
|
||||||
|
|
||||||
//let output = sign_extend_u16(output, 14);
|
//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 sample = output as f32 / (1 << 13) as f32;
|
||||||
|
|
||||||
let left = if self.enabled.0 { sample } else { 0.0 };
|
let left = if self.enabled.0 { sample } else { 0.0 };
|
||||||
|
@ -663,7 +654,9 @@ impl Channel {
|
||||||
},
|
},
|
||||||
OperatorAlgorithm::A5 => {
|
OperatorAlgorithm::A5 => {
|
||||||
let output1 = self.operators[0].get_output(feedback, clocks);
|
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 => {
|
OperatorAlgorithm::A6 => {
|
||||||
let output1 = self.operators[0].get_output(feedback, clocks);
|
let output1 = self.operators[0].get_output(feedback, clocks);
|
||||||
|
@ -672,9 +665,9 @@ impl Channel {
|
||||||
},
|
},
|
||||||
OperatorAlgorithm::A7 => {
|
OperatorAlgorithm::A7 => {
|
||||||
self.operators[0].get_output(feedback, clocks)
|
self.operators[0].get_output(feedback, clocks)
|
||||||
+ self.operators[1].get_output(0, clocks)
|
+ self.operators[1].get_output(0, clocks)
|
||||||
+ self.operators[2].get_output(0, clocks)
|
+ self.operators[2].get_output(0, clocks)
|
||||||
+ self.operators[3].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);
|
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);
|
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 (ch, op) = get_ch_op(bank, reg);
|
||||||
let index = get_index(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
|
// Register is 4 bits, so adjust it to match total_level's scale
|
||||||
let sustain_level = (self.registers[0x80 + index] as u16 & 0xF0) << 3;
|
let sustain_level = (self.registers[0x80 + index] as u16 & 0xF0) << 3;
|
||||||
// Adjust the maximum storable value to be the max attenuation
|
// 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);
|
self.channels[ch].operators[op].set_sustain_level(sustain_level);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1033,7 +1030,7 @@ impl Addressable for Ym2612 {
|
||||||
0..=3 => {
|
0..=3 => {
|
||||||
// Read the status byte (busy/overflow)
|
// Read the status byte (busy/overflow)
|
||||||
data[0] = ((self.timer_a_overflow as u8) << 1) | (self.timer_b_overflow as u8);
|
data[0] = ((self.timer_a_overflow as u8) << 1) | (self.timer_b_overflow as u8);
|
||||||
}
|
},
|
||||||
_ => {
|
_ => {
|
||||||
log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
|
log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
|
||||||
},
|
},
|
||||||
|
@ -1078,4 +1075,3 @@ impl Transmutable for Ym2612 {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
|
|
||||||
mod z8530;
|
mod z8530;
|
||||||
pub use crate::z8530::Z8530;
|
pub use crate::z8530::Z8530;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration};
|
use femtos::{Instant, Duration};
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
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";
|
const DEV_NAME: &str = "z8530";
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Z8530 {
|
pub struct Z8530 {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Addressable for Z8530 {
|
impl Addressable for Z8530 {
|
||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
|
@ -30,7 +27,6 @@ impl Addressable for Z8530 {
|
||||||
|
|
||||||
impl Steppable for Z8530 {
|
impl Steppable for Z8530 {
|
||||||
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
||||||
|
|
||||||
Ok(Duration::from_secs(1))
|
Ok(Duration::from_secs(1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,5 +40,3 @@ impl Transmutable for Z8530 {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
|
|
||||||
mod system;
|
mod system;
|
||||||
pub use crate::system::{build_computie, build_computie_k30, launch_terminal_emulator, launch_slip_connection, ComputieOptions};
|
pub use crate::system::{build_computie, build_computie_k30, launch_terminal_emulator, launch_slip_connection, ComputieOptions};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::Frequency;
|
use femtos::Frequency;
|
||||||
|
|
||||||
use moa_core::{System, Error, Debuggable, MemoryBlock, Device};
|
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);
|
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);
|
cpu.add_breakpoint(0);
|
||||||
|
|
||||||
system.add_interruptable_device("cpu", Device::new(cpu))?;
|
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));
|
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))?;
|
system.add_interruptable_device("cpu", Device::new(cpu))?;
|
||||||
|
|
||||||
Ok(system)
|
Ok(system)
|
||||||
|
@ -104,18 +85,39 @@ pub fn launch_terminal_emulator(name: String) {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::process::Command;
|
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));
|
thread::sleep(Duration::from_secs(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn launch_slip_connection(name: String) {
|
pub fn launch_slip_connection(name: String) {
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
Command::new("sudo").args(["slattach", "-s", "38400", "-p", "slip", &name]).spawn().unwrap();
|
Command::new("sudo")
|
||||||
Command::new("sudo").args(["ifconfig", "sl0", "192.168.1.2", "pointopoint", "192.168.1.200", "up"]).status().unwrap();
|
.args(["slattach", "-s", "38400", "-p", "slip", &name])
|
||||||
Command::new("sudo").args(["arp", "-Ds", "192.168.1.200", "enp4s0", "pub"]).status().unwrap();
|
.spawn()
|
||||||
Command::new("sudo").args(["iptables", "-A", "FORWARD", "-i", "sl0", "-j", "ACCEPT"]).status().unwrap();
|
.unwrap();
|
||||||
Command::new("sudo").args(["iptables", "-A", "FORWARD", "-o", "sl0", "-j", "ACCEPT"]).status().unwrap();
|
Command::new("sudo")
|
||||||
Command::new("sudo").args(["sh", "-c", "echo 1 > /proc/sys/net/ipv4/ip_forward"]).status().unwrap();
|
.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 peripherals;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
mod system;
|
mod system;
|
||||||
pub use crate::system::{SegaGenesisOptions, build_genesis};
|
pub use crate::system::{SegaGenesisOptions, build_genesis};
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration};
|
use femtos::{Instant, Duration};
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
||||||
use moa_host::{self, Host, HostError, ControllerDevice, ControllerInput, ControllerEvent, EventReceiver};
|
use moa_host::{self, Host, HostError, ControllerDevice, ControllerInput, ControllerEvent, EventReceiver};
|
||||||
use moa_signals::{Signal};
|
use moa_signals::{Signal};
|
||||||
|
|
||||||
const REG_VERSION: Address = 0x01;
|
const REG_VERSION: Address = 0x01;
|
||||||
const REG_DATA1: Address = 0x03;
|
const REG_DATA1: Address = 0x03;
|
||||||
const REG_DATA2: Address = 0x05;
|
const REG_DATA2: Address = 0x05;
|
||||||
const REG_DATA3: Address = 0x07;
|
const REG_DATA3: Address = 0x07;
|
||||||
const REG_CTRL1: Address = 0x09;
|
const REG_CTRL1: Address = 0x09;
|
||||||
const REG_CTRL2: Address = 0x0B;
|
const REG_CTRL2: Address = 0x0B;
|
||||||
const REG_CTRL3: Address = 0x0D;
|
const REG_CTRL3: Address = 0x0D;
|
||||||
const REG_S_CTRL1: Address = 0x13;
|
const REG_S_CTRL1: Address = 0x13;
|
||||||
const REG_S_CTRL2: Address = 0x19;
|
const REG_S_CTRL2: Address = 0x19;
|
||||||
const REG_S_CTRL3: Address = 0x1F;
|
const REG_S_CTRL3: Address = 0x1F;
|
||||||
|
|
||||||
|
|
||||||
const DEV_NAME: &str = "genesis_controller";
|
const DEV_NAME: &str = "genesis_controller";
|
||||||
|
@ -50,13 +49,13 @@ impl GenesisControllerPort {
|
||||||
let th_state = (self.outputs & 0x40) != 0;
|
let th_state = (self.outputs & 0x40) != 0;
|
||||||
|
|
||||||
match (th_state, self.th_count) {
|
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),
|
(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),
|
(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,
|
(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),
|
(false, 0) => self.outputs | (((inputs & 0x00C0) >> 2) as u8) | ((inputs & 0x0003) as u8),
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
|
@ -159,17 +158,39 @@ impl Addressable for GenesisControllers {
|
||||||
}
|
}
|
||||||
|
|
||||||
match addr {
|
match addr {
|
||||||
REG_VERSION => { data[i] = 0xA0; } // Overseas Version, NTSC, No Expansion
|
REG_VERSION => {
|
||||||
REG_DATA1 => { data[i] = self.port_1.get_data(); },
|
data[i] = 0xA0;
|
||||||
REG_DATA2 => { data[i] = self.port_2.get_data(); },
|
}, // Overseas Version, NTSC, No Expansion
|
||||||
REG_DATA3 => { data[i] = self.expansion.get_data(); },
|
REG_DATA1 => {
|
||||||
REG_CTRL1 => { data[i] = self.port_1.ctrl; },
|
data[i] = self.port_1.get_data();
|
||||||
REG_CTRL2 => { data[i] = self.port_2.ctrl; },
|
},
|
||||||
REG_CTRL3 => { data[i] = self.expansion.ctrl; },
|
REG_DATA2 => {
|
||||||
REG_S_CTRL1 => { data[i] = self.port_1.s_ctrl | 0x02; },
|
data[i] = self.port_2.get_data();
|
||||||
REG_S_CTRL2 => { data[i] = self.port_2.s_ctrl | 0x02; },
|
},
|
||||||
REG_S_CTRL3 => { data[i] = self.expansion.s_ctrl | 0x02; },
|
REG_DATA3 => {
|
||||||
_ => { log::warn!("{}: !!! unhandled reading from {:0x}", DEV_NAME, addr); },
|
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]);
|
log::info!("{}: read from register {:x} the value {:x}", DEV_NAME, addr, data[0]);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -180,16 +201,36 @@ impl Addressable for GenesisControllers {
|
||||||
|
|
||||||
log::info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
log::info!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
||||||
match addr {
|
match addr {
|
||||||
REG_DATA1 => { self.port_1.set_data(data[0]); }
|
REG_DATA1 => {
|
||||||
REG_DATA2 => { self.port_2.set_data(data[0]); },
|
self.port_1.set_data(data[0]);
|
||||||
REG_DATA3 => { self.expansion.set_data(data[0]); },
|
},
|
||||||
REG_CTRL1 => { self.port_1.set_ctrl(data[0]); },
|
REG_DATA2 => {
|
||||||
REG_CTRL2 => { self.port_2.set_ctrl(data[0]); },
|
self.port_2.set_data(data[0]);
|
||||||
REG_CTRL3 => { self.expansion.set_ctrl(data[0]); },
|
},
|
||||||
REG_S_CTRL1 => { self.port_1.s_ctrl = data[0] & 0xF8; },
|
REG_DATA3 => {
|
||||||
REG_S_CTRL2 => { self.port_2.s_ctrl = data[0] & 0xF8; },
|
self.expansion.set_data(data[0]);
|
||||||
REG_S_CTRL3 => { self.expansion.s_ctrl = data[0] & 0xF8; },
|
},
|
||||||
_ => { log::warn!("{}: !!! unhandled write of {:0x} to {:0x}", DEV_NAME, data[0], addr); },
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -197,7 +238,7 @@ impl Addressable for GenesisControllers {
|
||||||
|
|
||||||
impl Steppable for GenesisControllers {
|
impl Steppable for GenesisControllers {
|
||||||
fn step(&mut self, _system: &System) -> Result<Duration, Error> {
|
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() {
|
while let Some(event) = self.receiver.receive() {
|
||||||
self.process_event(event);
|
self.process_event(event);
|
||||||
|
@ -222,5 +263,3 @@ impl Transmutable for GenesisControllers {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use femtos::Instant;
|
use femtos::Instant;
|
||||||
|
@ -31,9 +30,15 @@ impl Addressable for CoprocessorCoordinator {
|
||||||
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
fn read(&mut self, _clock: Instant, addr: Address, data: &mut [u8]) -> Result<(), Error> {
|
||||||
match addr {
|
match addr {
|
||||||
0x100 => {
|
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);
|
log::info!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -49,7 +54,9 @@ impl Addressable for CoprocessorCoordinator {
|
||||||
0x200 => {
|
0x200 => {
|
||||||
self.reset.set(data[0] == 0);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -101,7 +108,9 @@ pub struct CoprocessorBankArea {
|
||||||
impl CoprocessorBankArea {
|
impl CoprocessorBankArea {
|
||||||
pub fn new(bus: Rc<RefCell<Bus>>) -> (Self, CoprocessorBankRegister) {
|
pub fn new(bus: Rc<RefCell<Bus>>) -> (Self, CoprocessorBankRegister) {
|
||||||
let base = Rc::new(Cell::new(0));
|
let base = Rc::new(Cell::new(0));
|
||||||
let register = CoprocessorBankRegister { base: base.clone() };
|
let register = CoprocessorBankRegister {
|
||||||
|
base: base.clone(),
|
||||||
|
};
|
||||||
let bank = Self {
|
let bank = Self {
|
||||||
base,
|
base,
|
||||||
bus,
|
bus,
|
||||||
|
@ -129,4 +138,3 @@ impl Transmutable for CoprocessorBankArea {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
pub mod ym7101;
|
|
||||||
pub mod controllers;
|
pub mod controllers;
|
||||||
pub mod coprocessor;
|
pub mod coprocessor;
|
||||||
|
pub mod ym7101;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration, Frequency};
|
use femtos::{Instant, Duration, Frequency};
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Addressable, Steppable, Inspectable, Transmutable, Device, read_beu16, dump_slice};
|
use moa_core::{System, Error, Address, Addressable, Steppable, Inspectable, Transmutable, Device, read_beu16, dump_slice};
|
||||||
|
@ -171,7 +170,13 @@ impl Ym7101Memory {
|
||||||
4 => Memory::Vsram,
|
4 => Memory::Vsram,
|
||||||
_ => Memory::Cram,
|
_ => 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 & 0x20) != 0 {
|
||||||
if (self.transfer_type & 0x10) != 0 {
|
if (self.transfer_type & 0x10) != 0 {
|
||||||
self.set_dma_mode(DmaType::Copy);
|
self.set_dma_mode(DmaType::Copy);
|
||||||
|
@ -198,7 +203,15 @@ impl Ym7101Memory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.transfer_dest_addr += self.transfer_auto_inc;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +221,14 @@ impl Ym7101Memory {
|
||||||
self.transfer_fill_word = if data.len() >= 2 { read_beu16(data) } else { data[0] as u16 };
|
self.transfer_fill_word = if data.len() >= 2 { read_beu16(data) } else { data[0] as u16 };
|
||||||
self.set_dma_mode(DmaType::Fill);
|
self.set_dma_mode(DmaType::Fill);
|
||||||
} else {
|
} 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;
|
let addr = self.transfer_dest_addr as usize;
|
||||||
|
@ -225,10 +245,12 @@ impl Ym7101Memory {
|
||||||
fn write_control_port(&mut self, data: &[u8]) -> Result<(), Error> {
|
fn write_control_port(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||||
let value = read_beu16(data);
|
let value = read_beu16(data);
|
||||||
match (data.len(), self.ctrl_port_buffer) {
|
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)),
|
(2, Some(upper)) => self.setup_transfer(upper, read_beu16(data)),
|
||||||
(4, None) => self.setup_transfer(value, read_beu16(&data[2..])),
|
(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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -239,7 +261,15 @@ impl Ym7101Memory {
|
||||||
|
|
||||||
match self.transfer_run {
|
match self.transfer_run {
|
||||||
DmaType::Memory => {
|
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();
|
let mut bus = system.get_bus();
|
||||||
|
|
||||||
while self.transfer_remain > 0 {
|
while self.transfer_remain > 0 {
|
||||||
|
@ -257,7 +287,13 @@ impl Ym7101Memory {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DmaType::Copy => {
|
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 {
|
while self.transfer_remain > 0 {
|
||||||
self.vram[self.transfer_dest_addr as usize] = self.vram[self.transfer_src_addr as usize];
|
self.vram[self.transfer_dest_addr as usize] = self.vram[self.transfer_src_addr as usize];
|
||||||
self.transfer_dest_addr += self.transfer_auto_inc;
|
self.transfer_dest_addr += self.transfer_auto_inc;
|
||||||
|
@ -266,14 +302,22 @@ impl Ym7101Memory {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DmaType::Fill => {
|
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 {
|
while self.transfer_remain > 0 {
|
||||||
self.vram[self.transfer_dest_addr as usize] = self.transfer_fill_word as u8;
|
self.vram[self.transfer_dest_addr as usize] = self.transfer_fill_word as u8;
|
||||||
self.transfer_dest_addr += self.transfer_auto_inc;
|
self.transfer_dest_addr += self.transfer_auto_inc;
|
||||||
self.transfer_remain -= 1;
|
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);
|
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)
|
Pixel::Rgb(((rgb & 0x00F) << 4) as u8, (rgb & 0x0F0) as u8, ((rgb & 0xF00) >> 4) as u8).encode(encoding)
|
||||||
} else {
|
} else {
|
||||||
let offset = if mode == ColourMode::Highlight { 0x80 } else { 0x00 };
|
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 {
|
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)
|
(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.current_y += 1;
|
||||||
|
|
||||||
self.state.h_scanlines = self.state.h_scanlines.wrapping_sub(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;
|
self.state.h_scanlines = self.state.h_int_lines;
|
||||||
system.get_interrupt_controller().set(true, 4, 28)?;
|
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) {
|
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.state.draw_frame(&mut frame);
|
||||||
self.sender.add(system.clock, 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 {
|
if (self.state.mode_2 & mode2::BF_DMA_ENABLED) != 0 {
|
||||||
self.state.memory.step_dma(system)?;
|
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)
|
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) {
|
fn update_register_value(&mut self, reg: usize, data: u8) {
|
||||||
match reg {
|
match reg {
|
||||||
reg::MODE_SET_1 => { self.state.mode_1 = data; },
|
reg::MODE_SET_1 => {
|
||||||
|
self.state.mode_1 = data;
|
||||||
|
},
|
||||||
reg::MODE_SET_2 => {
|
reg::MODE_SET_2 => {
|
||||||
self.state.mode_2 = data;
|
self.state.mode_2 = data;
|
||||||
self.state.update_screen_size();
|
self.state.update_screen_size();
|
||||||
},
|
},
|
||||||
reg::SCROLL_A_ADDR => { self.state.scroll_a_addr = (data as usize) << 10; },
|
reg::SCROLL_A_ADDR => {
|
||||||
reg::WINDOW_ADDR => { self.state.window_addr = (data as usize) << 10; },
|
self.state.scroll_a_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::WINDOW_ADDR => {
|
||||||
reg::BACKGROUND => { self.state.background = data; },
|
self.state.window_addr = (data as usize) << 10;
|
||||||
reg::H_INTERRUPT => { self.state.h_int_lines = data; },
|
},
|
||||||
reg::MODE_SET_3 => { self.state.mode_3 = data; },
|
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 => {
|
reg::MODE_SET_4 => {
|
||||||
self.state.mode_4 = data;
|
self.state.mode_4 = data;
|
||||||
self.state.update_screen_size();
|
self.state.update_screen_size();
|
||||||
},
|
},
|
||||||
reg::HSCROLL_ADDR => { self.state.hscroll_addr = (data as usize) << 10; },
|
reg::HSCROLL_ADDR => {
|
||||||
reg::AUTO_INCREMENT => { self.state.memory.transfer_auto_inc = data as u32; },
|
self.state.hscroll_addr = (data as usize) << 10;
|
||||||
|
},
|
||||||
|
reg::AUTO_INCREMENT => {
|
||||||
|
self.state.memory.transfer_auto_inc = data as u32;
|
||||||
|
},
|
||||||
reg::SCROLL_SIZE => {
|
reg::SCROLL_SIZE => {
|
||||||
let h = decode_scroll_size(data & 0x03);
|
let h = decode_scroll_size(data & 0x03);
|
||||||
let v = decode_scroll_size((data >> 4) & 0x03);
|
let v = decode_scroll_size((data >> 4) & 0x03);
|
||||||
|
@ -790,10 +876,13 @@ impl Ym7101 {
|
||||||
reg::DMA_ADDR_HIGH => {
|
reg::DMA_ADDR_HIGH => {
|
||||||
let mask = if (data & 0x80) == 0 { 0x7F } else { 0x3F };
|
let mask = if (data & 0x80) == 0 { 0x7F } else { 0x3F };
|
||||||
self.state.memory.transfer_bits = data & 0xC0;
|
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 */ },
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -864,7 +955,12 @@ impl Addressable for Ym7101 {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.state.memory.write_control_port(data)?;
|
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)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -905,7 +1003,7 @@ impl Inspectable for Ym7101 {
|
||||||
"vsram" => {
|
"vsram" => {
|
||||||
self.state.dump_vsram();
|
self.state.dump_vsram();
|
||||||
},
|
},
|
||||||
_ => { },
|
_ => {},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -941,4 +1039,3 @@ impl Ym7101State {
|
||||||
dump_slice(&self.memory.vsram, 80);
|
dump_slice(&self.memory.vsram, 80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -99,4 +98,3 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
|
||||||
|
|
||||||
Ok(system)
|
Ok(system)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use moa_core::Error;
|
use moa_core::Error;
|
||||||
|
@ -39,5 +38,3 @@ pub fn load_rom_file(filename: &str) -> Result<Vec<u8>, Error> {
|
||||||
|
|
||||||
Ok(contents)
|
Ok(contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
|
|
||||||
pub mod peripherals;
|
pub mod peripherals;
|
||||||
|
|
||||||
mod system;
|
mod system;
|
||||||
pub use crate::system::{build_macintosh_512k};
|
pub use crate::system::{build_macintosh_512k};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration};
|
use femtos::{Instant, Duration};
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
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 CA1: u8 = 0x02;
|
||||||
//const CA2: u8 = 0x04;
|
//const CA2: u8 = 0x04;
|
||||||
//const LSTRB: u8 = 0x08;
|
//const LSTRB: u8 = 0x08;
|
||||||
const ENABLE: u8 = 0x10;
|
const ENABLE: u8 = 0x10;
|
||||||
//const SELECT: u8 = 0x20;
|
//const SELECT: u8 = 0x20;
|
||||||
const Q6: u8 = 0x40;
|
const Q6: u8 = 0x40;
|
||||||
const Q7: u8 = 0x80;
|
const Q7: u8 = 0x80;
|
||||||
|
|
||||||
const DEV_NAME: &str = "iwm";
|
const DEV_NAME: &str = "iwm";
|
||||||
|
|
||||||
|
@ -61,7 +60,7 @@ impl Addressable for IWM {
|
||||||
Q7 => {
|
Q7 => {
|
||||||
// read "write-handshake" register
|
// read "write-handshake" register
|
||||||
data[i] = 0x3F | self.handshake;
|
data[i] = 0x3F | self.handshake;
|
||||||
}
|
},
|
||||||
b if b == (Q7 | Q6) => {
|
b if b == (Q7 | Q6) => {
|
||||||
panic!("");
|
panic!("");
|
||||||
},
|
},
|
||||||
|
@ -111,4 +110,3 @@ impl Transmutable for IWM {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use femtos::{Instant, Duration};
|
use femtos::{Instant, Duration};
|
||||||
|
@ -47,16 +46,30 @@ impl Mainboard {
|
||||||
if (port.data & 0x10) == 0 {
|
if (port.data & 0x10) == 0 {
|
||||||
println!("{}: overlay is 0 (normal)", DEV_NAME);
|
println!("{}: overlay is 0 (normal)", DEV_NAME);
|
||||||
lower_bus.borrow_mut().clear_all_bus_devices();
|
lower_bus.borrow_mut().clear_all_bus_devices();
|
||||||
lower_bus.borrow_mut().insert(0x000000, Device::new(AddressRepeater::new(ram.clone(), 0x400000)));
|
lower_bus
|
||||||
lower_bus.borrow_mut().insert(0x400000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
|
.borrow_mut()
|
||||||
lower_bus.borrow_mut().insert(0x600000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
|
.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 {
|
} else {
|
||||||
println!("{}: overlay is 1 (startup)", DEV_NAME);
|
println!("{}: overlay is 1 (startup)", DEV_NAME);
|
||||||
lower_bus.borrow_mut().clear_all_bus_devices();
|
lower_bus.borrow_mut().clear_all_bus_devices();
|
||||||
lower_bus.borrow_mut().insert(0x000000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
|
lower_bus
|
||||||
lower_bus.borrow_mut().insert(0x200000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
|
.borrow_mut()
|
||||||
lower_bus.borrow_mut().insert(0x400000, Device::new(AddressRepeater::new(rom.clone(), 0x100000)));
|
.insert(0x000000, 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(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)]
|
#[derive(Default)]
|
||||||
pub struct PhaseRead {
|
pub struct PhaseRead {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Addressable for PhaseRead {
|
impl Addressable for PhaseRead {
|
||||||
fn size(&self) -> usize {
|
fn size(&self) -> usize {
|
||||||
|
@ -159,4 +170,3 @@ impl Addressable for PhaseRead {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
pub mod iwm;
|
pub mod iwm;
|
||||||
pub mod video;
|
|
||||||
pub mod mainboard;
|
pub mod mainboard;
|
||||||
|
pub mod video;
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
|
||||||
use femtos::Duration;
|
use femtos::Duration;
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
||||||
use moa_host::{self, Host, HostError, Frame, FrameSender, Pixel};
|
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);
|
const SCRN_SIZE: (u32, u32) = (512, 342);
|
||||||
|
|
||||||
pub struct MacVideo {
|
pub struct MacVideo {
|
||||||
|
@ -81,4 +80,3 @@ impl Transmutable for MacVideo {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::Frequency;
|
use femtos::Frequency;
|
||||||
|
|
||||||
use moa_core::{System, Error, MemoryBlock, Debuggable, Device};
|
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(0x400694); // Ram Test
|
||||||
|
|
||||||
//cpu.add_breakpoint(0x400170); // Failed, loops infinitely
|
//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(0x4006ae);
|
||||||
//cpu.add_breakpoint(0x400706);
|
//cpu.add_breakpoint(0x400706);
|
||||||
//cpu.add_breakpoint(0x400722); // end of ram test
|
//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(0x40045c);
|
||||||
//cpu.add_breakpoint(0x400614); // Start of InitIO
|
//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(0x400648);
|
||||||
//cpu.add_breakpoint(0x40064c);
|
//cpu.add_breakpoint(0x40064c);
|
||||||
//cpu.add_breakpoint(0x4014a6); // DrvrInstall
|
//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
|
// Issue of writing to 0x100000 which doesn't exist
|
||||||
cpu.add_breakpoint(0x400d62);
|
cpu.add_breakpoint(0x400d62);
|
||||||
|
|
||||||
cpu.add_breakpoint(0x400464); // Boot Screen
|
cpu.add_breakpoint(0x400464); // Boot Screen
|
||||||
|
|
||||||
/*
|
/*
|
||||||
use crate::devices::Addressable;
|
use crate::devices::Addressable;
|
||||||
|
@ -127,4 +126,3 @@ pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
|
||||||
|
|
||||||
Ok(system)
|
Ok(system)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
|
|
||||||
pub mod peripherals;
|
pub mod peripherals;
|
||||||
|
|
||||||
mod system;
|
mod system;
|
||||||
pub use crate::system::{Trs80Options, build_trs80};
|
pub use crate::system::{Trs80Options, build_trs80};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
pub mod model1;
|
pub mod model1;
|
||||||
|
#[rustfmt::skip]
|
||||||
pub mod keymap;
|
pub mod keymap;
|
||||||
|
#[rustfmt::skip]
|
||||||
pub mod charset;
|
pub mod charset;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::{Instant, Duration};
|
use femtos::{Instant, Duration};
|
||||||
|
|
||||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
||||||
|
@ -8,12 +7,12 @@ use super::keymap;
|
||||||
use super::charset::CharacterGenerator;
|
use super::charset::CharacterGenerator;
|
||||||
|
|
||||||
|
|
||||||
const DEV_NAME: &str = "model1";
|
const DEV_NAME: &str = "model1";
|
||||||
const SCREEN_SIZE: (u32, u32) = (384, 128);
|
const SCREEN_SIZE: (u32, u32) = (384, 128);
|
||||||
|
|
||||||
|
|
||||||
pub struct Model1Keyboard {
|
pub struct Model1Keyboard {
|
||||||
receiver: EventReceiver<KeyEvent>,
|
receiver: EventReceiver<KeyEvent>,
|
||||||
keyboard_mem: [u8; 8],
|
keyboard_mem: [u8; 8],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,14 +40,30 @@ impl Addressable for Model1Keyboard {
|
||||||
if (0x20..=0xA0).contains(&addr) {
|
if (0x20..=0xA0).contains(&addr) {
|
||||||
let offset = addr - 0x20;
|
let offset = addr - 0x20;
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
if (offset & 0x01) != 0 { data[0] |= self.keyboard_mem[0]; }
|
if (offset & 0x01) != 0 {
|
||||||
if (offset & 0x02) != 0 { data[0] |= self.keyboard_mem[1]; }
|
data[0] |= self.keyboard_mem[0];
|
||||||
if (offset & 0x04) != 0 { data[0] |= self.keyboard_mem[2]; }
|
}
|
||||||
if (offset & 0x08) != 0 { data[0] |= self.keyboard_mem[3]; }
|
if (offset & 0x02) != 0 {
|
||||||
if (offset & 0x10) != 0 { data[0] |= self.keyboard_mem[4]; }
|
data[0] |= self.keyboard_mem[1];
|
||||||
if (offset & 0x20) != 0 { data[0] |= self.keyboard_mem[5]; }
|
}
|
||||||
if (offset & 0x40) != 0 { data[0] |= self.keyboard_mem[6]; }
|
if (offset & 0x04) != 0 {
|
||||||
if (offset & 0x80) != 0 { data[0] |= self.keyboard_mem[7]; }
|
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);
|
//info!("{}: read from keyboard {:x} of {:?}", DEV_NAME, addr, data);
|
||||||
} else {
|
} else {
|
||||||
log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
|
log::warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr);
|
||||||
|
@ -147,4 +162,3 @@ impl Transmutable for Model1Video {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use femtos::Frequency;
|
use femtos::Frequency;
|
||||||
|
|
||||||
use moa_core::{System, Error, MemoryBlock, Device};
|
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
|
// TODO the ioport needs to be hooked up
|
||||||
let cpu = Z80::from_type(Z80Type::Z80, options.frequency, system.bus.clone(), 0, None);
|
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))?;
|
system.add_interruptable_device("cpu", Device::new(cpu))?;
|
||||||
|
|
||||||
Ok(system)
|
Ok(system)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
const DEFAULT_HARTE_TESTS: &str = "tests/ProcessorTests/680x0/68000/v1/";
|
const DEFAULT_HARTE_TESTS: &str = "tests/ProcessorTests/680x0/68000/v1/";
|
||||||
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
@ -95,7 +94,7 @@ struct TestCase {
|
||||||
initial_state: TestState,
|
initial_state: TestState,
|
||||||
#[serde(rename(deserialize = "final"))]
|
#[serde(rename(deserialize = "final"))]
|
||||||
final_state: TestState,
|
final_state: TestState,
|
||||||
length: usize
|
length: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestState {
|
impl TestState {
|
||||||
|
@ -151,7 +150,9 @@ fn init_execute_test(cputype: M68kType, state: &TestState) -> Result<(M68k, Memo
|
||||||
// Insert basic initialization
|
// Insert basic initialization
|
||||||
let len = 0x100_0000;
|
let len = 0x100_0000;
|
||||||
let mut data = Vec::with_capacity(len);
|
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 memory = MemoryBlock::<u32, Instant>::from(data);
|
||||||
|
|
||||||
let mut cpu = M68k::from_type(cputype, Frequency::from_mhz(10));
|
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>
|
fn assert_value<T>(actual: T, expected: T, message: &str) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: PartialEq + Debug + UpperHex
|
T: PartialEq + Debug + UpperHex,
|
||||||
{
|
{
|
||||||
if actual == expected {
|
if actual == expected {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -197,13 +198,15 @@ fn load_state(cpu: &mut M68k, memory: &mut MemoryBlock<u32, Instant>, initial: &
|
||||||
|
|
||||||
// Load instructions into memory
|
// Load instructions into memory
|
||||||
for (i, ins) in initial.prefetch.iter().enumerate() {
|
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)))?;
|
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load data bytes into memory
|
// Load data bytes into memory
|
||||||
for (addr, byte) in initial.ram.iter() {
|
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)))?;
|
.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
|
// Load instructions into memory
|
||||||
for (i, ins) in expected.prefetch.iter().enumerate() {
|
for (i, ins) in expected.prefetch.iter().enumerate() {
|
||||||
let addr = expected.pc + (i as u32 * 2);
|
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)))?;
|
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
|
||||||
assert_value(actual, *ins, &format!("prefetch at {:x}", addr))?;
|
assert_value(actual, *ins, &format!("prefetch at {:x}", addr))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load data bytes into memory
|
// Load data bytes into memory
|
||||||
for (addr, byte) in expected.ram.iter() {
|
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)))?;
|
.map_err(|err| Error::Bus(format!("{:?}", err)))?;
|
||||||
assert_value(actual, *byte, &format!("ram at {:x}", addr))?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_cpu_and_assert(cpu: &mut M68k, memory: &mut MemoryBlock<u32, Instant>, case: &TestCase, test_timing: bool) -> Result<(), Error> {
|
fn step_cpu_and_assert(
|
||||||
let clock_elapsed = cpu.step(Instant::START, memory)
|
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)))?;
|
.map_err(|err| Error::Step(format!("{:?}", err)))?;
|
||||||
let cycles = clock_elapsed.as_duration() / cpu.info.frequency.period_duration();
|
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();
|
initial_cpu.dump_state(&mut writer).unwrap();
|
||||||
cpu.dump_state(&mut writer).unwrap();
|
cpu.dump_state(&mut writer).unwrap();
|
||||||
}
|
}
|
||||||
writeln!(writer, "FAILED: {:?}", err).unwrap();
|
writeln!(writer, "FAILED: {:?}", err).unwrap();
|
||||||
println!("{}", writer);
|
println!("{}", writer);
|
||||||
}
|
}
|
||||||
Err(err)
|
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
|
// Only run the test if it's selected by the exceptions flag
|
||||||
if case.is_extended_exception_case() && args.exceptions == Selection::ExcludeAddr
|
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::Exclude
|
||||||
|| !case.is_exception_case() && args.exceptions == Selection::Only {
|
|| !case.is_exception_case() && args.exceptions == Selection::Only
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +346,7 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) {
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
failed += 1;
|
failed += 1;
|
||||||
if !args.quiet {
|
if !args.quiet {
|
||||||
println!("FAILED: {:?}", err);
|
println!("FAILED: {:?}", err);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
passed += 1
|
passed += 1
|
||||||
|
@ -401,7 +413,11 @@ fn run_all_tests(args: &Args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
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);
|
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/";
|
const DEFAULT_RAD_TESTS: &str = "tests/jsmoo/misc/tests/GeneratedTests/z80/v1/";
|
||||||
|
|
||||||
use std::rc::Rc;
|
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>
|
fn assert_value<T>(actual: T, expected: T, message: &str) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: PartialEq + Debug + UpperHex
|
T: PartialEq + Debug + UpperHex,
|
||||||
{
|
{
|
||||||
if actual == expected {
|
if actual == expected {
|
||||||
Ok(())
|
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[0] = initial.b;
|
||||||
cpu.state.reg[1] = initial.c;
|
cpu.state.reg[1] = initial.c;
|
||||||
cpu.state.reg[2] = initial.d;
|
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;
|
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[0], expected.b, "b")?;
|
||||||
assert_value(cpu.state.reg[1], expected.c, "c")?;
|
assert_value(cpu.state.reg[1], expected.c, "c")?;
|
||||||
assert_value(cpu.state.reg[2], expected.d, "d")?;
|
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(())
|
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)?;
|
let clock_elapsed = cpu.step(system)?;
|
||||||
|
|
||||||
assert_state(cpu, system, io_bus, &case.final_state, args.check_extra_flags, &case.ports)?;
|
assert_state(cpu, system, io_bus, &case.final_state, args.check_extra_flags, &case.ports)?;
|
||||||
if args.check_timings {
|
if args.check_timings {
|
||||||
let cycles = clock_elapsed / cpu.frequency.period_duration();
|
let cycles = clock_elapsed / cpu.frequency.period_duration();
|
||||||
if cycles != case.cycles.len() as Address {
|
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);
|
initial_cpu.dump_state(system.clock);
|
||||||
cpu.dump_state(system.clock);
|
cpu.dump_state(system.clock);
|
||||||
}
|
}
|
||||||
println!("FAILED: {:?}", err);
|
println!("FAILED: {:?}", err);
|
||||||
}
|
}
|
||||||
Err(err)
|
Err(err)
|
||||||
},
|
},
|
||||||
|
@ -353,7 +375,7 @@ fn test_json_file(path: PathBuf, args: &Args) -> (usize, usize, String) {
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
failed += 1;
|
failed += 1;
|
||||||
if !args.quiet {
|
if !args.quiet {
|
||||||
println!("FAILED: {:?}", err);
|
println!("FAILED: {:?}", err);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
passed += 1
|
passed += 1
|
||||||
|
@ -426,7 +448,12 @@ fn run_all_tests(args: &Args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
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);
|
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()]);
|
opcodes.extend(vec![0; 3 - opcodes.len()]);
|
||||||
|
|
||||||
match (opcodes[0], opcodes[1]) {
|
match (opcodes[0], opcodes[1]) {
|
||||||
(0xCB, op) => {
|
(0xCB, op) => (0x30..=0x37).contains(&op),
|
||||||
(0x30..=0x37).contains(&op)
|
(0xDD, 0xCB) | (0xFD, 0xCB) => !(opcodes[2] & 0x07 == 0x06 && opcodes[2] != 0x36),
|
||||||
},
|
(0xDD, op) | (0xFD, op) => {
|
||||||
(0xDD, 0xCB) |
|
|
||||||
(0xFD, 0xCB) => {
|
|
||||||
!(opcodes[2] & 0x07 == 0x06 && opcodes[2] != 0x36)
|
|
||||||
},
|
|
||||||
(0xDD, op) |
|
|
||||||
(0xFD, op) => {
|
|
||||||
let upper = op & 0xF0;
|
let upper = op & 0xF0;
|
||||||
let lower = op & 0x0F;
|
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) &&
|
!(lower == 0x0E && (0x40..=0xB0).contains(&upper)
|
||||||
!((0x21..=0x23).contains(&op) || (0x34..=0x36).contains(&op) || (0x29..=0x2B).contains(&op)) &&
|
|| (0x70..=0x77).contains(&op) && op != 0x76
|
||||||
!(lower == 0x09 && upper <= 0x30) &&
|
|| op != 0x76 && (0x70..=0x77).contains(&op)
|
||||||
!(op == 0xE1 || op == 0xE3 || op == 0xE5 || op == 0xE9 || op == 0xF9)
|
|| 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) => {
|
(0xED, op) => {
|
||||||
// NOTE this assumes the tests don't have the missing instructions, or the Z180 instructions
|
// NOTE this assumes the tests don't have the missing instructions, or the Z180 instructions
|
||||||
// so it only checks for the undocumented ones
|
// so it only checks for the undocumented ones
|
||||||
op == 0x63 || op == 0x6B || op == 0x70 || op == 0x71
|
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
|
* 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?
|
* 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
|
||||||
* implement the inspect and debug traits
|
* figure out how to do interrupts, and add them to emulator-hal, implement them in m68k
|
||||||
* move the interrupt controller logic to the step() function only, and have a customish interrupt interface into the sim
|
* 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
|
* 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
|
* 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
|
* 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…
Reference in New Issue