From dcb08d2639d3ec94f03fd273ff61d93f8f7d7658 Mon Sep 17 00:00:00 2001 From: Nathan Kiesman Date: Sun, 14 Apr 2024 01:30:14 -0400 Subject: [PATCH] printing a single letter maybe --- Cargo.toml | 29 +- aiie/README.md | 3 - aiie/appleiie.bin | Bin 16128 -> 0 bytes aiie/applesoft.bin | Bin 10240 -> 0 bytes aiie/character.bin | Bin 8730 -> 0 bytes aiie/firmware.bin | Bin 3840 -> 0 bytes aiie/monitor.bin | Bin 2048 -> 0 bytes applei/wozmon.bin | Bin 0 -> 256 bytes cpu.trace | 731722 ++++++++++++++++++++++++++++++ src/cpu/execute.rs | 1 - src/cpu/mod.rs | 1 + src/main.rs | 27 +- src/memory/mos652x/pia.rs | 15 +- src/systems/aiie/keyboard.rs | 27 - src/systems/aiie/mod.rs | 216 - src/systems/aiie/notes.md | 5 - src/systems/aiie/roms.rs | 36 - src/systems/aiie/switches.rs | 631 - src/systems/applei/keyboard.rs | 475 + src/systems/applei/mod.rs | 160 + src/systems/applei/roms.rs | 39 + src/systems/mod.rs | 2 +- www/src/Emulator.tsx | 4 +- www/src/main.tsx | 2 +- 24 files changed, 732438 insertions(+), 957 deletions(-) delete mode 100644 aiie/README.md delete mode 100644 aiie/appleiie.bin delete mode 100644 aiie/applesoft.bin delete mode 100644 aiie/character.bin delete mode 100644 aiie/firmware.bin delete mode 100644 aiie/monitor.bin create mode 100644 applei/wozmon.bin create mode 100644 cpu.trace delete mode 100644 src/systems/aiie/keyboard.rs delete mode 100644 src/systems/aiie/mod.rs delete mode 100644 src/systems/aiie/notes.md delete mode 100644 src/systems/aiie/roms.rs delete mode 100644 src/systems/aiie/switches.rs create mode 100644 src/systems/applei/keyboard.rs create mode 100644 src/systems/applei/mod.rs create mode 100644 src/systems/applei/roms.rs diff --git a/Cargo.toml b/Cargo.toml index 370dbcab..30eecd7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,10 @@ name = "noentiendo" version = "0.1.0" edition = "2021" -authors = ["noentiendo team ", "Brooke Chalmers "] +authors = [ + "noentiendo team ", + "Brooke Chalmers ", +] description = "A modular retro emulation framework" documentation = "https://noentiendo.breq.dev/doc/libnoentiendo/" homepage = "https://noentiendo.breq.dev/" @@ -12,7 +15,7 @@ license = "AGPL-3.0-or-later" # Shared dependencies across all targets [dependencies] -instant = { version = "0.1", features = [ "wasm-bindgen" ] } +instant = { version = "0.1", features = ["wasm-bindgen"] } async-trait = "0.1" pixels = "0.11" serde = { version = "1.0", features = ["derive"] } @@ -28,16 +31,16 @@ serde-wasm-bindgen = "0.4" [dependencies.web-sys] version = "0.3" features = [ - 'HtmlCanvasElement', - 'CanvasRenderingContext2d', - 'CssStyleDeclaration', - 'KeyboardEvent', - 'Document', - 'NamedNodeMap', - 'Attr', - 'Gamepad', - 'GamepadButton', - 'console', + 'HtmlCanvasElement', + 'CanvasRenderingContext2d', + 'CssStyleDeclaration', + 'KeyboardEvent', + 'Document', + 'NamedNodeMap', + 'Attr', + 'Gamepad', + 'GamepadButton', + 'console', ] # Dependencies used when building for desktop @@ -45,7 +48,7 @@ features = [ winit = "0.27" winit_input_helper = "0.13" rand = "0.8" -clap = { version = "3.2", features = ["derive"]} +clap = { version = "3.2", features = ["derive"] } gilrs = "0.10.1" [profile.release] diff --git a/aiie/README.md b/aiie/README.md deleted file mode 100644 index 9e588012..00000000 --- a/aiie/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Source: -https://mirrors.apple2.org.za/ftp.apple.asimov.net/emulators/rom_images/ -apple_2e_unenhanced_rom.zip diff --git a/aiie/appleiie.bin b/aiie/appleiie.bin deleted file mode 100644 index de03bebd79ddd0fdcf4b455934ef656393ad867f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16128 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 diff --git a/aiie/character.bin b/aiie/character.bin deleted file mode 100644 index d48b74dae16f105a5e75f136c80f8044f9f9ffd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8730 zcmeHM%W51)6fK%TG(w2+FpI%7!_XKJ1}%gdj4;Ej{0sht{DPKQWRZX&n=C>wWRpd} zf>=+>wrqKuM}I&b=m)as{6fyHy0=c%OvfQ)nQ0AO_nfNxI^7M|xT@!m?iUMJj_Z0^ z*KS(Z@^e*P6h&PL#x-@zqL>!NQrE&QuzgrB95&%ryQ-9{ax=N$SC6*0wnw$YFddP>Eo7?$DjAh*yH*9p~M?Y>3+pgx10^j zfz-Jr*V4ISP!4C#Ef3@ZmQHck>k~748dtjUcv}XO@JOJG_zwo6oBU*5%!a#&j~aXytjH^BKmjn&1b0yb9L^6> zUWxVp{rrPAB3LzB;C;qxj#q37EN}S2&A>uh*{_$tlLv#T=q?zFSK)jOCFKF)dAZ4O zI2`TZ;~DMZJ@$_kymR;f=JMwGFDm%sxp|B)0Qn@|%(+Kf@;ylYYBqHPN%vp^j4}@Q z!;1G`Fg^fTcMly<6XL=ot>?nog;Zq{k1!p984y=ZvQ@x$~7P3&$&O}zuWva z3TzbED6mmrqrgUijRG45HVSMM*eLJ;1&(|7nqKzbk{|av)az`25&iB@o~-*nzPHZx zx6qRuuJ<0V`~SCpQ1x`wUrM3kB72>RE^iKly zlt-%1VjZ&k`%O?G+=clq6G)g}zh=XNfBBq+3&u~E4A6-O5}DK`(McH=NIxbdmq?|a ziB?%bp)B09(19gI7jn7vb0(JT{gjC%%#Vp!f`3TF(r8SCedRK-luN`?CJ{>+T@Z+| zGO_ePB9=0VSXqz|3is~zw!66vuzQz3F*`litmC`SGCw*oL!y=XCt4}f zw4Y{in22!_t#mulDsu>-C`Z7!XOx1BQ@Jv(-@BjH{xg3qja;8a;kawUkul-O@cELX z7UWDkGA16G*b)rZ6WNSXuZ+`LGA?*46(=bkQHz8YPlEN4>$6NP?wML-s1`Y@Mb6YB zV``C!y1=m9kxK}zKVh^A&m_Z?$H^$=aU7&Pier>V$VPFDVS&c1t|p7|6vY@yVvGr5 z*yJ!A8;4=>BbN|bZ^CGH&m_Z=$H}PVaU7I9ier*TnHa`U3}dWd!>cRBupNqFjEP~` z#4sEihtae$4uzqpY4!hsRE4IMiAWCV((15?p{QwBC!(g6i3mS!s%d2+!cPlq+G~I? z6gBNj9)#7jSGZg6O?wGs)rN7%?&sKH$4z?%Z2dOvDUfyBw5t;l{lZ8@3^(mLrPHG4A=0>Sk)lo6p#x;5Ha5da;6Cx(}heJeu(=l(};Vf6Pefw3_Bl29z)nrof1Y> zN*D|~35sD8PFqbFEyI{FJzVa|xs68WV;XL)5|;)55T6VK@vghNy)x*0W)*e*x5XV?qD` diff --git a/aiie/firmware.bin b/aiie/firmware.bin deleted file mode 100644 index c93511ce5d30f1446122f07d9684df8fcbfc7c1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3840 zcmZWseQ+Da72ng{$&zfxa^fT=ANc&$o@`~Jx@qFhnAQz6y_hT5scG_0rq?ob6sE(N zPTO%u?ZhRyjxZdRhBD(eFsMS;@wptxg+iP*!^Vzm%aW`mGjvKB8n+B8ZXjHt2?Qlk z-<~8Bz+>(1+qe6BZ{NOs?;Um$Ru0eN8tCquosfi%4Dpyn&L_e|;!JSTI3vycCod_9 z4!sk&SB3x0#!&DGO0`EUq3x=CVODQbJ7eg0hqa=&(G|L=cIiH~JJA{r9$`}L$$C0F zEk?V--YbEB#t=Z1cP2WFSX5j*+Zwr&+-!(ytI@2s8=KY63cCeXf`H&8Rp`1_%U`th{Sh*qbv$yxO6K zIJ3yBo!*_oP)6_Yw_q=y3vm{!g?M`2_u;(8AWR3CfvQp<0rVQm0GLq%ndV_^eg~W1 z!-4JQcd>VCENLLM&v>a!_MwA^<-6zPmYQ;A4k>s@v(|yssg}tEet$JVTEBY;OeznjC z)Gd($tO$BpSPWa}fW=@SMKa%ECG#RHMLaMA+`*ehY)}plHUzbcY*2d#es25&+Eqyx zB_(4{`>-YmhrZ!&D_ow+$4kF8EOobWP2FQYfQ3U>;V#{(M|Dk|Zp4Q#E_hjCL+Y&+ z7qPU5u)h&&$8dQ{8^iwfSbG2m>=mGre*;yxLh{((gyA3+(vAI_u;!|gwb;KIYo02} zV1F~#;L<3R@W{0|GD@oJ^cH;ha#aB*C{T~VF}1qtt_JMC8EfmR$gcPe7A9E4XA|_$*7_m}fxU zlC>k9C);m?I}h5=R}TCVj8CNzSnqkmsj}pmaqE87p|YU7^#t|KHx|qx9B%}3>6}%o z!>NXl>XgsT>kc~5ot_9E7l2Ls&Pv}^>33H8U6sDK()U&Re(2?yXny*+7&L#R%`|P2(9FQ^B>Y~4-wQF6%e@kt&z@O`rJg^9()lX!r&CCCVy%>q zAp@7)E*6;imJDa^ zxuR7kjOJ<2jmjGqjX8CX`elPxdyN_h3rH0V1n(-~Fq+ghJ4r#Z(rF$sm>oq9(t$j< z2w+2VG=I9Btr~;jp=u2nQjXZ{q8Y{wb9$@qI^DRIY;4iB711n679*^Dd67olmniD* z7R`_|t&!%Z5%zAtkCFzz7t?VOAG25(`;MlCq*M4!M*i30(;*&K3-2voqys+H8exUE zGcXdKUAFUGa0)^wEKva8-4FOMw$sY%%fiNt276y$PM?4`@M2mxkcTPZo;(OpA`VDg zjj#SNZSi66hXL=*v9#|f$b>%dSD8^CeDxvT+5!dAYne5{qnz-$ zEL10>Tsp28ZSv5PApnwaA_pg+S)Yx&l^b0F>qt z9j=S>xvU*#(FwsUBFjgNufhAAnGD8}QOMTORO-jnp4tG2DR* zShQYo20GkhRPh_UV?Eu=Sp0H`CrsnA@<&2-D=mVArTiy9+#! z(-3DO?qS4vwha-BwH`mef{5p1T6w;0++xBLFcX{-wq(H&U?|W?@;c#|FjG}9VgAEnFP=Bd{>~B zihWn_N;f4}!EK?Kk`FDrMq}dfZvQYA+Lrxb)3)VAH+f%F=DYRofyrqxL3vRTF8X8G zGUnZ%!K}wY@GNQ{upa=UaEUV`N>TWyBer7NtS>E{lr6M9arQA(;8b_Wwb?FJIOJ;?oUUfRIKkJ+mI0zo7q+ z(?3-A=({C2)b8os(cLtu?;)3hkbWT<2fYpBUZ**RsI%hfd^_Za=RWyq=s!tL3%0D}?T4Ektc{_XVZ>#NX1)5d<53xC*S^xk5 diff --git a/aiie/monitor.bin b/aiie/monitor.bin deleted file mode 100644 index cb6d7e1374e5c5d454be5f89de9d7a9fcd1c2b49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2048 zcmYjS4NP0t6~6WZW5^HA(gyOk-hgrJ&H+Pygp30uh34V8(yFj3O;+tGTDIqjO;eUh znKY?s>PI|Uwnr1XbW5}w&yVs@oY^dyN;AgXDb)2y@<#sr%iA$q zq&?}~bI$!a_xyb4T(24LOWWm2(deHO>SMM}S6%&!wgDT5QF_uDT@%Nv)8Zq)fd!eL z+9p2aNCP#Jt$}T1tBeE}wx@%ZM;z;QFy$q}R^Cw!nqC!b6&0m6!Ny6hYCq$s6kHYX zmAV8M=Ph0sv)RQGFMN(lZpa;O!EYBZo4Mc~_KM}-j#|`^w$gFRJ2HMagMXN&CFF#R zM#hISLcN0#8qPX?I&zjUbA8$huW0sq%W;rq<3YN}>AH?jrrDSq-ztO**qfPbh>QOY zF-nQJ$PbSx8NoS*vcfhQky6t(oAcQqP5Xb3?-UYr0qzesgiLdcw#ig4t{(qQ2Ij}d zG8(hI{uvi^eFkc!dNmnyi;MWB%t(?N;%8y5&j;5rGFjf3=9;C3aLb6r5Io`*4RNjn zce_@dC=r>O?(Ibi@)O!plu=E`_Eqr?wC2B5HWVq$KcTk9Q$I&rUOS1<%P$PP)$0|` zUz9KYYB)TLX2M~#sc%#5D{pMPaK3MI?fEx0UqFuam;A%)Ypwe?!oU6k+HmNQ{DghI!T9Xr~=k2Z~>8nj`9&35!?Fg}^Eu^ka>K`52NKPkwaLOURIPbjyXnei`XAwTjs3|G96w4^J^y_`jJomfXYqkASaZQ(Zg z+ozKsOz77ENE0)&^yA3)6aw1DzoA%4;xrxPKp`7>Nr`kTkrx#iX)7XMQ6l>lyelm~ zCDgzwCT99|xmu`|tAsjVO&%Y}6Z4ok_s;5)Abrd&d)ohi;naRweGw^^waRH)31KMm zIpuYY0p3^0HT#_T;W0Lgf0n~r3hJMtcU4tPsW-HFnlzb@JD*m}{bh;|O{D%;d5$NQ zXNNko3wBODMZX}oY^*A>!dn7kvoDxWtXDa{RL6cZo%l({tn~U=;s7!&f)7aiH*G% zi@Y~_kxWo}V~QB)x{uWBe4pOZXOtZSx+G zc5~#*_|rVi_?My&$rfPa&=Cq^DQN<75i7)*;6i2=XY>5C#GQah;6gqa;grq>OS-c` zXi!c-Y+`oS(N^9*VCQ?95ixv;^R3!ypx;Wq5U#J*QrsAdmaUP8!M)gC zFiDn+4#U1#4*y_Deu~93jrV5+cc6uDh6LW@`n0kuoDo``Zg)?Z-z@RzO5F0|RNn z3;EYPo%Jve3U-Pb$W!&&DtQV#lRqlD2lP}iH!Lf44m}^_*3 xg-wHH#!?Oa>-4+R?~JO~CU(9#`S!1?s=oBh>#x3Y<4V4&>LHV34rkl5{{e#!<7@x` diff --git a/applei/wozmon.bin b/applei/wozmon.bin new file mode 100644 index 0000000000000000000000000000000000000000..7280907226f38c9243aa42bcba245bf5824b764e GIT binary patch literal 256 zcmcZ+v7o+3=)%h7y@D5dg)f}E|3UcV><`Q*1o&6pQF#AMm0|CJ1@xLUPO>||g%+56$j$#oOHeE4v3*N4|9FMY6X(`eId(E>_& zZaB#`fi1J+WTtKjhg-cCCP4m=+A15w6IHL7ox{7vyz*3DywP$J { - // BRK self.registers.pc.increment(); self.interrupt(true, true); Ok(7) diff --git a/src/cpu/mod.rs b/src/cpu/mod.rs index c2340c37..857bb584 100644 --- a/src/cpu/mod.rs +++ b/src/cpu/mod.rs @@ -109,6 +109,7 @@ pub trait InterruptHandler { impl InterruptHandler for Mos6502 { fn interrupt(&mut self, maskable: bool, break_instr: bool) { + return; if maskable && !break_instr && self.registers.sr.read(flags::INTERRUPT) { return; } diff --git a/src/main.rs b/src/main.rs index 85fcbb95..e1328ebb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,20 +4,11 @@ use libnoentiendo::{ platform::{SyncPlatform, TextPlatform, WinitPlatform}, roms::DiskLoadable, systems::{ - aiie::{AiieSystemBuilder, AiieSystemConfig, AiieSystemRoms}, - basic::BasicSystemBuilder, - c64::C64SystemBuilder, - c64::C64SystemConfig, - c64::C64SystemRoms, - easy::Easy6502SystemBuilder, - klaus::KlausSystemBuilder, - pet::PetSystemBuilder, - pet::PetSystemConfig, - pet::PetSystemRoms, - vic::Vic20SystemBuilder, - vic::Vic20SystemConfig, - vic::Vic20SystemRoms, - SystemBuilder, + applei::AppleiSystemBuilder, applei::AppleiSystemConfig, applei::AppleiSystemRoms, + basic::BasicSystemBuilder, c64::C64SystemBuilder, c64::C64SystemConfig, c64::C64SystemRoms, + easy::Easy6502SystemBuilder, klaus::KlausSystemBuilder, pet::PetSystemBuilder, + pet::PetSystemConfig, pet::PetSystemRoms, vic::Vic20SystemBuilder, vic::Vic20SystemConfig, + vic::Vic20SystemRoms, SystemBuilder, }, }; @@ -32,7 +23,7 @@ enum SystemArg { Pet, Vic, C64, - Aiie, + Applei, } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] @@ -119,9 +110,9 @@ fn main() { C64SystemConfig { mapping }, platform.provider(), ), - SystemArg::Aiie => AiieSystemBuilder::build( - AiieSystemRoms::from_disk(), - AiieSystemConfig {}, + SystemArg::Applei => AppleiSystemBuilder::build( + AppleiSystemRoms::from_disk(), + AppleiSystemConfig { mapping }, platform.provider(), ), }; diff --git a/src/memory/mos652x/pia.rs b/src/memory/mos652x/pia.rs index 9b10cb8b..5acc2d0a 100644 --- a/src/memory/mos652x/pia.rs +++ b/src/memory/mos652x/pia.rs @@ -56,7 +56,13 @@ impl PiaPortRegisters { /// Poll the underlying port for interrupts. pub fn poll(&mut self, cycles: u32, info: &SystemInfo) -> bool { - self.port.poll(cycles, info) + let int = self.port.poll(cycles, info); + if int { + self.control |= pia_control_bits::C1_ACTIVE_TRANSITION_FLAG; + } else { + self.control &= !pia_control_bits::C1_ACTIVE_TRANSITION_FLAG; + } + int } /// Reset the DDR, control register, and underlying port. @@ -98,16 +104,19 @@ impl Pia { impl Memory for Pia { fn read(&mut self, address: u16) -> u8 { - match address % 0x04 { + let val = match address % 0x04 { 0x00 => self.a.read(), 0x01 => self.a.control, 0x02 => self.b.read(), 0x03 => self.b.control, _ => unreachable!(), - } + }; + //println!("PIA read {:04X} {:02X}", address, val); + val } fn write(&mut self, address: u16, value: u8) { + println!("PIA write {:04X} {:02X}", address, value); match address % 0x04 { 0x00 => self.a.write(value), 0x01 => self.a.control = value, diff --git a/src/systems/aiie/keyboard.rs b/src/systems/aiie/keyboard.rs deleted file mode 100644 index ecc5f6fe..00000000 --- a/src/systems/aiie/keyboard.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::keyboard::{KeyAdapter, KeyState, KeySymbol}; - -pub struct AppleIISymbolAdapter; - -impl KeyAdapter for AppleIISymbolAdapter { - fn map(state: &KeyState) -> KeyState { - let mut mapped = KeyState::new(); - - for symbol in state.pressed() { - use KeySymbol::*; - - let mapped_symbol = match symbol { - &Char(c) => Some(c as u8), - Return => Some(0x0D), // Carriage Return - Backspace => Some(0x08), - Escape => Some(0x1B), - _ => None, - }; - - if let Some(symbol) = mapped_symbol { - mapped.press(symbol); - } - } - - mapped - } -} diff --git a/src/systems/aiie/mod.rs b/src/systems/aiie/mod.rs deleted file mode 100644 index c31de06b..00000000 --- a/src/systems/aiie/mod.rs +++ /dev/null @@ -1,216 +0,0 @@ -use std::sync::Arc; - -use crate::{ - cpu::{MemoryIO, Mos6502, Mos6502Variant}, - memory::{BankedMemory, BlockMemory, BranchMemory, LoggingMemory, NullMemory}, - platform::{Color, PlatformProvider, WindowConfig}, - systems::System, -}; - -// https://mirrors.apple2.org.za/apple.cabi.net/Languages.Programming/MemoryMap.IIe.64K.128K.txt -// https://www.kreativekorp.com/miscpages/a2info/memorymap.shtml - -mod keyboard; -mod roms; -mod switches; - -pub use roms::AiieSystemRoms; - -use instant::Duration; - -use self::switches::{AiieBankSelectors, AiieSoftSwitches}; - -use super::SystemBuilder; - -const WIDTH: u32 = 40; -const HEIGHT: u32 = 24; -const CHAR_HEIGHT: u32 = 8; -const CHAR_WIDTH: u32 = 7; - -/// Configuration for a Apple IIe system. -pub struct AiieSystemConfig {} - -/// A factory for creating a Commodore 64 system. -pub struct AiieSystemBuilder; - -impl SystemBuilder for AiieSystemBuilder { - fn build( - roms: AiieSystemRoms, - _config: AiieSystemConfig, - platform: Arc, - ) -> Box { - platform.request_window(WindowConfig::new( - WIDTH * CHAR_WIDTH, - HEIGHT * CHAR_HEIGHT, - 2.0, - )); - - let selectors = AiieBankSelectors::new(); - let io = AiieSoftSwitches::new(platform, selectors.clone()); - - let memory = BranchMemory::new() - .map( - 0x0000, - BankedMemory::new(selectors.zp_stack.clone()) - .bank(BlockMemory::ram(0x0100)) // Main memory - .bank(BlockMemory::ram(0x0100)), // Aux memory - ) - .map( - 0x0100, - BankedMemory::new(selectors.low_segment.clone()) - .bank(BlockMemory::ram(0x0100)) // Main memory - .bank(BlockMemory::ram(0x0100)), // Aux memory - ) - .map( - 0x0200, - BankedMemory::new(selectors.after_stack.clone()) - .bank(BlockMemory::ram(0x0200)) // Main memory - .bank(BlockMemory::ram(0x0200)), // Aux memory - ) - .map( - 0x0400, - BankedMemory::new(selectors.text_page_1.clone()) - .bank(BlockMemory::ram(0x0400)) // Text Page 1 - .bank(BlockMemory::ram(0x0400)), // Text Page 1X - ) - .map( - 0x0800, - BankedMemory::new(selectors.text_page_2.clone()) - .bank(BlockMemory::ram(0x1800)) // Text Page 2 - .bank(BlockMemory::ram(0x1800)), // Text Page 2X - ) - .map( - 0x2000, - BankedMemory::new(selectors.hires_page_1.clone()) - .bank(BlockMemory::ram(0x2000)) // HiRes Page 1 - .bank(BlockMemory::ram(0x2000)), // HiRes Page 1X - ) - .map( - 0x4000, - BankedMemory::new(selectors.hires_page_2.clone()) - .bank(BlockMemory::ram(0x8000)) // HiRes Page 1 - .bank(BlockMemory::ram(0x8000)), // HiRes Page 1X - ) - .map(0xC000, io) - .map( - 0xC100, - BankedMemory::new(selectors.ext_slot_rom.clone()) - .bank(LoggingMemory::new( - NullMemory::new(), - "Peripheral Card", - 0xC100, - )) - .bank(BlockMemory::from_file(0x0F00, roms.firmware)), - ); - - let upper_rom = BranchMemory::new() - .map(0x0000, BlockMemory::from_file(0x2800, roms.applesoft)) - .map(0x2800, BlockMemory::from_file(0x0800, roms.monitor)); - - let upper_main_ram = BranchMemory::new() - .map( - 0x0000, - BankedMemory::new(selectors.ram_bank_select.clone()) - .bank(BlockMemory::ram(0x1000)) - .bank(BlockMemory::ram(0x1000)), - ) - .map(0x1000, BlockMemory::ram(0x2000)); - - let upper_aux_ram = BranchMemory::new() - .map( - 0x0000, - BankedMemory::new(selectors.ram_bank_select) - .bank(BlockMemory::ram(0x1000)) - .bank(BlockMemory::ram(0x1000)), - ) - .map(0x1000, BlockMemory::ram(0x2000)); - - let memory = memory.map( - 0xD000, - BankedMemory::new(selectors.rom_ram_select) - .bank(upper_rom) - .bank( - BankedMemory::new(selectors.upper_ram) - .bank(upper_main_ram) - .bank(upper_aux_ram), - ), - ); - - let cpu = Mos6502::new(memory, Mos6502Variant::NMOS); - - Box::new(AiieSystem { - cpu, - characters: roms.character.get_data(), - }) - } -} - -/// The Apple IIe system. -pub struct AiieSystem { - cpu: Mos6502, - characters: Vec, -} - -impl System for AiieSystem { - fn tick(&mut self) -> Duration { - if self.cpu.registers.pc.address() < 0xF800 { - // println!("{:#04x}", self.cpu.registers.pc.address()); - // println!("{}", self.cpu.memory.read(32768)); - } - Duration::from_secs_f64(1.0 / 1_000_000.0) * self.cpu.tick() as u32 - } - - fn reset(&mut self) { - self.cpu.reset(); - } - - #[allow(clippy::identity_op)] - fn render(&mut self, framebuffer: &mut [u8], config: WindowConfig) { - // https://retrocomputing.stackexchange.com/a/2541 - for index in 0x000..=0x3FF { - let position = match index & 0x7F { - 0x00..=0x27 => Some((0, (index & 0x7F) - 0x00)), - 0x28..=0x4F => Some((1, (index & 0x7F) - 0x28)), - 0x50..=0x77 => Some((2, (index & 0x7F) - 0x50)), - _ => None, - }; - - if let Some((third, x)) = position { - let y = (third * 8) + (index >> 7); - // println!("{} -> {}, {}", index, x, y); - - let value = self.cpu.read((0x0400 + index) as u16); - - let character_index = ((value & 0b0111_1111) as usize) * 8; - let inverted = (value & 0b1000_0000) == 0; - /*let mode = (value & 0b1100_0000) >> 6; - - let inverted = match mode { - 0b00 => true, - 0b01 => flash_state, - 0b10 | 0b11 => false, - _ => unreachable!(), - };*/ - - let character = self.characters[character_index..(character_index + 8)].to_vec(); - - for line in 0..CHAR_HEIGHT { - let line_data = character[line as usize]; - for pixel in 0..CHAR_WIDTH { - let color = if (line_data & (1 << pixel) != 0) ^ inverted { - Color::new(0, 255, 0) - } else { - Color::new(0, 0, 0) - }; - - let x = x * CHAR_WIDTH + pixel; - let y = y * CHAR_HEIGHT + line; - let index = ((y * config.width + x) * 4) as usize; - let pixel = &mut framebuffer[index..(index + 4)]; - pixel.copy_from_slice(&color.to_rgba()); - } - } - } - } - } -} diff --git a/src/systems/aiie/notes.md b/src/systems/aiie/notes.md deleted file mode 100644 index 6ddc3370..00000000 --- a/src/systems/aiie/notes.md +++ /dev/null @@ -1,5 +0,0 @@ -# Useful documentation/notes - -[apple ii general memory map](https://mirrors.apple2.org.za/apple.cabi.net/Languages.Programming/MemoryMap.IIe.64K.128K.txt) - -[computer specific memory maps](https://www.kreativekorp.com/miscpages/a2info/memorymap.shtml) \ No newline at end of file diff --git a/src/systems/aiie/roms.rs b/src/systems/aiie/roms.rs deleted file mode 100644 index 22cc00fc..00000000 --- a/src/systems/aiie/roms.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::roms::RomFile; - -/// The set of ROM files required to run a Commodore 64 system. -/// Roms from the ROMS directory in https://mirrors.apple2.org.za/ftp.apple.asimov.net/emulators/rom_images/ -pub struct AiieSystemRoms { - /// Character ROM. Used to generate the 7x8 character bitmaps. - pub character: RomFile, - - /// Firmware ROM. Contains text drawing and I/O routines. - pub firmware: RomFile, - - /// AppleSoft BASIC ROM. Contains the BASIC interpreter and editor. - pub applesoft: RomFile, - - /// Monitor ROM. Contains reset vectors and the system monitor. - pub monitor: RomFile, -} - -impl AiieSystemRoms { - #[cfg(not(target_arch = "wasm32"))] - pub fn from_disk() -> Self { - use crate::roms::DiskLoadable; - - let character = RomFile::from_file("aiie/character.bin"); - let firmware = RomFile::from_file("aiie/firmware.bin"); - let applesoft = RomFile::from_file("aiie/applesoft.bin"); - let monitor = RomFile::from_file("aiie/monitor.bin"); - - Self { - character, - firmware, - applesoft, - monitor, - } - } -} diff --git a/src/systems/aiie/switches.rs b/src/systems/aiie/switches.rs deleted file mode 100644 index 5b3f8733..00000000 --- a/src/systems/aiie/switches.rs +++ /dev/null @@ -1,631 +0,0 @@ -use std::{cell::Cell, io::Write, rc::Rc, sync::Arc}; - -use crate::{ - keyboard::{KeyAdapter, SymbolAdapter}, - memory::{ActiveInterrupt, Memory, SystemInfo}, - platform::PlatformProvider, - systems::aiie::keyboard::AppleIISymbolAdapter, -}; - -/// Implementation of the "soft switches" which control system parameters in the Apple IIe. -pub struct AiieSoftSwitches { - platform: Arc, - selectors: AiieBankSelectors, - - /// The most recent key pressed by the user. Used to detect changes. - previous_key: Option, - - /// Whether there is a keypress waiting for the user. - pub keypress_waiting: bool, - - /// Whether the system is using the "80 column" memory layout. - pub eighty_col_memory: bool, - - /// Whether any reads to RAM in the lower 48k bytes should go to main RAM or aux RAM. - pub read_aux_48k: bool, - /// Whether any writes to RAM in the lower 48k bytes should go to main RAM or aux RAM. - pub write_aux_48k: bool, - - /// Whether the external slot area should be mapped to external cards or ROM. - pub ext_slot_rom: bool, - - /// Whether the zero page, stack page, and upper RAM should be in main RAM or aux RAM. - pub aux_zeropage: bool, - - /// Not sure what this one does. - pub ext_slot_c3_rom: bool, - - /// Whether the display should be configured to display 40 columns or 80 columns. - pub eighty_col_display: bool, - - /// Whether an alternative character ROM should be used. - pub alt_characters: bool, - - /// Whether text or graphics mode should be used. - pub text_mode: bool, - - /// TODO - pub mixed_mode: bool, - - /// Selects the page used for text or graphics data. - pub text_page2: bool, - - /// Enables HiRes graphics. - pub hi_res: bool, - - /// Voice parameters for the Annunciator. - pub annunciator: (bool, bool, bool, bool), - - /// Control the mapping of RAM/ROM starting at 0xD000. - /// True: Read from RAM. False: Read from ROM. - pub bank_read_ram: bool, - - /// True: Write to RAM. False: Writes are no-ops. - pub bank_write_ram: bool, - - /// True: Select RAM bank 2. False: Select RAM bank 1. - pub bank_ram_select: bool, -} - -impl AiieSoftSwitches { - pub fn new(platform: Arc, selectors: AiieBankSelectors) -> Self { - Self { - platform, - selectors, - previous_key: None, - keypress_waiting: false, - eighty_col_memory: false, - read_aux_48k: false, - write_aux_48k: false, - ext_slot_rom: false, - aux_zeropage: false, - ext_slot_c3_rom: false, - eighty_col_display: false, - alt_characters: false, - text_mode: false, - mixed_mode: false, - text_page2: false, - hi_res: false, - annunciator: (false, false, false, false), - - bank_read_ram: false, - bank_write_ram: false, - bank_ram_select: false, - } - } - - /// Set or clear a softswitch value. - /// Each softswitch effectively has two addresses. - /// Writing to or reading from the address of a softswitch where the LSB is - /// set will toggle the softswitch ON, and if the LSB of the address is cleared - /// then the softswitch will be toggled OFF. - fn softswitch(&mut self, address: u16) { - let value = (address & 1) == 1; - - println!("softswitch {:02X} <- {}", address & !1, value); - - match address & !1 { - 0x00 => self.eighty_col_memory = value, - 0x02 => self.read_aux_48k = value, - 0x04 => self.write_aux_48k = value, - 0x06 => self.ext_slot_rom = value, - 0x08 => self.aux_zeropage = value, - 0x0A => self.ext_slot_c3_rom = value, - 0x0C => self.eighty_col_display = value, - 0x0E => self.alt_characters = value, - - 0x50 => self.text_mode = value, - 0x52 => self.mixed_mode = value, - 0x54 => self.text_page2 = value, - 0x56 => self.hi_res = value, - - 0x58 => self.annunciator.0 = value, - 0x5A => self.annunciator.1 = value, - 0x5C => self.annunciator.2 = value, - 0x5E => self.annunciator.3 = value, - - _ => todo!("unimplemented softswitch"), - }; - - self.selectors.clone().set(self); - } - - /// Read one of the "RD" locations. - fn read_flag(&mut self, address: u16) -> u8 { - let value = match address { - 0x11 => self.bank_ram_select, - 0x12 => self.bank_read_ram, - 0x13 => self.read_aux_48k, - 0x14 => self.write_aux_48k, - 0x15 => self.ext_slot_rom, - 0x16 => self.aux_zeropage, - 0x17 => self.ext_slot_c3_rom, - 0x18 => self.eighty_col_memory, - 0x19 => true, //todo!("RDVBLBAR: not VBL (VBL signal low)"), - 0x1A => self.text_mode, - 0x1B => self.mixed_mode, - 0x1C => self.text_page2, - 0x1D => self.hi_res, - 0x1E => self.alt_characters, - 0x1F => self.eighty_col_display, - - _ => todo!("unimplemented softswitch"), - }; - - if value { - 1 - } else { - 0 - } - } -} - -impl Memory for AiieSoftSwitches { - fn read(&mut self, address: u16) -> u8 { - match address % 0x100 { - 0x00 => { - let state = AppleIISymbolAdapter::map(&SymbolAdapter::map(&self.platform.get_key_state())); - let key = state.get_one_key(); - - if key != self.previous_key { - self.keypress_waiting = true; - } - self.previous_key = key; - - (self.keypress_waiting as u8) << 7 | key.unwrap_or(0) - } - 0x01..=0x0F | 0x50..=0x5F => { - self.softswitch(address); - 0 - } - 0x10 => { - self.keypress_waiting = false; - - let state = AppleIISymbolAdapter::map(&SymbolAdapter::map(&self.platform.get_key_state())); - let key = state.get_one_key(); - - (self.keypress_waiting as u8) << 7 | key.unwrap_or(0) - } - 0x11..=0x1F => self.read_flag(address) << 7, - 0x30 => { - print!("🔈"); - std::io::stdout().flush().unwrap(); - 0 - } - 0x61 => 0x00, //todo!("OPNAPPLE: open apple (command) key data"), - 0x62 => 0x00, //todo!("CLSAPPLE: closed apple (option) key data"), - 0x70 => 0x00, //todo!("PDLTRIG : trigger paddles"), - 0x77 => {println!("BLOSSOM"); 0}, //http://apple2.guidero.us/doku.php/mg_notes/general/io_page - 0x78 => {println!("Enable IOU"); 0}, - 0x79 => {println!("Disable IOU"); 0}, - - // These softswitches are used for the upper section of ROM/RAM (past 0xD000). - 0x80..=0x8F => { - self.bank_write_ram = (address & 0b0001) != 0; - self.bank_ram_select = (address & 0b1000) == 0; - - self.bank_read_ram = match address & 0b11 { - 0b00 => true, - 0b01 => false, - 0b10 => false, - 0b11 => true, - _ => unreachable!(), - }; - - self.selectors.clone().set(self); - - 0 - }, - 0x90..=0xFF => { - println!("IO Card Read: {:04X}", address); - 69 - }, - - _ => { println!("Read Address: {:04X}", address); 0} - //unimplemented!();}, - } - } - - fn write(&mut self, address: u16, _value: u8) { - match address % 0x100 { - 0x00..=0x0F | 0x50..=0x5F => self.softswitch(address), - 0x10 => { - self.keypress_waiting = false; - } - 0x11..=0x1F => (), - 0x30 => println!("SPEAKER : toggle speaker diaphragm"), - 0x61 => todo!("OPNAPPLE: open apple (command) key data"), - 0x62 => todo!("CLSAPPLE: closed apple (option) key data"), - 0x70 => todo!("PDLTRIG : trigger paddles"), - - 0x80..=0x8F => { - self.bank_write_ram = (address & 0b0001) != 0; - self.bank_ram_select = (address & 0b1000) == 0; - - self.bank_read_ram = match address & 0b11 { - 0b00 => true, - 0b01 => false, - 0b10 => false, - 0b11 => true, - _ => unreachable!(), - }; - - self.selectors.clone().set(self); - - - }, - 0x90..=0xFF => { - println!("IO Card write: {:04X} {:04X}", address, _value); - }, - - _ => { println!("Address: {:04X} Value: {:04X}", address, _value); - unimplemented!();}, - } - } - - fn reset(&mut self) { - // set all the flags to false - self.eighty_col_memory = false; - self.read_aux_48k = false; - self.write_aux_48k = false; - self.ext_slot_rom = false; - self.aux_zeropage = false; - self.ext_slot_c3_rom = false; - self.eighty_col_display = false; - self.alt_characters = false; - self.text_mode = false; - self.mixed_mode = false; - self.text_page2 = false; - self.hi_res = false; - self.annunciator = (false, false, false, false); - self.bank_read_ram = false; - self.bank_write_ram = false; - self.bank_ram_select = false; - } - - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> ActiveInterrupt { - ActiveInterrupt::None - } -} - -#[derive(Clone)] -pub struct AiieBankSelectors { - pub zp_stack: Rc>, - pub low_segment: Rc>, - pub after_stack: Rc>, - pub text_page_1: Rc>, - pub text_page_2: Rc>, - pub hires_page_1: Rc>, - pub hires_page_2: Rc>, - - pub ext_slot_rom: Rc>, - - pub rom_ram_select: Rc>, - pub upper_ram: Rc>, - pub ram_bank_select: Rc>, -} - -impl AiieBankSelectors { - pub fn new() -> AiieBankSelectors { - AiieBankSelectors { - zp_stack: Rc::new(Cell::new((0, 0))), - low_segment: Rc::new(Cell::new((0, 0))), - after_stack: Rc::new(Cell::new((0, 0))), - text_page_1: Rc::new(Cell::new((0, 0))), - text_page_2: Rc::new(Cell::new((0, 0))), - hires_page_1: Rc::new(Cell::new((0, 0))), - hires_page_2: Rc::new(Cell::new((0, 0))), - ext_slot_rom: Rc::new(Cell::new((0, 0))), - rom_ram_select: Rc::new(Cell::new((0, 0))), - upper_ram: Rc::new(Cell::new((0, 0))), - ram_bank_select: Rc::new(Cell::new((0, 0))), - } - } - - fn set(&mut self, switches: &AiieSoftSwitches) { - let selector_value = ( - switches.read_aux_48k as usize, - switches.write_aux_48k as usize, - ); - self.low_segment.set(selector_value); - self.text_page_1.set(selector_value); - self.text_page_2.set(selector_value); - self.hires_page_1.set(selector_value); - self.hires_page_2.set(selector_value); - - if switches.eighty_col_memory { - if switches.text_page2 { - self.text_page_1.set((1, 1)); - } else { - self.text_page_1.set((0, 0)); - } - - if switches.hi_res { - if switches.text_page2 { - self.hires_page_1.set((1, 1)); - } else { - self.hires_page_1.set((0, 0)); - } - } - } - - if switches.ext_slot_rom { - self.ext_slot_rom.set((1, 1)); - } - if !switches.ext_slot_rom && switches.ext_slot_c3_rom{ - self.ext_slot_rom.set((0, 0)); - } - if !switches.ext_slot_rom && !switches.ext_slot_c3_rom{ - self.ext_slot_rom.set((1, 1)); - } - - self.rom_ram_select.set(( - switches.bank_read_ram as usize, - switches.bank_write_ram as usize, - )); - - if switches.aux_zeropage { - self.zp_stack.set((1, 1)); - self.upper_ram.set((1, 1)); - } else { - self.zp_stack.set((0, 0)); - self.upper_ram.set((0, 0)); - } - - if switches.bank_ram_select { - self.ram_bank_select.set((1, 1)); - } else { - self.ram_bank_select.set((0, 0)); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::platform::{Platform, TextPlatform}; - use std::sync::Arc; - - // http://www.apple-iigs.info/doc/fichiers/Apple%20IIe%20Technical%20Notes.pdf - #[test] - fn fig4_left() { - for hires in 0..=1 { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.softswitch(0x00); // 80STORE OFF - switches.softswitch(0x54); // PAGE2 OFF (should also be on) - switches.softswitch(0x56 + hires); // HIRES OFF or ON - switches.softswitch(0x02); // RAMRD OFF - switches.softswitch(0x04); // RAMWRT OFF - - assert_eq!(selectors.zp_stack.get(), (0, 0)); - assert_eq!(selectors.low_segment.get(), (0, 0)); - assert_eq!(selectors.text_page_1.get(), (0, 0)); - assert_eq!(selectors.text_page_2.get(), (0, 0)); - assert_eq!(selectors.hires_page_1.get(), (0, 0)); - assert_eq!(selectors.hires_page_2.get(), (0, 0)); - - switches.softswitch(0x01); // 80STORE ON - switches.softswitch(0x54); // PAGE2 OFF - switches.softswitch(0x56 + hires); // HIRES OFF or ON - switches.softswitch(0x02); // RAMRD OFF - switches.softswitch(0x04); // RAMWRT OFF - - assert_eq!(selectors.zp_stack.get(), (0, 0)); - assert_eq!(selectors.low_segment.get(), (0, 0)); - assert_eq!(selectors.text_page_1.get(), (0, 0)); - assert_eq!(selectors.text_page_2.get(), (0, 0)); - assert_eq!(selectors.hires_page_1.get(), (0, 0)); - assert_eq!(selectors.hires_page_2.get(), (0, 0)); - } - } - - #[test] - fn fig4_right() { - for hires in 0..=1 { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.softswitch(0x00); // 80STORE OFF - switches.softswitch(0x54); // PAGE2 OFF (should also be on) - switches.softswitch(0x56 + hires); // HIRES OFF or ON - switches.softswitch(0x03); // RAMRD ON - switches.softswitch(0x05); // RAMWRT ON - - assert_eq!(selectors.zp_stack.get(), (0, 0)); - assert_eq!(selectors.low_segment.get(), (1, 1)); - assert_eq!(selectors.text_page_1.get(), (1, 1)); - assert_eq!(selectors.text_page_2.get(), (1, 1)); - assert_eq!(selectors.hires_page_1.get(), (1, 1)); - assert_eq!(selectors.hires_page_2.get(), (1, 1)); - - switches.softswitch(0x01); // 80STORE ON - switches.softswitch(0x55); // PAGE2 ON - switches.softswitch(0x56 + hires); // HIRES OFF or ON - switches.softswitch(0x03); // RAMRD ON - switches.softswitch(0x05); // RAMWRT ON - - assert_eq!(selectors.zp_stack.get(), (0, 0)); - assert_eq!(selectors.low_segment.get(), (1, 1)); - assert_eq!(selectors.text_page_1.get(), (1, 1)); - assert_eq!(selectors.text_page_2.get(), (1, 1)); - assert_eq!(selectors.hires_page_1.get(), (1, 1)); - assert_eq!(selectors.hires_page_2.get(), (1, 1)); - } - } - - #[test] - fn fig5_left() { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.softswitch(0x01); // 80STORE ON - switches.softswitch(0x55); // PAGE2 ON - switches.softswitch(0x56); // HIRES OFF - switches.softswitch(0x02); // RAMRD OFF - switches.softswitch(0x04); // RAMWRT OFF - - assert_eq!(selectors.zp_stack.get(), (0, 0)); - assert_eq!(selectors.low_segment.get(), (0, 0)); - assert_eq!(selectors.text_page_1.get(), (1, 1)); - assert_eq!(selectors.text_page_2.get(), (0, 0)); - assert_eq!(selectors.hires_page_1.get(), (0, 0)); - assert_eq!(selectors.hires_page_2.get(), (0, 0)); - } - - #[test] - fn fig5_right() { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.softswitch(0x01); // 80STORE ON - switches.softswitch(0x55); // PAGE2 ON - switches.softswitch(0x57); // HIRES ON - switches.softswitch(0x02); // RAMRD OFF - switches.softswitch(0x04); // RAMWRT OFF - - assert_eq!(selectors.zp_stack.get(), (0, 0)); - assert_eq!(selectors.low_segment.get(), (0, 0)); - assert_eq!(selectors.text_page_1.get(), (1, 1)); - assert_eq!(selectors.text_page_2.get(), (0, 0)); - assert_eq!(selectors.hires_page_1.get(), (1, 1)); - assert_eq!(selectors.hires_page_2.get(), (0, 0)); - } - - #[test] - fn fig6_left() { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.softswitch(0x01); // 80STORE ON - switches.softswitch(0x54); // PAGE2 OFF - switches.softswitch(0x56); // HIRES OFF - switches.softswitch(0x03); // RAMRD ON - switches.softswitch(0x05); // RAMWRT ON - - assert_eq!(selectors.zp_stack.get(), (0, 0)); - assert_eq!(selectors.low_segment.get(), (1, 1)); - assert_eq!(selectors.text_page_1.get(), (0, 0)); - assert_eq!(selectors.text_page_2.get(), (1, 1)); - assert_eq!(selectors.hires_page_1.get(), (1, 1)); - assert_eq!(selectors.hires_page_2.get(), (1, 1)); - } - - #[test] - fn fig6_right() { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.softswitch(0x01); // 80STORE ON - switches.softswitch(0x54); // PAGE2 OFF - switches.softswitch(0x57); // HIRES ON - switches.softswitch(0x03); // RAMRD ON - switches.softswitch(0x05); // RAMWRT ON - - assert_eq!(selectors.zp_stack.get(), (0, 0)); - assert_eq!(selectors.low_segment.get(), (1, 1)); - assert_eq!(selectors.text_page_1.get(), (0, 0)); - assert_eq!(selectors.text_page_2.get(), (1, 1)); - assert_eq!(selectors.hires_page_1.get(), (0, 0)); - assert_eq!(selectors.hires_page_2.get(), (1, 1)); - } - - // http://www.applelogic.org/files/AIIETECHREF2.pdf - #[test] - fn test_c08x() { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.read(0x80); - assert_eq!(selectors.rom_ram_select.get(), (1, 0)); - assert_eq!(selectors.ram_bank_select.get(), (1, 1)); - - switches.read(0x81); - assert_eq!(selectors.rom_ram_select.get(), (0, 1)); - assert_eq!(selectors.ram_bank_select.get(), (1, 1)); - - switches.read(0x82); - assert_eq!(selectors.rom_ram_select.get(), (0, 0)); - assert_eq!(selectors.ram_bank_select.get(), (1, 1)); - - switches.read(0x83); - assert_eq!(selectors.rom_ram_select.get(), (1, 1)); - assert_eq!(selectors.ram_bank_select.get(), (1, 1)); - - switches.read(0x88); - assert_eq!(selectors.rom_ram_select.get(), (1, 0)); - assert_eq!(selectors.ram_bank_select.get(), (0, 0)); - - switches.read(0x89); - assert_eq!(selectors.rom_ram_select.get(), (0, 1)); - assert_eq!(selectors.ram_bank_select.get(), (0, 0)); - - switches.read(0x8A); - assert_eq!(selectors.rom_ram_select.get(), (0, 0)); - assert_eq!(selectors.ram_bank_select.get(), (0, 0)); - - switches.read(0x8B); - assert_eq!(selectors.rom_ram_select.get(), (1, 1)); - assert_eq!(selectors.ram_bank_select.get(), (0, 0)); - } - - #[test] - fn test_rdbnk2() { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.read(0x80); - assert_eq!(switches.read(0x11) & 0x80, 0x80); - switches.read(0x88); - assert_eq!(switches.read(0x11) & 0x80, 0x00); - } - - #[test] - fn test_rdlcram() { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.read(0x80); - assert_eq!(switches.read(0x12) & 0x80, 0x80); - switches.read(0x81); - assert_eq!(switches.read(0x12) & 0x80, 0x00); - } - - #[test] - fn test_altzp() { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.softswitch(0x08); - assert_eq!(selectors.zp_stack.get(), (0, 0)); - assert_eq!(selectors.upper_ram.get(), (0, 0)); - - switches.softswitch(0x09); - assert_eq!(selectors.zp_stack.get(), (1, 1)); - assert_eq!(selectors.upper_ram.get(), (1, 1)); - } - - #[test] - fn test_rdaltzp() { - let platform = Arc::new(TextPlatform::new()); - let selectors = AiieBankSelectors::new(); - let mut switches = AiieSoftSwitches::new(platform.provider(), selectors.clone()); - - switches.softswitch(0x08); - assert_eq!(switches.read(0x16) & 0x80, 0x00); - - switches.softswitch(0x09); - assert_eq!(switches.read(0x16) & 0x80, 0x80); - } -} diff --git a/src/systems/applei/keyboard.rs b/src/systems/applei/keyboard.rs new file mode 100644 index 00000000..de8d21d8 --- /dev/null +++ b/src/systems/applei/keyboard.rs @@ -0,0 +1,475 @@ +use serde::{Deserialize, Serialize}; + +use crate::keyboard::{KeyAdapter, KeyPosition, KeyState, KeySymbol, VirtualKey}; + +/// The keys found on the Applei's "Graphics" keyboard. +/// Source: +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum AppleiKeys { + Exclamation, + DoubleQuote, + Hash, + Dollar, + Percent, + Apostrophe, + Ampersand, + Backslash, + LeftParen, + RightParen, + LeftArrow, // doesn't move cursor, literally types "<-" + + Q, + W, + E, + R, + T, + Y, + U, + I, + O, + P, + UpArrow, // literally types "^" + + A, + S, + D, + F, + G, + H, + J, + K, + L, + Colon, + Return, + + Z, + X, + C, + V, + B, + N, + M, + Comma, + Semicolon, + Question, + + LShift, + Reverse, + At, + LeftBracket, + RightBracket, + Space, + LessThan, + GreaterThan, + RunStop, + RShift, + + ClrHome, + CursorUpDown, + CursorLeftRight, + InsertDelete, + Num7, + Num8, + Num9, + NumDivide, + Num4, + Num5, + Num6, + NumMultiply, + Num1, + Num2, + Num3, + NumPlus, + Num0, + NumPeriod, + NumMinus, + NumEquals, + + Unused, +} + +/// The keyboard matrix for the Applei's "Graphics" keyboard. +/// Source: +pub const KEYBOARD_MAPPING: [[AppleiKeys; 8]; 10] = { + use AppleiKeys::*; + + [ + [ + Exclamation, + Hash, + Percent, + Ampersand, + LeftParen, + LeftArrow, + ClrHome, + CursorLeftRight, + ], + [ + DoubleQuote, + Dollar, + Apostrophe, + Backslash, + RightParen, + Unused, + CursorUpDown, + InsertDelete, + ], + [Q, E, T, U, O, UpArrow, Num7, Num9], + [W, R, Y, I, P, Unused, Num8, NumDivide], + [A, D, G, J, L, Unused, Num4, Num6], + [S, F, H, K, Colon, Unused, Num5, NumMultiply], + [Z, C, B, M, Semicolon, Return, Num1, Num3], + [X, V, N, Comma, Question, Unused, Num2, NumPlus], + [ + LShift, + At, + RightBracket, + Unused, + GreaterThan, + RShift, + Num0, + NumMinus, + ], + [ + Reverse, + LeftBracket, + Space, + LessThan, + RunStop, + Unused, + NumPeriod, + NumEquals, + ], + ] +}; + +/// An adapter that maps standard keyboard positions to keys on the Applei's "Graphics" keyboard. +pub struct AppleiKeyboardAdapter; + +impl KeyAdapter for AppleiKeyboardAdapter { + fn map(state: &KeyState) -> KeyState { + let mut mapped = KeyState::new(); + + for symbol in state.pressed() { + use KeyPosition::*; + + mapped.press(match symbol { + Digit0 => AppleiKeys::Num0, + Digit1 => AppleiKeys::Num1, + Digit2 => AppleiKeys::Num2, + Digit3 => AppleiKeys::Num3, + Digit4 => AppleiKeys::Num4, + Digit5 => AppleiKeys::Num5, + Digit6 => AppleiKeys::Num6, + Digit7 => AppleiKeys::Num7, + Digit8 => AppleiKeys::Num8, + Digit9 => AppleiKeys::Num9, + + A => AppleiKeys::A, + B => AppleiKeys::B, + C => AppleiKeys::C, + D => AppleiKeys::D, + E => AppleiKeys::E, + F => AppleiKeys::F, + G => AppleiKeys::G, + H => AppleiKeys::H, + I => AppleiKeys::I, + J => AppleiKeys::J, + K => AppleiKeys::K, + L => AppleiKeys::L, + M => AppleiKeys::M, + N => AppleiKeys::N, + O => AppleiKeys::O, + P => AppleiKeys::P, + Q => AppleiKeys::Q, + R => AppleiKeys::R, + S => AppleiKeys::S, + T => AppleiKeys::T, + U => AppleiKeys::U, + V => AppleiKeys::V, + W => AppleiKeys::W, + X => AppleiKeys::X, + Y => AppleiKeys::Y, + Z => AppleiKeys::Z, + + Minus => AppleiKeys::NumMinus, + Equals => AppleiKeys::NumEquals, + LeftBracket => AppleiKeys::LeftBracket, + RightBracket => AppleiKeys::RightBracket, + Backslash => AppleiKeys::Backslash, + Semicolon => AppleiKeys::Semicolon, + Apostrophe => AppleiKeys::Apostrophe, + Enter => AppleiKeys::Return, + + LShift => AppleiKeys::LShift, + RShift => AppleiKeys::RShift, + Comma => AppleiKeys::Comma, + Period => AppleiKeys::NumPeriod, + Slash => AppleiKeys::NumDivide, + + Space => AppleiKeys::Space, + Backspace => AppleiKeys::InsertDelete, + + _ => continue, + }) + } + + mapped + } +} + +/// An adapter that maps keyboard symbols to keys on the Applei's "Graphics" keyboard. +pub struct AppleiSymbolAdapter; + +impl KeyAdapter for AppleiSymbolAdapter { + fn map(state: &KeyState) -> KeyState { + let mut mapped = KeyState::new(); + + if state.is_pressed(KeySymbol::UpArrow) || state.is_pressed(KeySymbol::LeftArrow) { + mapped.press(AppleiKeys::LShift); + + if state.is_pressed(KeySymbol::UpArrow) { + mapped.press(AppleiKeys::CursorUpDown); + } + if state.is_pressed(KeySymbol::LeftArrow) { + mapped.press(AppleiKeys::CursorLeftRight); + } + + return mapped; + } + + for symbol in state.pressed() { + use KeySymbol::*; + + mapped.press(match symbol { + Char('!') => AppleiKeys::Exclamation, + Char('"') => AppleiKeys::DoubleQuote, + Char('#') => AppleiKeys::Hash, + Char('$') => AppleiKeys::Dollar, + Char('%') => AppleiKeys::Percent, + Char('\'') => AppleiKeys::Apostrophe, + Char('&') => AppleiKeys::Ampersand, + Char('(') => AppleiKeys::LeftParen, + Char(')') => AppleiKeys::RightParen, + // TODO: Left Arrow + Home => AppleiKeys::ClrHome, + DownArrow => AppleiKeys::CursorUpDown, + RightArrow => AppleiKeys::CursorLeftRight, + Backspace => AppleiKeys::InsertDelete, + + Char('q') | Char('Q') => AppleiKeys::Q, + Char('w') | Char('W') => AppleiKeys::W, + Char('e') | Char('E') => AppleiKeys::E, + Char('r') | Char('R') => AppleiKeys::R, + Char('t') | Char('T') => AppleiKeys::T, + Char('y') | Char('Y') => AppleiKeys::Y, + Char('u') | Char('U') => AppleiKeys::U, + Char('i') | Char('I') => AppleiKeys::I, + Char('o') | Char('O') => AppleiKeys::O, + Char('p') | Char('P') => AppleiKeys::P, + Char('^') => AppleiKeys::UpArrow, + + Char('7') => AppleiKeys::Num7, + Char('8') => AppleiKeys::Num8, + Char('9') => AppleiKeys::Num9, + Char('/') => AppleiKeys::NumDivide, + + Char('a') | Char('A') => AppleiKeys::A, + Char('s') | Char('S') => AppleiKeys::S, + Char('d') | Char('D') => AppleiKeys::D, + Char('f') | Char('F') => AppleiKeys::F, + Char('g') | Char('G') => AppleiKeys::G, + Char('h') | Char('H') => AppleiKeys::H, + Char('j') | Char('J') => AppleiKeys::J, + Char('k') | Char('K') => AppleiKeys::K, + Char('l') | Char('L') => AppleiKeys::L, + Char(':') => AppleiKeys::Colon, + Return => AppleiKeys::Return, + + Char('4') => AppleiKeys::Num4, + Char('5') => AppleiKeys::Num5, + Char('6') => AppleiKeys::Num6, + Char('*') => AppleiKeys::NumMultiply, + + Char('z') | Char('Z') => AppleiKeys::Z, + Char('x') | Char('X') => AppleiKeys::X, + Char('c') | Char('C') => AppleiKeys::C, + Char('v') | Char('V') => AppleiKeys::V, + Char('b') | Char('B') => AppleiKeys::B, + Char('n') | Char('N') => AppleiKeys::N, + Char('m') | Char('M') => AppleiKeys::M, + Char(',') => AppleiKeys::Comma, + Char(';') => AppleiKeys::Semicolon, + Char('?') => AppleiKeys::Question, + + Char('1') => AppleiKeys::Num1, + Char('2') => AppleiKeys::Num2, + Char('3') => AppleiKeys::Num3, + Char('+') => AppleiKeys::NumPlus, + + LAlt => AppleiKeys::LShift, // Map Alt to Shift since "shift" actually does graphics characters. + // TODO: Reverse + Char('@') => AppleiKeys::At, + Char('[') => AppleiKeys::LeftBracket, + Char(']') => AppleiKeys::RightBracket, + Char(' ') => AppleiKeys::Space, + Char('<') => AppleiKeys::LessThan, + Char('>') => AppleiKeys::GreaterThan, + Interrupt => AppleiKeys::RunStop, + RAlt => AppleiKeys::RShift, + + Char('0') => AppleiKeys::Num0, + Char('.') => AppleiKeys::NumPeriod, + Char('-') => AppleiKeys::NumMinus, + Char('=') => AppleiKeys::NumEquals, + + _ => continue, + }) + } + + mapped + } +} + +pub struct AppleiVirtualAdapter; + +impl KeyAdapter for AppleiVirtualAdapter { + fn map(state: &KeyState) -> KeyState { + let mut mapped = KeyState::new(); + + for symbol in state.pressed() { + /*if let VirtualKey::CommodoreApplei(symbol) = symbol { + mapped.press(*symbol); + }*/ + } + + mapped + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_positional() { + let mut state = KeyState::::new(); + + state.press(KeyPosition::A); + state.press(KeyPosition::Apostrophe); + state.press(KeyPosition::Digit9); + state.press(KeyPosition::Semicolon); + + let mapped = AppleiKeyboardAdapter::map(&state); + + assert_eq!( + vec![ + &AppleiKeys::A, + &AppleiKeys::Apostrophe, + &AppleiKeys::Num9, + &AppleiKeys::Semicolon, + ], + mapped.pressed().collect::>() + ); + } + + #[test] + fn test_symbolic() { + let mut state = KeyState::::new(); + + state.press(KeySymbol::Char('a')); + state.press(KeySymbol::Char('\'')); + state.press(KeySymbol::Char('9')); + + let mapped = AppleiSymbolAdapter::map(&state); + + assert_eq!( + vec![&AppleiKeys::A, &AppleiKeys::Apostrophe, &AppleiKeys::Num9,], + mapped.pressed().collect::>() + ); + } + + #[test] + fn test_symbolic_unshifting() { + let mut state = KeyState::::new(); + + state.press(KeySymbol::Char('"')); + + let mapped = AppleiSymbolAdapter::map(&state); + + assert_eq!( + vec![&AppleiKeys::DoubleQuote], + mapped.pressed().collect::>() + ); + } + + #[test] + fn test_alt_to_shift() { + let mut state = KeyState::::new(); + + state.press(KeySymbol::LAlt); + state.press(KeySymbol::Char('a')); + + let mapped = AppleiSymbolAdapter::map(&state); + assert_eq!( + vec![&AppleiKeys::LShift, &AppleiKeys::A], + mapped.pressed().collect::>() + ); + + state.release(KeySymbol::LAlt); + state.press(KeySymbol::RAlt); + + let mapped = AppleiSymbolAdapter::map(&state); + assert_eq!( + vec![&AppleiKeys::A, &AppleiKeys::RShift], + mapped.pressed().collect::>() + ); + } + + #[test] + fn test_cursor_keys() { + let mut state = KeyState::::new(); + + state.press(KeySymbol::DownArrow); + assert_eq!( + vec![&AppleiKeys::CursorUpDown], + AppleiSymbolAdapter::map(&state) + .pressed() + .collect::>() + ); + + state.release(KeySymbol::DownArrow); + state.press(KeySymbol::UpArrow); + assert_eq!( + vec![&AppleiKeys::LShift, &AppleiKeys::CursorUpDown], + AppleiSymbolAdapter::map(&state) + .pressed() + .collect::>() + ); + + state.press(KeySymbol::LeftArrow); + assert_eq!( + vec![ + &AppleiKeys::LShift, + &AppleiKeys::CursorUpDown, + &AppleiKeys::CursorLeftRight, + ], + AppleiSymbolAdapter::map(&state) + .pressed() + .collect::>() + ); + + // map the up and left arrow, but give up on the right + state.press(KeySymbol::RightArrow); + assert_eq!( + vec![ + &AppleiKeys::LShift, + &AppleiKeys::CursorUpDown, + &AppleiKeys::CursorLeftRight + ], + AppleiSymbolAdapter::map(&state) + .pressed() + .collect::>() + ); + } +} diff --git a/src/systems/applei/mod.rs b/src/systems/applei/mod.rs new file mode 100644 index 00000000..1022caed --- /dev/null +++ b/src/systems/applei/mod.rs @@ -0,0 +1,160 @@ +use crate::cpu::{MemoryIO, Mos6502, Mos6502Variant}; +use crate::keyboard::{KeyAdapter, KeyMappingStrategy, SymbolAdapter}; +use crate::memory::mos652x::{Pia, Via}; +use crate::memory::{BlockMemory, BranchMemory, NullMemory, NullPort, Port, SystemInfo}; +use crate::platform::{Color, PlatformProvider, TextPlatform, WindowConfig}; +use crate::systems::{System, SystemBuilder}; +use crate::trace::TraceHandler; +use instant::Instant; +use std::cell::Cell; +use std::rc::Rc; +use std::sync::Arc; +use std::time::Duration; +mod roms; +pub use roms::AppleiSystemRoms; +mod keyboard; +//pub use keyboard::AppleiKeys; +use std::io::stdout; + +//use self::keyboard::AppleiVirtualAdapter; + +const WIDTH: u32 = 40; +const HEIGHT: u32 = 24; +const CHAR_WIDTH: u32 = 5; +const CHAR_HEIGHT: u32 = 8; + +const KBD_REG: u16 = 0xD010; +const KBDCTR_REG: u16 = 0xD011; +const DSP_REG: u16 = 0xD012; +const DSPCTR_REG: u16 = 0xD013; + +//const VRAM_SIZE: usize = WIDTH * HEIGHT * CHAR_WIDTH * CHAR_HEIGHT; //that's multiplication babey! + +pub struct KBD { + ascii_code: Rc>, + new_data: bool, +} +impl KBD { + pub fn new() -> Self { + Self { + ascii_code: Rc::new(Cell::new(0x4D)), + new_data: false, + } + } +} + +impl Port for KBD { + fn read(&mut self) -> u8 { + self.new_data = false; + //self.ascii_code.get() //what is this, java? + 0xC0 + } + + fn write(&mut self, value: u8) { + self.new_data = true; + self.ascii_code.set(value); + } + + fn poll(&mut self, _cycles: u32, info: &SystemInfo) -> bool { + self.new_data + } + + fn reset(&mut self) { + self.ascii_code.set(0); + } +} + +pub struct DSP { + ascii_code: Rc>, + new_data: bool, +} +impl DSP { + pub fn new() -> Self { + Self { + ascii_code: Rc::new(Cell::new(0)), + new_data: false, + } + } +} +impl Port for DSP { + fn read(&mut self) -> u8 { + self.new_data = false; + self.ascii_code.get() //what is this, java? + } + + fn write(&mut self, value: u8) { + self.new_data = true; + self.ascii_code.set(value); + //not sure we need this + } + + fn poll(&mut self, _cycles: u32, info: &SystemInfo) -> bool { + self.new_data + } + + fn reset(&mut self) { + self.ascii_code.set(0); + } +} + +/// Configuration for an Apple I system. +pub struct AppleiSystemConfig { + pub mapping: KeyMappingStrategy, +} + +/// A factory for the Apple I +pub struct AppleiSystemBuilder; + +impl SystemBuilder for AppleiSystemBuilder { + fn build( + roms: AppleiSystemRoms, + config: AppleiSystemConfig, + platform: Arc, + ) -> Box { + platform.request_window(WindowConfig::new( + WIDTH * CHAR_WIDTH, + HEIGHT * CHAR_HEIGHT, + 2.0, + )); + + let ram = BlockMemory::ram(0x1000); + let wozmon = BlockMemory::from_file(0x100, roms.wozmon); + + let KBD = KBD::new(); + let DSP = DSP::new(); + let pia = Pia::new(Box::new(KBD), Box::new(DSP)); + + let memory = BranchMemory::new() + .map(0x0000, ram) + .map(0xD010, pia) + .map(0xFF00, wozmon); + + let cpu = Mos6502::new(memory, Mos6502Variant::NMOS); + Box::new(AppleiSystem { cpu }) + } +} + +/// The Apple I system. +pub struct AppleiSystem { + cpu: Mos6502, +} + +impl System for AppleiSystem { + fn attach_trace_handler(&mut self, handler: Box) { + self.cpu.attach_trace_handler(handler); + } + + fn tick(&mut self) -> Duration { + Duration::from_secs_f64(1.0 / 1_022_727.0) * self.cpu.tick() as u32 + } + + fn reset(&mut self) { + self.cpu.reset(); + } + + fn render(&mut self, framebuffer: &mut [u8], config: WindowConfig) { + if (self.cpu.read(DSPCTR_REG) & (1 << 7)) > 0 { + print!("{}", self.cpu.read(DSP_REG) as char); + } + } +} diff --git a/src/systems/applei/roms.rs b/src/systems/applei/roms.rs new file mode 100644 index 00000000..26f177b1 --- /dev/null +++ b/src/systems/applei/roms.rs @@ -0,0 +1,39 @@ +use crate::roms::RomFile; + +#[cfg(target_arch = "wasm32")] +use js_sys::{Reflect, Uint8Array}; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::{JsCast, JsValue}; + +/// The set of ROM files required to run an Apple I system. +pub struct AppleiSystemRoms { + /// The entire 256 byte system monitor + /// All hail woz, etc, etc. + pub wozmon: RomFile, +} + +impl AppleiSystemRoms { + #[cfg(not(target_arch = "wasm32"))] + pub fn from_disk() -> Self { + use crate::roms::DiskLoadable; + + let wozmon = RomFile::from_file("applei/wozmon.bin"); + + Self { wozmon } + } + + #[cfg(target_arch = "wasm32")] + pub fn from_jsvalue(value: &JsValue) -> Self { + use crate::roms::JsValueLoadable; + + let wozmon = Reflect::get(value, &JsValue::from_str("char")) + .unwrap() + .dyn_into::() + .unwrap(); + + Self { + wozmon: RomFile::from_uint8array(&wozmon), + } + } +} diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 0319df8c..b2a4c41d 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -5,7 +5,7 @@ use crate::{ use instant::Duration; use std::sync::Arc; -pub mod aiie; +pub mod applei; pub mod basic; pub mod c64; pub mod easy; diff --git a/www/src/Emulator.tsx b/www/src/Emulator.tsx index f619666d..3b42d876 100644 --- a/www/src/Emulator.tsx +++ b/www/src/Emulator.tsx @@ -13,7 +13,7 @@ const Emulator = forwardRef( className?: string; onReady?: (instance: Noentiendo) => void; }, - ref + ref, ) => { const instance = useRef(); const canvas = useRef(null); @@ -44,7 +44,7 @@ const Emulator = forwardRef( }); return ; - } + }, ); export default Emulator; diff --git a/www/src/main.tsx b/www/src/main.tsx index 02055fd7..8b1ddb97 100644 --- a/www/src/main.tsx +++ b/www/src/main.tsx @@ -6,5 +6,5 @@ import "./index.css"; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + , );