Create a C interface

This commit is contained in:
Seth Morabito 2018-12-21 17:00:43 -08:00
parent 17b81b71da
commit 20b07f0079
4 changed files with 222 additions and 72 deletions

View File

@ -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" }

View File

@ -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.

View File

@ -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]);
}
}
}

View File

@ -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;