Create a C interface
This commit is contained in:
parent
17b81b71da
commit
20b07f0079
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "dmd_core"
|
||||
description = "AT&T / Teletype DMD 5620 Terminal Emulator - Core Library"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
authors = ["Seth Morabito <web@loomcom.com>"]
|
||||
homepage = "https://github.com/sethm/dmd_core"
|
||||
repository = "https://github.com/sethm/dmd_core"
|
||||
|
@ -12,6 +12,10 @@ categories = ["simulation"]
|
|||
|
||||
[dependencies]
|
||||
lazy_static = "^1.2.0"
|
||||
libc = "^0.2.45"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib", "rlib"]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "https://github.com/sethm/dmd_core", branch = "master" }
|
||||
|
|
|
@ -27,6 +27,10 @@ however.
|
|||
|
||||
## Changelog
|
||||
|
||||
0.5.0: Non-breaking but major change. `dmd_core` now presents a
|
||||
C compatible API to make interacting with C and C++ code
|
||||
easier, without needed to write a full stub library.
|
||||
|
||||
0.4.0: Breaking change. TX to the keyboard from the terminal is
|
||||
now supported, so that clients can use it to detect when
|
||||
a bell (^G) request has been sent.
|
||||
|
|
281
src/dmd.rs
281
src/dmd.rs
|
@ -1,19 +1,28 @@
|
|||
use crate::bus::{Bus, AccessCode};
|
||||
use crate::cpu::Cpu;
|
||||
use crate::err::BusError;
|
||||
use crate::err::CpuError;
|
||||
use crate::rom_hi::HI_ROM;
|
||||
use crate::rom_lo::LO_ROM;
|
||||
|
||||
use libc::*;
|
||||
use std::ptr;
|
||||
use std::sync::Mutex;
|
||||
|
||||
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 Dmd {
|
||||
///
|
||||
/// Construct a new DMD Terminal
|
||||
///
|
||||
pub fn new() -> Dmd {
|
||||
let cpu = Cpu::new();
|
||||
let bus = Bus::new(0x100000);
|
||||
|
@ -23,9 +32,6 @@ impl Dmd {
|
|||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Reset the terminal's CPU. This is equivalent to a hard power reset.
|
||||
///
|
||||
pub fn reset(&mut self) -> Result<(), BusError> {
|
||||
self.bus.load(0, &LO_ROM)?;
|
||||
self.bus.load(0x10000, &HI_ROM)?;
|
||||
|
@ -34,44 +40,26 @@ impl Dmd {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Return a view of the terminal's Video Memory
|
||||
///
|
||||
pub fn video_ram(&self) -> Result<&[u8], BusError> {
|
||||
self.bus.video_ram()
|
||||
}
|
||||
|
||||
///
|
||||
/// Return the contents of the CPU's Program Counter.
|
||||
///
|
||||
pub fn get_pc(&self) -> u32 {
|
||||
self.cpu.get_pc()
|
||||
}
|
||||
|
||||
///
|
||||
/// Return the contents of the CPU's Argument Pointer.
|
||||
///
|
||||
pub fn get_ap(&self) -> u32 {
|
||||
self.cpu.get_ap()
|
||||
}
|
||||
|
||||
///
|
||||
/// Return the contents of the CPU's Processor Status Word.
|
||||
///
|
||||
pub fn get_psw(&self) -> u32 {
|
||||
self.cpu.get_psw()
|
||||
}
|
||||
|
||||
///
|
||||
/// Return the contents of a CPU register (0-15)
|
||||
///
|
||||
pub fn get_register(&self, reg: u8) -> u32 {
|
||||
self.cpu.r[(reg & 0xf) as usize]
|
||||
}
|
||||
|
||||
///
|
||||
/// Read a word from the terminal's memory. NB: Address must be word aligned.
|
||||
///
|
||||
pub fn read(&mut self, addr: usize) -> Option<u32> {
|
||||
match self.bus.read_word(addr, AccessCode::AddressFetch) {
|
||||
Ok(d) => Some(d),
|
||||
|
@ -79,101 +67,254 @@ impl Dmd {
|
|||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Step the terminal's CPU one time, allowing the CPU's internal error and
|
||||
/// exception handling to take care of all error / exception types.
|
||||
///
|
||||
pub fn step(&mut self) {
|
||||
self.cpu.step(&mut self.bus);
|
||||
}
|
||||
|
||||
///
|
||||
/// Free-run the CPU for a given number of steps.
|
||||
///
|
||||
pub fn run(&mut self, count: usize) {
|
||||
for _ in 0..count {
|
||||
self.cpu.step(&mut self.bus);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Step the terminal's CPU one time, returning any error that may have occured.
|
||||
/// Useful for debugging.
|
||||
///
|
||||
pub fn step_with_error(&mut self) -> Result<(), CpuError> {
|
||||
self.cpu.step_with_error(&mut self.bus)
|
||||
}
|
||||
|
||||
///
|
||||
/// Poll for a character to transmit from the terminal to the host.
|
||||
///
|
||||
pub fn rs232_tx_poll(&mut self) -> Option<u8> {
|
||||
self.bus.rs232_tx_poll()
|
||||
}
|
||||
|
||||
///
|
||||
/// Poll for a character to transmit from the terminal to the keyboard.
|
||||
///
|
||||
pub fn kb_tx_poll(&mut self) -> Option<u8> {
|
||||
self.bus.kb_tx_poll()
|
||||
}
|
||||
|
||||
///
|
||||
/// Receive a character from the host to the terminal.
|
||||
///
|
||||
pub fn rx_char(&mut self, character: u8) {
|
||||
self.bus.rx_char(character);
|
||||
}
|
||||
|
||||
///
|
||||
/// Receive a character from the keyboard to the terminal.
|
||||
///
|
||||
pub fn rx_keyboard(&mut self, keycode: u8) {
|
||||
self.bus.rx_keyboard(keycode);
|
||||
}
|
||||
|
||||
///
|
||||
/// Register a new mouse position with the terminal.
|
||||
///
|
||||
pub fn mouse_move(&mut self, x: u16, y: u16) {
|
||||
self.bus.mouse_move(x, y);
|
||||
}
|
||||
|
||||
///
|
||||
/// Register a "mouse down" event with the terminal.
|
||||
pub fn mouse_down(&mut self, button: u8) {
|
||||
self.bus.mouse_down(button);
|
||||
}
|
||||
|
||||
///
|
||||
/// Register a "mouse up" event with the terminal.
|
||||
///
|
||||
pub fn mouse_up(&mut self, button: u8) {
|
||||
self.bus.mouse_up(button);
|
||||
}
|
||||
|
||||
///
|
||||
/// Return the current state of the DUART's Output Port
|
||||
///
|
||||
pub fn duart_output(&self) -> u8 {
|
||||
self.bus.duart_output()
|
||||
}
|
||||
|
||||
///
|
||||
/// Load NVRAM into the emulator
|
||||
///
|
||||
pub fn set_nvram(&mut self, nvram: &[u8; 8192]) {
|
||||
self.bus.set_nvram(nvram);
|
||||
}
|
||||
|
||||
///
|
||||
/// Get NVRAM from the emulator
|
||||
///
|
||||
pub fn get_nvram(&self) -> [u8; 8192] {
|
||||
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) => {
|
||||
match dmd.video_ram() {
|
||||
Ok(video_ram) => video_ram.as_ptr(),
|
||||
Err(_) => ptr::null()
|
||||
}
|
||||
}
|
||||
Err(_) => ptr::null()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn dmd_step() -> c_int {
|
||||
match DMD.lock() {
|
||||
Ok(mut dmd) => {
|
||||
dmd.step();
|
||||
SUCCESS
|
||||
}
|
||||
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 uint32_t) -> c_int {
|
||||
match DMD.lock() {
|
||||
Ok(dmd) => {
|
||||
*pc = dmd.get_pc();
|
||||
SUCCESS
|
||||
},
|
||||
Err(_) => ERROR
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn dmd_get_register(reg: uint8_t, val: &mut uint32_t) -> c_int {
|
||||
match DMD.lock() {
|
||||
Ok(dmd) => {
|
||||
*val = dmd.get_register(reg);
|
||||
SUCCESS
|
||||
},
|
||||
Err(_) => ERROR
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn dmd_get_duart_output_port(oport: &mut uint8_t) -> c_int {
|
||||
match DMD.lock() {
|
||||
Ok(dmd) => {
|
||||
*oport = dmd.duart_output();
|
||||
SUCCESS
|
||||
}
|
||||
Err(_) => ERROR
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn dmd_rx_char(c: uint8_t) -> c_int {
|
||||
match DMD.lock() {
|
||||
Ok(mut dmd) => {
|
||||
dmd.rx_char(c as u8);
|
||||
SUCCESS
|
||||
}
|
||||
Err(_) => ERROR
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn dmd_rx_keyboard(c: uint8_t) -> c_int {
|
||||
match DMD.lock() {
|
||||
Ok(mut dmd) => {
|
||||
dmd.rx_keyboard(c);
|
||||
SUCCESS
|
||||
}
|
||||
Err(_) => ERROR
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn dmd_mouse_move(x: uint16_t, y: uint16_t) -> c_int {
|
||||
match DMD.lock() {
|
||||
Ok(mut dmd) => {
|
||||
dmd.mouse_move(x, y);
|
||||
SUCCESS
|
||||
}
|
||||
Err(_) => ERROR
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn dmd_mouse_down(button: uint8_t) -> c_int {
|
||||
match DMD.lock() {
|
||||
Ok(mut dmd) => {
|
||||
dmd.mouse_down(button);
|
||||
SUCCESS
|
||||
}
|
||||
Err(_) => ERROR
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn dmd_mouse_up(button: uint8_t) -> 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 uint8_t) -> 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 uint8_t) -> 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) => {
|
||||
let buf = dmd.get_nvram();
|
||||
for i in 0..8192 {
|
||||
nvram[i] = buf[i];
|
||||
}
|
||||
SUCCESS
|
||||
}
|
||||
Err(_) => ERROR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::dmd::Dmd;
|
||||
|
@ -207,4 +348,4 @@ mod tests {
|
|||
assert_eq!(0xa5, new_nvram[0xfff]);
|
||||
assert_eq!(0xff, new_nvram[0x1fff]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,4 +10,5 @@ pub mod rom_hi;
|
|||
pub mod rom_lo;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate lazy_static;
|
||||
extern crate libc;
|
Loading…
Reference in New Issue