409 lines
8.0 KiB
Rust
409 lines
8.0 KiB
Rust
#![allow(clippy::unreadable_literal)]
|
|
|
|
use crate::bus::{AccessCode, Bus};
|
|
use crate::cpu::Cpu;
|
|
use crate::err::BusError;
|
|
use crate::rom_hi::HI_ROM;
|
|
use crate::rom_lo::LO_ROM;
|
|
|
|
use libc::*;
|
|
use std::sync::Mutex;
|
|
use std::{ffi::CStr, ptr};
|
|
|
|
lazy_static! {
|
|
static ref DMD: Mutex<Dmd> = Mutex::new(Dmd::new());
|
|
}
|
|
|
|
// Return vlaues for the C library
|
|
const SUCCESS: c_int = 0;
|
|
const ERROR: c_int = 1;
|
|
const BUSY: c_int = 2;
|
|
|
|
pub struct Dmd {
|
|
cpu: Cpu,
|
|
bus: Bus,
|
|
}
|
|
|
|
impl Default for Dmd {
|
|
fn default() -> Self {
|
|
Dmd::new()
|
|
}
|
|
}
|
|
|
|
impl Dmd {
|
|
pub fn new() -> Dmd {
|
|
let cpu = Cpu::new();
|
|
let bus = Bus::new(0x100000);
|
|
Dmd {
|
|
cpu,
|
|
bus,
|
|
}
|
|
}
|
|
|
|
pub fn trace_on(&mut self, name: &str) -> std::io::Result<()> {
|
|
self.bus.trace_on(name)
|
|
}
|
|
|
|
pub fn trace_off(&mut self) {
|
|
self.bus.trace_off()
|
|
}
|
|
|
|
pub fn reset(&mut self) -> Result<(), BusError> {
|
|
self.bus.load(0, &LO_ROM)?;
|
|
self.bus.load(0x10000, &HI_ROM)?;
|
|
self.cpu.reset(&mut self.bus)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn video_ram(&self) -> &[u8] {
|
|
self.bus.video_ram()
|
|
}
|
|
|
|
pub fn get_pc(&self) -> u32 {
|
|
self.cpu.get_pc()
|
|
}
|
|
|
|
pub fn get_ap(&self) -> u32 {
|
|
self.cpu.get_ap()
|
|
}
|
|
|
|
pub fn get_psw(&self) -> u32 {
|
|
self.cpu.get_psw()
|
|
}
|
|
|
|
pub fn get_register(&self, reg: u8) -> u32 {
|
|
self.cpu.r[(reg & 0xf) as usize]
|
|
}
|
|
|
|
pub fn read_word(&mut self, addr: usize) -> Option<u32> {
|
|
match self.bus.read_word(addr, AccessCode::AddressFetch) {
|
|
Ok(d) => Some(d),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn read_byte(&mut self, addr: usize) -> Option<u8> {
|
|
match self.bus.read_byte(addr, AccessCode::AddressFetch) {
|
|
Ok(d) => Some(d),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn step(&mut self) {
|
|
self.cpu.step(&mut self.bus);
|
|
}
|
|
|
|
pub fn run(&mut self, count: usize) {
|
|
for _ in 0..count {
|
|
self.cpu.step(&mut self.bus);
|
|
}
|
|
}
|
|
|
|
pub fn rs232_tx_poll(&mut self) -> Option<u8> {
|
|
self.bus.rs232_tx_poll()
|
|
}
|
|
|
|
pub fn kb_tx_poll(&mut self) -> Option<u8> {
|
|
self.bus.kb_tx_poll()
|
|
}
|
|
|
|
pub fn rx_char(&mut self, character: u8) {
|
|
self.bus.rx_char(character);
|
|
}
|
|
|
|
pub fn rx_keyboard(&mut self, keycode: u8) {
|
|
self.bus.rx_keyboard(keycode);
|
|
}
|
|
|
|
pub fn mouse_move(&mut self, x: u16, y: u16) {
|
|
self.bus.mouse_move(x, y);
|
|
}
|
|
|
|
pub fn mouse_down(&mut self, button: u8) {
|
|
self.bus.mouse_down(button);
|
|
}
|
|
|
|
pub fn mouse_up(&mut self, button: u8) {
|
|
self.bus.mouse_up(button);
|
|
}
|
|
|
|
pub fn duart_output(&self) -> u8 {
|
|
self.bus.duart_output()
|
|
}
|
|
|
|
pub fn set_nvram(&mut self, nvram: &[u8]) {
|
|
self.bus.set_nvram(nvram);
|
|
}
|
|
|
|
pub fn get_nvram(&self) -> &[u8] {
|
|
self.bus.get_nvram()
|
|
}
|
|
}
|
|
|
|
//
|
|
// Provide a C interface
|
|
//
|
|
|
|
#[no_mangle]
|
|
fn dmd_reset() -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => match dmd.reset() {
|
|
Ok(()) => SUCCESS,
|
|
Err(_) => ERROR,
|
|
},
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_video_ram() -> *const u8 {
|
|
match DMD.lock() {
|
|
Ok(dmd) => dmd.video_ram().as_ptr(),
|
|
Err(_) => ptr::null(),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_step() -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => {
|
|
dmd.step();
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// Uses a raw pointer.
|
|
///
|
|
#[no_mangle]
|
|
pub unsafe fn dmd_trace_on(file_name: *const c_char) -> c_int {
|
|
let file_name = CStr::from_ptr(file_name);
|
|
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => match file_name.to_str() {
|
|
Ok(file_name) => match dmd.trace_on(file_name) {
|
|
Ok(()) => SUCCESS,
|
|
Err(_) => ERROR,
|
|
},
|
|
Err(_) => ERROR,
|
|
},
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_step_loop(steps: usize) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => {
|
|
dmd.run(steps);
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_get_pc(pc: &mut u32) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(dmd) => {
|
|
*pc = dmd.get_pc();
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_get_register(reg: u8, val: &mut u32) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(dmd) => {
|
|
*val = dmd.get_register(reg);
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_read_word(addr: u32, val: &mut u32) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => match dmd.read_word(addr as usize) {
|
|
Some(word) => {
|
|
*val = word;
|
|
SUCCESS
|
|
}
|
|
None => ERROR,
|
|
},
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_read_byte(addr: u32, val: &mut u8) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => match dmd.read_byte(addr as usize) {
|
|
Some(byte) => {
|
|
*val = byte;
|
|
SUCCESS
|
|
}
|
|
None => ERROR,
|
|
},
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_get_duart_output_port(oport: &mut u8) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(dmd) => {
|
|
*oport = dmd.duart_output();
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_rx_char(c: u8) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => {
|
|
dmd.rx_char(c as u8);
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_rx_keyboard(c: u8) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => {
|
|
dmd.rx_keyboard(c);
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_mouse_move(x: u16, y: u16) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => {
|
|
dmd.mouse_move(x, y);
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_mouse_down(button: u8) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => {
|
|
dmd.mouse_down(button);
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_mouse_up(button: u8) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => {
|
|
dmd.mouse_up(button);
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_rs232_tx_poll(tx_char: &mut u8) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => match dmd.rs232_tx_poll() {
|
|
Some(c) => {
|
|
*tx_char = c;
|
|
SUCCESS
|
|
}
|
|
None => BUSY,
|
|
},
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_kb_tx_poll(tx_char: &mut u8) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => match dmd.kb_tx_poll() {
|
|
Some(c) => {
|
|
*tx_char = c;
|
|
SUCCESS
|
|
}
|
|
None => BUSY,
|
|
},
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_set_nvram(nvram: &[u8; 8192]) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(mut dmd) => {
|
|
dmd.set_nvram(nvram);
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
fn dmd_get_nvram(nvram: &mut [u8; 8192]) -> c_int {
|
|
match DMD.lock() {
|
|
Ok(dmd) => {
|
|
nvram.clone_from_slice(dmd.get_nvram());
|
|
SUCCESS
|
|
}
|
|
Err(_) => ERROR,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::dmd::Dmd;
|
|
|
|
#[test]
|
|
fn creates_dmd() {
|
|
let mut dmd = Dmd::new();
|
|
dmd.reset().unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn loads_and_reads_nvram() {
|
|
let mut dmd = Dmd::new();
|
|
|
|
let mut to_load: [u8; 8192] = [0; 8192];
|
|
to_load[0] = 0x5a;
|
|
to_load[0xfff] = 0xa5;
|
|
to_load[0x1fff] = 0xff;
|
|
|
|
let old_nvram = dmd.get_nvram();
|
|
|
|
assert_eq!(0, old_nvram[0]);
|
|
assert_eq!(0, old_nvram[0xfff]);
|
|
assert_eq!(0, old_nvram[0x1fff]);
|
|
|
|
dmd.set_nvram(&to_load);
|
|
|
|
let new_nvram = dmd.get_nvram();
|
|
|
|
assert_eq!(0x5a, new_nvram[0]);
|
|
assert_eq!(0xa5, new_nvram[0xfff]);
|
|
assert_eq!(0xff, new_nvram[0x1fff]);
|
|
}
|
|
}
|