use crate::LoadAddress;
use super::io;
use anyhow::Result;
use hex::FromHex;
use log::debug;
use serialport::SerialPort;
use std::thread;
use std::time::Duration;
use std::io::{Read, Write};
const DELAY_WRITE: Duration = Duration::from_millis(20);
const DELAY_KEYPRESS: Duration = DELAY_WRITE;
pub const DEFAULT_BAUD_RATE: u32 = 2000000;
pub fn stop_cpu(port: &mut dyn Write) -> Result<()> {
port.write_all("t1\r".as_bytes())?;
port.flush()?;
thread::sleep(DELAY_WRITE);
Ok(())
}
pub fn start_cpu(port: &mut dyn Write) -> Result<()> {
port.write_all("t0\r".as_bytes())?;
port.flush()?;
thread::sleep(DELAY_WRITE);
Ok(())
}
pub fn is_c65_mode<T: Read + Write>(port: &mut T) -> Result<bool> {
let byte = peek(port, 0xffd3030)?;
Ok(byte == 0x64)
}
fn print_ports() {
debug!("Detecting serial ports");
serialport::available_ports()
.expect("No serial ports found!")
.iter()
.for_each(|port| println!("{}", port.port_name));
println!();
}
pub fn open_port(name: &str, baud_rate: u32) -> Result<Box<dyn SerialPort>> {
debug!("Opening serial port {}", name);
match serialport::new(name, baud_rate)
.timeout(Duration::from_millis(10))
.open()
{
Ok(port) => Ok(port),
Err(err) => {
eprintln!("Invalid serial port, try one of these?\n");
print_ports();
Err(err.into())
}
}
}
pub fn reset(port: &mut dyn Write) -> Result<()> {
debug!("Sending RESET signal");
port.write_all("!\n".as_bytes())?;
thread::sleep(Duration::from_secs(4));
Ok(())
}
pub fn go64<T: Read + Write>(port: &mut T) -> Result<()> {
debug!("Sending GO64");
if is_c65_mode(port)? {
type_text(port, "go64\ry\r")?;
thread::sleep(Duration::from_secs(1));
}
Ok(())
}
pub fn go65<T: Read + Write>(port: &mut T) -> Result<()> {
if !is_c65_mode(port)? {
reset(port)?;
}
Ok(())
}
fn type_key(port: &mut dyn Write, mut key: char) -> Result<()> {
let mut c1: u8 = 0x7f;
let mut c2 = match key {
'!' => {
key = '1';
0x0f
}
'\"' => {
key = '2';
0x0f
}
'#' => {
key = '3';
0x0f
}
'$' => {
key = '4';
0x0f
}
'%' => {
key = '5';
0x0f
}
'(' => {
key = '8';
0x0f
}
')' => {
key = '9';
0x0f
}
'?' => {
key = '/';
0x0f
}
'<' => {
key = ',';
0x0f
}
'>' => {
key = '.';
0x0f
}
_ => 0x7f,
};
match key as u8 {
0x14 => c1 = 0x00, 0x0d => c1 = 0x01, 0x1d => c1 = 0x02, 0xf7 => c1 = 0x03,
0x9d => {
c1 = 0x02;
c2 = 0x0f;
}
0x91 => {
c1 = 0x07;
c2 = 0x0f;
}
0xf1 => c2 = 0x04, 0xf3 => c1 = 0x05, 0xf5 => c1 = 0x06, 0x11 => c1 = 0x07, b'3' => c1 = 0x08,
b'w' => c1 = 0x09,
b'a' => c1 = 0x0a,
b'4' => c1 = 0x0b,
b'z' => c1 = 0x0c,
b's' => c1 = 0x0d,
b'e' => c1 = 0x0e,
b'5' => c1 = 0x10,
b'r' => c1 = 0x11,
b'd' => c1 = 0x12,
b'6' => c1 = 0x13,
b'c' => c1 = 0x14,
b'f' => c1 = 0x15,
b't' => c1 = 0x16,
b'x' => c1 = 0x17,
b'7' => c1 = 0x18,
b'y' => c1 = 0x19,
b'g' => c1 = 0x1a,
b'8' => c1 = 0x1b,
b'b' => c1 = 0x1c,
b'h' => c1 = 0x1d,
b'u' => c1 = 0x1e,
b'v' => c1 = 0x1f,
b'9' => c1 = 0x20,
b'i' => c1 = 0x21,
b'j' => c1 = 0x22,
b'0' => c1 = 0x23,
b'm' => c1 = 0x24,
b'k' => c1 = 0x25,
b'o' => c1 = 0x26,
b'n' => c1 = 0x27,
b'+' => c1 = 0x28,
b'p' => c1 = 0x29,
b'l' => c1 = 0x2a,
b'-' => c1 = 0x2b,
b'.' => c1 = 0x2c,
b':' => c1 = 0x2d,
b'@' => c1 = 0x2e,
b',' => c1 = 0x2f,
b'}' => c1 = 0x30,
b'*' => c1 = 0x31,
b';' => c1 = 0x32,
0x13 => c1 = 0x33,
b'=' => c1 = 0x35,
b'/' => c1 = 0x37,
b'1' => c1 = 0x38,
b'_' => c1 = 0x39,
b'2' => c1 = 0x3b,
b' ' => c1 = 0x3c,
b'q' => c1 = 0x3e,
0x03 => c1 = 0x3f, 0x0c => c1 = 0x3f,
_ => c1 = 0x7f,
}
port.write_all(format!("sffd3615 {:02x} {:02x}\n", c1, c2).as_bytes())?;
thread::sleep(DELAY_KEYPRESS);
Ok(())
}
fn stop_typing(port: &mut dyn Write) -> Result<()> {
port.write_all("sffd3615 7f 7f 7f \n".as_bytes())?;
thread::sleep(DELAY_WRITE);
Ok(())
}
pub fn type_text(port: &mut dyn Write, text: &str) -> Result<()> {
debug!("Typing text");
thread::sleep(DELAY_KEYPRESS);
text.replace("\\r", "\r")
.replace("\\n", "\r")
.chars()
.for_each(|key| type_key(port, key).unwrap_or(()));
stop_typing(port)?;
Ok(())
}
#[allow(dead_code)]
fn mega65_info<T: Read + Write>(port: &mut T) -> Result<()> {
debug!("Requesting serial monitor info");
port.write_all("h\n".as_bytes())?;
thread::sleep(DELAY_WRITE);
let mut buffer = Vec::new();
buffer.resize(65, 0);
port.read_exact(&mut buffer)?;
let lines = buffer.split(|i| *i == b'\n');
for line in lines {
for i in line {
print!("{}", *i as char);
}
}
println!();
Ok(())
}
pub fn read_memory<T: Read + Write>(port: &mut T, address: u32, length: usize) -> Result<Vec<u8>> {
debug!("Loading {} bytes from 0x{:x}", length, address);
flush_monitor(port)?;
stop_cpu(port)?;
port.write_all(format!("m{:07x}\r", address).as_bytes())?;
thread::sleep(DELAY_WRITE);
let mut buffer = Vec::new();
let mut bytes = Vec::new();
bytes.reserve(length);
buffer.resize(27, 0);
port.read_exact(&mut buffer)?;
while bytes.len() < length {
buffer.resize(16 * 2, 0);
port.read_exact(&mut buffer)?;
let mut sixteen_bytes: Vec<u8> = Vec::from_hex(&buffer)?;
bytes.append(&mut sixteen_bytes);
port.write_all("m\r".as_bytes())?;
thread::sleep(DELAY_WRITE);
buffer.resize(18, 0);
port.read_exact(&mut buffer)?;
}
bytes.truncate(length);
start_cpu(port)?;
Ok(bytes)
}
pub fn peek<T: Read + Write>(port: &mut T, address: u32) -> Result<u8> {
let bytes = read_memory(port, address, 1)?;
Ok(bytes[0])
}
pub fn flush_monitor<T: Read + Write>(port: &mut T) -> Result<()> {
port.write_all(&[0x15, b'#', b'\r'])?;
let mut byte = [0u8];
loop {
thread::sleep(DELAY_WRITE);
match port.read_exact(&mut byte) {
Ok(()) => continue,
Err(_) => break,
}
}
Ok(())
}
pub fn write_memory<T: Read + Write>(port: &mut T, address: u16, bytes: &[u8]) -> Result<()> {
debug!("Writing {} byte(s) to address 0x{:x}", bytes.len(), address);
stop_cpu(port)?;
port.write_all(format!("l{:x} {:x}\r", address, address + bytes.len() as u16).as_bytes())?;
thread::sleep(DELAY_WRITE);
port.write_all(bytes)?;
thread::sleep(DELAY_WRITE);
start_cpu(port)?;
Ok(())
}
pub fn poke<T: Read + Write>(port: &mut T, destination: u16, value: u8) -> Result<()> {
write_memory(port, destination, &[value])
}
pub fn handle_prg_from_bytes<T: Read + Write>(
port: &mut T,
bytes: &[u8],
load_address: LoadAddress,
reset_before_run: bool,
run: bool,
) -> Result<()> {
if reset_before_run {
reset(port)?;
}
match load_address {
LoadAddress::Commodore65 => go65(port)?,
LoadAddress::Commodore64 => go64(port)?,
_ => {
return Err(anyhow::Error::msg("unsupported load address"));
}
}
write_memory(port, load_address.value(), bytes)?;
if run {
type_text(port, "run\r")?;
}
Ok(())
}
pub fn handle_prg<T: Read + Write>(
port: &mut T,
file: &str,
reset_before_run: bool,
run: bool,
) -> Result<()> {
let (load_address, bytes) = io::load_prg(file)?;
handle_prg_from_bytes(port, &bytes, load_address, reset_before_run, run)
}