#![allow(non_snake_case)]
use std::fs;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use chrono::Local;
use log::{debug, error, info};
use crate::bios::Bios;
use crate::data_input_stream::DataInputStream;
use crate::hardware::screen::Screen;
use crate::int;
#[derive(Debug)]
pub(crate) struct Memory {
pub(crate) light_pen_clic: bool,
pub(crate) light_pen_x: int,
pub(crate) light_pen_y: int,
mem: Vec<Vec<int>>,
mapper: [int; 16],
key: Vec<bool>,
dirty: Vec<bool>,
ORA: int,
ORB: int,
DDRA: int,
DDRB: int,
CRA: int,
pub(crate) CRB: int,
pub(crate) sound_mem: u8,
GA0: int,
GA1: int,
GA2: int,
pub(crate) GA3: int,
k7_bit: int,
k7_char: int,
k7_fis: Option<DataInputStream>,
k7_fos: Option<BufWriter<File>>,
is_file_opened: bool,
is_file_opened_out: bool,
k7_in: Option<DataInputStream>,
k7_out: Option<DataInputStream>,
k7_out_name: Option<String>,
}
impl Default for Memory {
fn default() -> Self {
Memory {
light_pen_clic: false,
light_pen_x: 0,
light_pen_y: 0,
mem: vec![vec![0; 0x1000]; 18],
mapper: [0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17],
key: vec![false; 256],
dirty: vec![false; 200],
ORA: 0,
ORB: 0,
DDRA: 0,
DDRB: 0,
CRA: 0,
CRB: 0,
sound_mem: 0,
GA0: 0,
GA1: 0,
GA2: 0,
GA3: 0,
k7_bit: 0,
k7_char: 0,
k7_fis: None,
k7_fos: None,
is_file_opened: false,
is_file_opened_out: false,
k7_in: None,
k7_out: None,
k7_out_name: None,
}
}
}
impl Memory {
pub(crate) fn read(&self, address: int) -> int {
let page = (address & 0xF000) >> 12;
self.mem[self.mapper[page as usize] as usize][(address & 0xFFF) as usize]
}
pub(crate) fn read_16(&self, address: int) -> int {
let b1 = self.read(address);
let b2 = self.read(address + 1);
(b1 << 8) + b2
}
pub(crate) fn write(&mut self, address: int, value: int) {
let page = (address & 0xF000) >> 12;
if (self.mapper[page as usize] >= 14) && (self.mapper[page as usize] <= 17) {
return; }
if address < 0x1F40 {
self.dirty[(address / 40) as usize] = true;
}
if page == 0x0A {
self.hardware(address, value);
} else {
self.mem[self.mapper[page as usize] as usize][(address & 0xFFF) as usize] =
value & 0xFF;
}
}
fn write_p(&mut self, address: int, value: int) {
if address < 0x1F40 {
self.dirty[(address / 40) as usize] = true;
}
let page = (address & 0xF000) >> 12;
if page == 0x0A {
self.hardware(address, value);
} else {
self.mem[self.mapper[page as usize] as usize][(address & 0xFFF) as usize] =
value & 0xFF;
}
}
fn get(&mut self, address: int) -> int {
let page = (address & 0xF000) >> 12;
self.mem[self.mapper[page as usize] as usize][(address & 0xFFF) as usize]
}
pub(crate) fn set(&mut self, address: int, value: int) {
let page = (address & 0xF000) >> 12;
self.mem[self.mapper[page as usize] as usize][(address & 0xFFF) as usize] = value & 0xFF;
}
pub(crate) fn POINT(&mut self, address: int) -> int {
let page = (address & 0xF000) >> 12;
self.mem[page as usize][(address & 0xFFF) as usize]
}
pub(crate) fn COLOR(&mut self, address: int) -> int {
let page = (address & 0xF000) >> 12;
self.mem[(page + 2) as usize][(address & 0xFFF) as usize]
}
pub(crate) fn is_dirty(&mut self, line: usize) -> bool {
let ret = self.dirty[line];
self.dirty[line] = false;
ret
}
pub(crate) fn set_all_dirty(&mut self) {
for i in 0..200 {
self.dirty[i] = true;
}
}
pub(crate) fn reset(&mut self) {
for i in 0..0xFFFF {
self.set(i, 0x00);
}
self.load_rom();
self.CRA = 0x00;
self.CRB = 0x00;
self.DDRA = 0x5F;
self.DDRB = 0x7F;
self.mem[0xA + 2][0x7CC] = 0xFF;
self.mem[0xA + 2][0x7CD] = 0xFF;
self.mem[0xA + 2][0x7CE] = 0xFF;
self.mem[0xA + 2][0x7CF] = 0xFF;
self.patch_k7();
}
fn load_rom(&mut self) {
let embedded_bios = Bios::get("mo5.rom").unwrap();
let starting_address = 0xC000;
for i in starting_address..0x10000 {
self.write_p(
i,
embedded_bios.data[(i - starting_address) as usize] as int,
);
}
}
fn hardware(&mut self, ADR: int, mut OP: int) {
if ADR == 0xA7C0 {
if (self.CRA & 0x04) == 0x04
{
if (OP & 0x01) == 0x01 {
self.mapper[0] = 0;
self.mapper[1] = 1;
} else {
self.mapper[0] = 2;
self.mapper[1] = 3;
}
OP |= 0x80 + 0x20; self.ORA = (self.ORA & (self.DDRA ^ 0xFF)) | (OP & self.DDRA);
if self.light_pen_clic {
self.mem[0xA + 2][0x7C0] = self.ORA | 0x20;
} else {
self.mem[0xA + 2][0x7C0] = self.ORA & (0xFF - 0x20);
}
} else {
self.DDRA = OP;
self.mem[0xA + 2][0x7C0] = OP;
}
} else if ADR == 0xA7C1
{
if (self.CRB & 0x04) == 0x04
{
self.ORB = (self.ORB & (self.DDRB ^ 0xFF)) | (OP & self.DDRB);
if self.key[(self.ORB & 0x7E) as usize] {
self.ORB &= 0x7F;
} else {
self.ORB |= 0x80;
}
self.mem[0xA + 2][0x7C1] = self.ORB;
self.sound_mem = ((self.ORB & 1) << 5) as u8;
} else {
self.DDRB = OP;
self.mem[0xA + 2][0x7C1] = OP;
}
} else if ADR == 0xA7C2 {
self.CRA = (self.CRA & 0xD0) | (OP & 0x3F);
self.mem[0xA + 2][0x7C2] = self.CRA;
} else if ADR == 0xA7C3 {
self.CRB = (self.CRB & 0xD0) | (OP & 0x3F);
self.mem[0xA + 2][0x7C3] = self.CRB;
}
}
pub(crate) fn set_key(&mut self, i: int) {
debug!("key down:{}", i);
self.key[i as usize] = true;
}
pub(crate) fn rem_key(&mut self, i: int) {
if self.key[i as usize] {
debug!("key up:{}", i);
self.key[i as usize] = false;
}
}
pub(crate) fn set_k7file(&mut self, name: &Path) -> bool {
info!("opening:{}", name.to_str().unwrap());
if self.k7_fis.is_none() {
self.is_file_opened = false;
}
if Path::new(name).exists() {
let metadata = fs::metadata(name).unwrap();
if metadata.len() == 0 {
error!("Error : file is empty");
return false;
}
if metadata.len() > 1000000 {
error!("Error : file is too big {}", metadata.len());
return false;
}
match DataInputStream::new(name) {
Ok(data) => {
println!(
"Opened K7 {} of length {}",
name.file_name().unwrap().to_str().unwrap(),
data.len()
);
self.k7_fis = Some(data);
self.is_file_opened = true;
self.k7_bit = 0;
self.k7_char = 0;
self.is_file_opened
}
Err(error) => {
error!("Error : file is missing {}", error);
false
}
}
} else {
self.is_file_opened
}
}
fn create_k7file(&mut self) -> bool {
if self.k7_out_name.is_some() {
return self.is_file_opened_out;
}
let aujourdhui = Local::now();
let kout_name = aujourdhui.format("%Y-%m-%d-%H_%M_%S.k7").to_string();
println!("Creating:{}", &kout_name);
self.k7_out_name = Some(kout_name);
if self.k7_fos.is_none() {
self.is_file_opened_out = false;
}
if self.is_file_opened_out {
}
let k7out_name = &self.k7_out_name.clone().unwrap();
if let Ok(k7fos) = File::open(k7out_name) {
let buf = BufWriter::new(k7fos);
self.k7_fos = Some(buf);
self.is_file_opened_out = true;
} else {
return self.is_file_opened_out;
}
self.k7_bit = 0;
self.k7_char = 0;
self.is_file_opened_out
}
fn readbit(&mut self, screen: &mut Screen) -> int {
if !self.is_file_opened {
return 0;
}
if self.k7_bit == 0x00 {
if self.k7_in.is_some() {
self.k7_char = self.k7_in.as_mut().unwrap().read();
} else if self.k7_fis.is_some() {
self.k7_char = self.k7_fis.as_mut().unwrap().read();
} else {
return 0;
}
self.k7_bit = 0x80;
}
let mut octet = self.get(0x2045);
if (self.k7_char & self.k7_bit) == 0 {
octet <<= 1;
self.set(0xF16A, 0x00);
} else {
octet = (octet << 1) | 0x01;
self.set(0xF16A, 0xFF);
}
self.set(0x2045, octet & 0xFF);
screen.led = octet & 0xff;
screen.show_led = 10;
self.k7_bit >>= 1;
0
}
pub(crate) fn periph(&mut self, PC: int, S: int, A: int, screen: &mut Screen) {
if PC == 0xF169 {
self.readbit(screen);
}
if PC == 0xF1B0 {
self.create_k7file();
if !self.is_file_opened_out {
return;
}
let data_out = [A as u8];
if let Some(k7fos) = &mut self.k7_fos {
if let Err(result) = k7fos.write(&data_out) {
eprintln!("Error writing to file: {}", result);
}
}
}
if PC == 0xF18C {
let mut c = self.get(S);
c &= 0xFE;
self.write(S, c);
}
if PC == 0xf549 {
self.write(S + 6, self.light_pen_x >> 8);
self.write(S + 7, self.light_pen_x & 255);
self.write(S + 8, self.light_pen_y >> 8);
self.write(S + 9, self.light_pen_y & 255);
}
}
fn patch_k7(&mut self) {
self.set(0xf548, 0x02); self.set(0xf549, 0x39);
self.set(0xF1AF, 0x02);
self.set(0xF1B0, 0x39);
self.set(0xF18B, 0x02);
self.set(0xF18C, 0x39);
self.set(0xF168, 0x02);
self.set(0xF169, 0x86); self.set(0xF16A, 0x00);
self.set(0xF16B, 0x39);
}
fn unpatch_k7(&mut self) {}
}