use crate::cpu::{
mos6502::{Mos6502, Mos6502Variant},
Cpu,
};
use crate::keyboard::commodore::C64VirtualAdapter;
use crate::keyboard::{
commodore::{C64KeyboardAdapter, C64SymbolAdapter},
KeyAdapter, KeyMappingStrategy, SymbolAdapter,
};
use crate::memory::mos652x::Via;
use crate::memory::{BlockMemory, BranchMemory, NullMemory, NullPort, Port};
use crate::platform::{PlatformProvider, WindowConfig};
use crate::roms::RomFile;
use crate::systems::System;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::sync::Arc;
mod chip;
mod keyboard;
use self::keyboard::KEYBOARD_MAPPING;
use chip::{VicChip, VicChipIO};
use instant::Duration;
#[cfg(target_arch = "wasm32")]
use js_sys::Reflect;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsValue;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::JsCast;
#[cfg(target_arch = "wasm32")]
use js_sys::Uint8Array;
use super::BuildableSystem;
pub struct Vic20SystemRoms {
pub character: RomFile,
pub basic: RomFile,
pub kernal: RomFile,
pub cartridge: Option<RomFile>,
}
impl Vic20SystemRoms {
#[cfg(not(target_arch = "wasm32"))]
pub fn from_disk(cartridge_path: Option<&str>) -> Self {
use crate::roms::DiskLoadable;
let character = RomFile::from_file("vic/char.bin");
let basic = RomFile::from_file("vic/basic.bin");
let kernal = RomFile::from_file("vic/kernal.bin");
let cartridge = cartridge_path.map(RomFile::from_file);
Self {
character,
basic,
kernal,
cartridge,
}
}
#[cfg(target_arch = "wasm32")]
pub fn from_jsvalue(value: &JsValue) -> Self {
use crate::roms::JsValueLoadable;
let character = Reflect::get(value, &JsValue::from_str("char"))
.unwrap()
.dyn_into::<Uint8Array>()
.unwrap();
let basic = Reflect::get(value, &JsValue::from_str("basic"))
.unwrap()
.dyn_into::<Uint8Array>()
.unwrap();
let kernal = Reflect::get(value, &JsValue::from_str("kernal"))
.unwrap()
.dyn_into::<Uint8Array>()
.unwrap();
let cartridge = Reflect::get(value, &JsValue::from_str("cartridge"))
.unwrap()
.dyn_into::<Uint8Array>();
let cartridge = match cartridge {
Ok(v) => Some(RomFile::from_uint8array(&v)),
Err(_) => None,
};
Self {
character: RomFile::from_uint8array(&character),
basic: RomFile::from_uint8array(&basic),
kernal: RomFile::from_uint8array(&kernal),
cartridge,
}
}
}
pub struct VicVia1PortA {
platform: Arc<dyn PlatformProvider>,
joy_pin_3: Rc<Cell<bool>>,
}
impl VicVia1PortA {
pub fn new(platform: Arc<dyn PlatformProvider>) -> Self {
Self {
platform,
joy_pin_3: Rc::new(Cell::new(true)),
}
}
pub fn get_joy_pin_3(&self) -> Rc<Cell<bool>> {
self.joy_pin_3.clone()
}
}
impl Port for VicVia1PortA {
fn read(&mut self) -> u8 {
let joystick = self.platform.get_joystick_state();
let pin_0 = !joystick.up;
let pin_1 = !joystick.down;
let pin_2 = !joystick.left;
self.joy_pin_3.set(!joystick.right);
let lightpen_fire = !joystick.fire;
(pin_0 as u8) << 2 | (pin_1 as u8) << 3 | (pin_2 as u8) << 4 | (lightpen_fire as u8) << 5
}
fn write(&mut self, _value: u8) {}
fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool {
false
}
fn reset(&mut self) {}
}
pub struct VicVia2PortB {
keyboard_col: Rc<Cell<u8>>,
joy_pin_3: Rc<Cell<bool>>,
}
impl VicVia2PortB {
pub fn new(joy_pin_3: Rc<Cell<bool>>) -> Self {
Self {
keyboard_col: Rc::new(Cell::new(0)),
joy_pin_3,
}
}
pub fn get_keyboard_col(&self) -> Rc<Cell<u8>> {
self.keyboard_col.clone()
}
}
impl Port for VicVia2PortB {
fn read(&mut self) -> u8 {
self.keyboard_col.get() | (self.joy_pin_3.get() as u8) << 7
}
fn write(&mut self, value: u8) {
self.keyboard_col.set(value);
}
fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool {
false
}
fn reset(&mut self) {}
}
pub struct VicVia2PortA {
keyboard_col: Rc<Cell<u8>>,
mapping_strategy: KeyMappingStrategy,
platform: Arc<dyn PlatformProvider>,
}
impl VicVia2PortA {
pub fn new(
keyboard_col: Rc<Cell<u8>>,
mapping_strategy: KeyMappingStrategy,
platform: Arc<dyn PlatformProvider>,
) -> Self {
Self {
keyboard_col,
mapping_strategy,
platform,
}
}
}
impl Port for VicVia2PortA {
fn read(&mut self) -> u8 {
let col_mask = self.keyboard_col.get();
let mut value = 0b1111_1111;
let state = match &self.mapping_strategy {
KeyMappingStrategy::Physical => C64KeyboardAdapter::map(&self.platform.get_key_state()),
KeyMappingStrategy::Symbolic => {
C64SymbolAdapter::map(&SymbolAdapter::map(&self.platform.get_key_state()))
}
};
let state = state | C64VirtualAdapter::map(&self.platform.get_virtual_key_state());
for (y, row) in KEYBOARD_MAPPING.iter().enumerate() {
for (x, key) in row.iter().enumerate() {
if ((!col_mask & (1 << x)) != 0) && state.is_pressed(*key) {
value &= !(1 << y);
}
}
}
value
}
fn write(&mut self, _value: u8) {}
fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool {
false
}
fn reset(&mut self) {}
}
pub struct Vic20SystemConfig {
pub mapping: KeyMappingStrategy,
}
impl BuildableSystem<Vic20SystemRoms, Vic20SystemConfig> for Vic20System {
fn build(
roms: Vic20SystemRoms,
config: Vic20SystemConfig,
platform: Arc<dyn PlatformProvider>,
) -> Box<dyn System> {
let low_ram = BlockMemory::ram(0x0400);
let main_ram = BlockMemory::ram(0x0E00);
let vic_chip = Rc::new(RefCell::new(VicChip::new(platform.clone())));
let v1a = VicVia1PortA::new(platform.clone());
let v2b = VicVia2PortB::new(v1a.get_joy_pin_3());
let v2a = VicVia2PortA::new(v2b.get_keyboard_col(), config.mapping, platform);
let via1 = Via::new(Box::new(v1a), Box::new(NullPort::new()));
let via2 = Via::new(Box::new(v2a), Box::new(v2b));
let basic_rom = BlockMemory::from_file(0x2000, roms.basic);
let kernel_rom = BlockMemory::from_file(0x2000, roms.kernal);
let vram = BlockMemory::ram(0x0200);
let characters = BlockMemory::from_file(0x1000, roms.character);
let colors = BlockMemory::ram(0x0200);
let chip_io = VicChipIO::new(vic_chip.clone());
let cartridge = match roms.cartridge {
Some(rom) => BlockMemory::from_file(0x4000, rom),
None => BlockMemory::ram(0x4000),
};
let memory = BranchMemory::new()
.map(0x0000, low_ram)
.map(0x0400, NullMemory::new())
.map(0x1000, main_ram)
.map(0x1E00, vram)
.map(0x2000, NullMemory::new())
.map(0x8000, characters)
.map(0x9000, chip_io)
.map(0x9010, NullMemory::new())
.map(0x9110, via1)
.map(0x9120, via2)
.map(0x9130, NullMemory::new())
.map(0x9600, colors)
.map(0xA000, cartridge)
.map(0xC000, basic_rom)
.map(0xE000, kernel_rom);
let cpu = Mos6502::new(memory, Mos6502Variant::NMOS);
Box::new(Vic20System { cpu, vic: vic_chip })
}
}
pub struct Vic20System {
cpu: Mos6502,
vic: Rc<RefCell<VicChip>>,
}
impl System for Vic20System {
fn get_cpu_mut(&mut self) -> Box<&mut dyn Cpu> {
Box::new(&mut self.cpu)
}
fn tick(&mut self) -> instant::Duration {
Duration::from_secs_f64(1.0 / 1_000_000.0) * self.cpu.tick() as u32
}
fn reset(&mut self) {
self.cpu.reset();
}
fn render(&mut self, framebuffer: &mut [u8], _config: WindowConfig) {
self
.vic
.borrow_mut()
.redraw_screen(&mut self.cpu.memory, framebuffer);
}
}