mos_hardware/c64.rs
1// copyright 2022 mikael lund aka wombat
2//
3// licensed under the apache license, version 2.0 (the "license");
4// you may not use this file except in compliance with the license.
5// you may obtain a copy of the license at
6//
7// http://www.apache.org/licenses/license-2.0
8//
9// unless required by applicable law or agreed to in writing, software
10// distributed under the license is distributed on an "as is" basis,
11// without warranties or conditions of any kind, either express or implied.
12// see the license for the specific language governing permissions and
13// limitations under the license.
14
15//! Commodore 64 support.
16//!
17//! Future information may be incorporated using the
18//! [Ultimate Commodore 64 Reference](https://github.com/mist64/c64ref)
19
20use crate::cia::*;
21use crate::sid::*;
22use crate::vic2::*;
23use bitflags::bitflags;
24use volatile_register::RW;
25
26bitflags! {
27 /// Control flags for the CPU port `R6510` at 0x0001
28 ///
29 /// Three-word combination constants like `RAM_IO_KERNAL` refer to banking configurations
30 /// of what is visible at addresses `$A000-BFFF`, `$D000-DFFF`, and `$E000-FFFF`.
31 /// Regardless of `0x0001`, the VIC-II chip *always* sees the `CHARROM` at `$1000-1FFF` and `$9000-9FFF`,
32 /// and RAM everywhere else.
33 ///
34 /// [More information](https://codebase64.org/doku.php?id=base:memory_management).
35 ///
36 /// # Examples
37 ///
38 /// Here's an example that makes the RAM available "under" both the BASIC and KERNAL
39 /// ROMs located at 0xA000-0xBFFF and 0xE000-0xFFFF.
40 /// The VIC, SID, and CIA I/O devices are left accessible at 0xD000-0xDFFF:
41 /// ~~~
42 /// (*CPU_PORT).write(CpuPortFlags::RAM_IO_RAM);
43 /// assert_eq!(CpuPortFlags::RAM_IO_RAM.bits(), 0x35);
44 /// assert_eq!(CpuPortFlags::RAM_IO_KERNAL.bitw(), 0x36);
45 /// ~~~
46 pub struct CpuPortFlags: u8 {
47 const DEFAULT = Self::BASIC_IO_KERNAL.bits;
48 const BASIC_IO_KERNAL = 0b00110111;
49 const RAM_RAM_RAM = 0b00110000;
50 const RAM_CHAR_RAM = 0b00110001;
51 const RAM_CHAR_KERNAL = 0b00110010;
52 const BASIC_CHAR_KERNAL = 0b00110011;
53 const RAM_IO_RAM = 0b00110101;
54 const RAM_IO_KERNAL = 0b00110110;
55 const DATASETTE_SIGNAL = 0b00001000; // bit 3
56 const DATASETTE_BUTTON_OFF = 0b00010000; // bit 4
57 const DATASETTE_MOTOR_OFF = 0b00100000; // bit 5
58 }
59}
60
61/// Pointer to the `R6510` register for 6510 I/O (0x0001)
62pub const CPU_PORT: *mut RW<CpuPortFlags> = (0x0001) as *mut RW<CpuPortFlags>;
63
64/// Pointer to beginning of default video memory (0x0400)
65pub const DEFAULT_VIDEO_MEMORY: *mut u8 = (0x0400) as *mut u8;
66
67/// Pointer to the default video matrix area (0x0400)
68///
69/// The video matrix is where text screen characters are stored in RAM.
70/// By default this corresponds to 25 lines, each with 40 columns.
71pub const DEFAULT_VIDEO_MATRIX: *mut [u8; 25 * 40] = (0x0400) as *mut [u8; 25 * 40];
72
73/// Default sprite shape pointers (0x0400)
74///
75/// This is the default location for sprite shape pointers, i.e.
76/// relative to the default screen memory location 0x0400.
77/// Individual sprite shape pointers can be calculated with
78/// `vic2::to_sprite_pointer()`.
79pub const DEFAULT_SPRITE_PTR: [*mut u8; 8] = [
80 (0x0400 + 0x3F8) as *mut u8,
81 (0x0400 + 0x3F8 + 1) as *mut u8,
82 (0x0400 + 0x3F8 + 2) as *mut u8,
83 (0x0400 + 0x3F8 + 3) as *mut u8,
84 (0x0400 + 0x3F8 + 4) as *mut u8,
85 (0x0400 + 0x3F8 + 5) as *mut u8,
86 (0x0400 + 0x3F8 + 6) as *mut u8,
87 (0x0400 + 0x3F8 + 7) as *mut u8,
88];
89
90/// Default upper case font in the CHARROM (0x1000)
91pub const DEFAULT_UPPERCASE_FONT: *mut u8 = (0x1000) as *mut u8;
92
93/// Default mixed case font in the CHARROM (0x1800)
94pub const DEFAULT_MIXEDCASE_FONT: *mut u8 = (0x1800) as *mut u8;
95
96/// Pointer to BASIC ROM start (0xa000)
97pub const BASIC_ROM: *mut u8 = (0xa000) as *mut u8;
98
99/// Pointer to BASIC ROM area (0xa000 - 0xbfff)
100pub const BASIC_ROM_AREA: *mut [u8; 0x1fff] = (0xa000) as *mut [u8; 0x1fff];
101
102/// Pointer to the video interface controller (0xd000)
103pub const VIC: *const MOSVideoInterfaceControllerII =
104 (0xd000) as *const MOSVideoInterfaceControllerII;
105
106/// Pointer to the sound interface device (0xd400)
107pub const SID: *const MOSSoundInterfaceDevice = (0xd400) as *const MOSSoundInterfaceDevice;
108
109/// Pointer to default color RAM (0xd800)
110pub const COLOR_RAM: *mut u8 = (0xd800) as *mut u8;
111
112/// Pointer to first complex interface adapter (0xdc00)
113pub const CIA1: *const MOSComplexInterfaceAdapter6526 =
114 (0xdc00) as *const MOSComplexInterfaceAdapter6526;
115
116/// Pointer to second complex interface adapter (0xdd00)
117pub const CIA2: *const MOSComplexInterfaceAdapter6526 =
118 (0xdd00) as *const MOSComplexInterfaceAdapter6526;
119
120/// Pointer to the KERNAL ROM memory area (0xe000 - 0xffff)
121pub const KERNAL_ROM: *mut [u8; 8192] = (0xe000) as *mut [u8; 8192];
122
123bitflags! {
124 /// Flags for the `CIA1::control_a` register (0xdc0e)
125 pub struct CIA1ControlAFlags: u8 {
126 /// Start (1) or stop (0) timer A
127 const START = 0b00000001; // bit 0
128 const PBON = 0b00000010;
129 const OUTMODE = 0b00000100;
130 const RUNMODE = 0b00001000;
131 const FORCE_LOAD = 0b00010000;
132 const INMODE = 0b00100000;
133 const SERIAL_OUTPUT = 0b01000000;
134 const FIFTY_HZ_RTC = 0b10000000;
135 }
136}
137
138pub enum VicBank {
139 Region0000 = 0x11, // Bank 0
140 Region4000 = 0x10, // Bank 1
141 Region8000 = 0x01, // Bank 2
142 RegionC000 = 0x00, // Bank 3
143}
144
145extern "C" {
146 // defined in c to allow assembly and interrupt attribute
147 fn hardware_raster_irq_c(triggering_raster_line: u8);
148}
149
150/// Setup hardware raster interrupt (0xfffe)
151///
152/// This registers a Rust function, `called_every_frame()` to be triggered
153/// at a specific raster line. The BASIC and KERNAL roms are disabled so
154/// suffix your main program with an endless loop.
155/// `fn called_every_frame()` must be defined and *exported*
156/// on the Rust side and will be called from C via a wrapper. This is because
157/// the LLVM `__interrupt__` attribute is currently not available from Rust.
158///
159/// # Examples
160/// ```
161/// #[no_mangle]
162/// pub unsafe extern fn called_every_frame() {
163/// ...
164/// }
165///
166/// #[start]
167/// fn _main(_argc: isize, _argv: *const *const u8) -> isize {
168/// c64::hardware_raster_irq(100); // trigger at raster line 100
169/// loop {} // let's not return to dead BASIC
170/// }
171/// ```
172pub fn hardware_raster_irq(triggering_raster_line: u8) {
173 unsafe {
174 hardware_raster_irq_c(triggering_raster_line);
175 }
176}
177
178/// Special keyboard and PETSCII codes
179pub enum Keyboard {
180 Delete = 0x14,
181 Stop = 0x03,
182 Return = 0x0d,
183 Home = 0x13,
184 CursorDown = 0x11,
185 CursorRight = 0x1d,
186 Space = 0x20,
187 ArrowLeft = 0x5f,
188 Run = 0x83,
189 F1 = 0x85,
190 F2 = 0x86,
191 F3 = 0x87,
192 F4 = 0x88,
193 F5 = 0x89,
194 F6 = 0x8a,
195 F7 = 0x8b,
196 F8 = 0x8c,
197 ShiftReturn = 0x8d,
198 CursorUp = 0x91,
199 Clear = 0x93,
200 Insert = 0x94,
201 CursorLeft = 0x9d,
202}
203
204/// Get reference to VIC2 chip
205pub const fn vic2() -> &'static MOSVideoInterfaceControllerII {
206 unsafe { &*VIC }
207}
208
209/// Get reference to SID chip
210pub const fn sid() -> &'static MOSSoundInterfaceDevice {
211 unsafe { &*SID }
212}
213
214/// Get reference to CIA1 chip
215pub const fn cia1() -> &'static MOSComplexInterfaceAdapter6526 {
216 unsafe { &*CIA1 }
217}
218
219/// Get reference to CIA2 chip
220pub const fn cia2() -> &'static MOSComplexInterfaceAdapter6526 {
221 unsafe { &*CIA2 }
222}
223
224/// Clears screen, functional style (fill with SPACE character)
225pub fn clear_screen() {
226 unsafe {
227 (*DEFAULT_VIDEO_MATRIX)
228 .iter_mut()
229 .for_each(|i| *i = Keyboard::Space as u8);
230 }
231}
232
233/// Shift to lower case ROM charset
234pub fn set_lower_case() {
235 unsafe {
236 vic2().screen_and_charset_bank.write(23);
237 }
238}
239
240/// Shift to upper case ROM charset
241pub fn set_upper_case() {
242 unsafe {
243 vic2().screen_and_charset_bank.write(21);
244 }
245}