mos_hardware/vera.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// originally from cc65 header file; modififed from original version.
16//
17// cx16.h
18//
19// CX16 system-specific definitions
20// For prerelease 39
21//
22//
23// This software is provided "as-is", without any expressed or implied
24// warranty. In no event will the authors be held liable for any damages
25// arising from the use of this software.
26//
27// Permission is granted to anyone to use this software for any purpose,
28// including commercial applications, and to alter it and redistribute it
29// freely, subject to the following restrictions:
30//
31// 1. The origin of this software must not be misrepresented; you must not
32// claim that you wrote the original software. If you use this software
33// in a product, an acknowledgment in the product documentation would be
34// appreciated, but is not required.
35// 2. Altered source versions must be plainly marked as such, and must not
36// be misrepresented as being the original software.
37// 3. This notice may not be removed or altered from any source
38// distribution.
39
40//! Registers for the Versatile Embedded Retro Adapter (VERA) graphics chip.
41//!
42//! VERA consists of:
43//! - Video generator featuring:
44//! - Multiple output formats (VGA, NTSC Composite, NTSC S-Video, RGB video) at a fixed resolution of 640x480 at 60Hz
45//! - Support for two layers, both supporting either tile or bitmap mode.
46//! - Support for up to 128 sprites.
47//! - Embedded video RAM of 128kB.
48//! - Palette with 256 colors selected from a total range of 4096 colors.
49//! - 16-channel Programmable Sound Generator with multiple waveforms (Pulse, Sawtooth, Triangle, Noise)
50//! - High quality PCM audio playback from 4 kB FIFO buffer with up to 48kHz 16-bit stereo sound.
51//! - SPI controller for SecureDigital storage.
52//! - [VERA Reference Guide](https://github.com/commanderx16/x16-docs/blob/master/VERA%20Programmer's%20Reference.md)
53
54use bitflags::bitflags;
55use core::mem::ManuallyDrop;
56use volatile_register::{RW, WO};
57
58pub const VIDEOMODE_80X60: u8 = 0;
59pub const VIDEOMODE_80X30: u8 = 1;
60pub const VIDEOMODE_40X60: u8 = 2;
61pub const VIDEOMODE_40X30: u8 = 3;
62pub const VIDEOMODE_40X15: u8 = 4;
63pub const VIDEOMODE_20X30: u8 = 5;
64pub const VIDEOMODE_20X15: u8 = 6;
65pub const VIDEOMODE_80COL: u8 = 0;
66pub const VIDEOMODE_40COL: u8 = 3;
67pub const VIDEOMODE_320X240: u8 = 128;
68pub const VIDEOMODE_SWAP: i32 = -1;
69
70pub const IRQ_VSYNC: u8 = 1;
71pub const IRQ_RASTER: u8 = 2;
72pub const IRQ_SPR_COLL: u8 = 4;
73pub const IRQ_AUDIO_LOW: u8 = 8;
74
75pub const INC_0: u8 = convert_stride(0);
76pub const INC_2: u8 = convert_stride(2);
77pub const INC_4: u8 = convert_stride(4);
78pub const INC_8: u8 = convert_stride(8);
79pub const INC_16: u8 = convert_stride(16);
80pub const INC_32: u8 = convert_stride(32);
81pub const INC_64: u8 = convert_stride(64);
82pub const INC_128: u8 = convert_stride(128);
83pub const INC_256: u8 = convert_stride(256);
84pub const INC_512: u8 = convert_stride(512);
85pub const INC_40: u8 = convert_stride(40);
86pub const INC_80: u8 = convert_stride(80);
87pub const INC_160: u8 = convert_stride(160);
88pub const INC_320: u8 = convert_stride(320);
89pub const INC_640: u8 = convert_stride(640);
90
91pub const DEC_0: u8 = convert_stride(0);
92pub const DEC_2: u8 = convert_stride(-2);
93pub const DEC_4: u8 = convert_stride(-4);
94pub const DEC_8: u8 = convert_stride(-8);
95pub const DEC_16: u8 = convert_stride(-16);
96pub const DEC_32: u8 = convert_stride(-32);
97pub const DEC_64: u8 = convert_stride(-64);
98pub const DEC_128: u8 = convert_stride(-128);
99pub const DEC_256: u8 = convert_stride(-256);
100pub const DEC_512: u8 = convert_stride(-512);
101pub const DEC_40: u8 = convert_stride(-40);
102pub const DEC_80: u8 = convert_stride(-80);
103pub const DEC_160: u8 = convert_stride(-160);
104pub const DEC_320: u8 = convert_stride(-320);
105pub const DEC_640: u8 = convert_stride(-640);
106
107/// Convert stride to register value.
108///
109/// By setting the 'Address Increment' field in `ADDRx_H`, the address will be incremented after each access to the data register.
110/// Setting the `DECR` bit, will decrement instead of increment.
111/// More [information](https://github.com/commanderx16/x16-docs/blob/master/VERA%20Programmer's%20Reference.md#video-ram-access)
112///
113/// Example:
114/// ~~~
115/// const DEC_8: u8 = convert_stride(-8); // negative stride
116/// const INC_127: u8 = convert_stride(127); // compile time error: invalid stride
117/// ~~~
118pub const fn convert_stride(stride: i16) -> u8 {
119 let value = match stride.abs() {
120 0 => 0,
121 1 => 1,
122 2 => 2,
123 4 => 3,
124 8 => 4,
125 16 => 5,
126 32 => 6,
127 64 => 7,
128 128 => 8,
129 256 => 9,
130 512 => 10,
131 40 => 11,
132 80 => 12,
133 160 => 13,
134 320 => 14,
135 640 => 15,
136 _ => panic!("invalid stride"),
137 };
138 let decrement = stride < 0;
139 // first a single shift to make room for the `DECR` bit.
140 // then shift by 3 to end up in bits 7-3.
141 ((value << 1) | decrement as u8) << 3
142}
143
144/// Versatile Embedded Retro Adapter (VERA) graphics chip
145#[repr(C)]
146pub struct VersatileEmbeddedRetroAdapter {
147 /// VRAM Address 0-16 (offset 0x00)
148 pub address: RW<u16>,
149 /// `ADDRx_H` - Address (offset 0x02)
150 pub address_hi: RW<u8>,
151 /// `DATA0` - VRAM Data port 0 (offset 0x03)
152 pub data0: RW<u8>,
153 /// `DATA1` - VRAM Data port 1 (offset 0x04)
154 pub data1: RW<u8>,
155 /// `CTRL` - Control, offset 0x05
156 pub control: RW<ControlFlags>,
157 /// `IEN` - Interrupt enable, offset 0x06
158 pub irq_enable: RW<u8>,
159 /// `ISR` - Interrupt flags, offset 0x07
160 pub irq_flags: RW<u8>,
161
162 /// `IRQLINE_L` - Interrupt raster, offset 0x08
163 ///
164 /// `IRQLINE` specifies at which line the `LINE` interrupt will be generated.
165 /// Note that bit 8 of this value is present in the `IEN` register.
166 /// For interlaced modes the interrupt will be generated each field and the bit 0 of `IRQ_LINE` is ignored.
167 pub irq_raster: RW<u8>,
168 /// `DC_VIDEO` - Display composer
169 pub display_composer: DisplayComposer,
170 pub layer0: Layer,
171 pub layer1: Layer,
172 /// Audio (offset 0x1b)
173 pub audio: Audio,
174 pub spi: SPIController,
175}
176
177bitflags! {
178 /// Flags for the `VersatileEmbeddedRetroAdapter::control` (`CTRL`) register at offset 0x05
179 pub struct ControlFlags: u8 {
180 const ADDRSEL = 0b0000_0001;
181 const DCSEL = 0b0000_0010;
182 /// RESET flag
183 /// When set, the FPGA will reconfigure itself:
184 /// all registers will be reset; the palette RAM will be set to default values.
185 const RESET = 0b1000_0000;
186 }
187}
188
189#[repr(C)]
190pub union DisplayComposer {
191 /// Visible when Display Composer (DC) `SEL` flag = 0
192 pub display0: ManuallyDrop<Display0>,
193 /// Visible when Display Composer (DC) `SEL` flag = 1
194 pub display1: ManuallyDrop<Display1>,
195}
196
197bitflags! {
198 /// Flags for Display Composer (DC) VIDEO at offset 0x09
199 ///
200 /// Bits 0-1 define the OUTPUT modes.
201 pub struct VideoFlags: u8 {
202 const DISABLED = 0b0000_0000;
203 const VGA = 0b0000_0001;
204 const NTSC = 0b0000_0010;
205 /// RGB interlaced, composite sync (via VGA connector)
206 const RGB = 0b0000_0011;
207
208 /// Disable chroma
209 ///
210 /// Setting `CHROMA_DISABLE` disables output of chroma in NTSC composite mode and will give a
211 /// better picture on a monochrome display.
212 /// (Setting this bit will also disable the chroma output on the S-video output.)
213 const CHROMA_DISABLE = 0b0000_0100; // bit 2
214 const LAYER0_ENABLE = 0b0001_0000; // bit 4
215 const LAYER1_ENABLE = 0b0010_0000; // bit 5
216 const SPRITES_ENABLE = 0b0100_0000; // bit 6
217
218 /// Read-only bit which reflects the active interlaced field in composite and RGB modes
219 ///
220 /// 0: even, 1: odd
221 const CURRENT_FIELD = 0b1000_0000; // bit 7
222 }
223}
224
225/// Active when Display Composer (DC) SEL=0
226#[repr(C)]
227pub struct Display0 {
228 /// Flags to enable video layers
229 pub video: RW<VideoFlags>,
230 /// `HSCALE` - Active Display H-Scale, offset 0x0a
231 ///
232 /// `HSCALE` and `VSCALE` will set the fractional scaling factor of the active part of the display.
233 /// Setting this value to 128 will output 1 output pixel for every input pixel.
234 /// Setting this to 64 will output 2 output pixels for every input pixel.
235 pub hscale: RW<u8>,
236 /// `VSCALE` - Active Display V-Scale, offset 0x0b
237 ///
238 /// `HSCALE` and `VSCALE` will set the fractional scaling factor of the active part of the display.
239 /// Setting this value to 128 will output 1 output pixel for every input pixel.
240 /// Setting this to 64 will output 2 output pixels for every input pixel.
241 pub vscale: RW<u8>,
242 /// `DC_BORDER` - Border Color, offset 0x0c
243 ///
244 /// Determines the palette index which is used for the non-active area of the screen.
245 pub border: RW<u8>,
246}
247
248/// Active when Display Composer (DC) `SEL=1`
249///
250/// `HSTART`/`HSTOP` and `VSTART`/`VSTOP` determines the active part of the screen.
251/// The values here are specified in the native 640x480 display space.
252/// `HSTART=0`, `HSTOP=640`, `VSTART=0`, `VSTOP=480` will set the active area to the full resolution.
253/// Note that the lower 2 bits of `HSTART`/`HSTOP` and the lower 1 bit of `VSTART`/`VSTOP` isn't available.
254/// This means that horizontally the start and stop values can be set at a multiple of 4 pixels,
255/// vertically at a multiple of 2 pixels.
256#[repr(C)]
257pub struct Display1 {
258 /// Horizontal start position
259 pub hstart: RW<u8>,
260 /// Horizontal stop position
261 pub hstop: RW<u8>,
262 /// Vertical start position
263 pub vstart: RW<u8>,
264 /// Vertical stop position
265 pub vstop: RW<u8>,
266}
267
268/// Video layer registers
269///
270/// The features of the two VERA layers are the same.
271/// Each layer supports a few different modes which are specified using T256C / 'Bitmap Mode' / 'Color Depth' in `Lx_CONFIG`.
272/// The layer can either operate in tile mode or bitmap mode.
273/// This is selected using the 'Bitmap Mode' bit; 0 selects tile mode, 1 selects bitmap mode.
274/// The handling of 1 bpp tile mode is different from the other tile modes.
275/// Depending on the T256C bit the tiles use either a 16-color foreground and background color or a 256-color foreground color.
276/// Other modes ignore the T256C bit.
277#[repr(C)]
278pub struct Layer {
279 /// `Lx_CONFIG`
280 pub config: RW<u8>,
281 /// `Lx_MAPBASE` - Map Base Address (16:9)
282 pub mapbase: RW<u8>,
283 /// `Lx_TILEBASE`
284 pub tilebase: RW<u8>,
285 /// `H-SCROLL` - Horizontal scroll
286 pub hscroll: RW<u16>,
287 /// `V-SCROLL` - Vertical scroll
288 pub vscroll: RW<u16>,
289}
290
291/// VERA audio
292///
293/// The audio functionality consists of two independent systems:
294/// 1. The PSG or Programmable Sound Generator.
295/// 2. The PCM (or Pulse-Code Modulation) playback system.
296#[repr(C)]
297pub struct Audio {
298 /// `AUDIO_CTRL`
299 pub control: RW<u8>,
300 /// `AUDIO_RATE` - PCM Sample Rate
301 pub rate: RW<u8>,
302 /// `AUDIO_DATA` - Audio FIFO data (write-only)
303 pub data: WO<u8>,
304}
305
306/// SPI controller connected to the SD card connector
307///
308/// The speed of the clock output of the SPI controller can be controlled by the 'Slow Clock' bit.
309/// When this bit is 0 the clock is 12.5MHz, when 1 the clock is about 390kHz.
310/// The slow clock speed is to be used during the initialization phase of the SD card.
311/// Some SD cards require a clock less than 400kHz during part of the initialization.
312/// A transfer can be started by writing to `SPI_DATA`.
313/// While the transfer is in progress the BUSY bit will be set.
314/// After the transfer is done, the result can be read from the SPI_DATA register.
315/// The chip select can be controlled by writing the `SELECT` bit.
316/// Writing 1 will assert the chip-select (logic-0) and writing 0 will release the chip-select (logic-1).
317#[repr(C)]
318pub struct SPIController {
319 pub data: RW<u8>,
320 pub control: RW<u8>,
321}
322
323/// VRAM, 0x00000 - 0x1F9BF
324pub const VIDEO_RAM: *mut u8 = (0x00000) as *mut u8;
325
326/// PSG registers, 0x1F9C0 - 0x1F9FF
327pub const PSG_REGISTERS: *mut u8 = (0x1f9c0u32) as *mut u8;
328
329/// Palette, 0x1FA00 - 0x1FBFF
330pub const PALETTE: *mut u8 = (0x1fa00u32) as *mut u8;
331
332/// Sprite attributes, 0x1FC00 - 0x1FFFF
333pub const SPRITE_ATTRIBUTES: *mut u8 = (0x1fc00u32) as *mut u8;