a800xl_utils/screen/
mod.rs

1use crate::cio::{Cmd, IOCB};
2use crate::consts;
3use ufmt_write::uWrite;
4
5/// show / hide text cursor
6pub fn show_cursor(visible: bool) {
7    unsafe {
8        *crate::consts::CRSINH = !visible as u8;
9    }
10}
11
12/// reset ATRACT (screen saver counter)
13pub fn clear_atract() {
14    unsafe { *consts::ATRACT = 0 }
15}
16
17/// move text cursor to specified position
18pub fn gotoxy(x: i16, y: i16) {
19    unsafe {
20        **consts::OLDADR = *consts::OLDCHR;
21        *consts::OLDADR = (*consts::SAVMSC).add(40 * y as usize + x as usize);
22        let c = **consts::OLDADR;
23        *consts::OLDCHR = c;
24        if *consts::CRSINH == 0 {
25            **consts::OLDADR |= 0x80;
26        }
27        set_pos(x, y);
28    }
29}
30
31/// set current cursor position
32pub fn set_pos(x: i16, y: i16) {
33    unsafe {
34        *consts::COLCRS = x as u16;
35        *consts::ROWCRS = y as u8;
36    }
37}
38
39/// set old cursor position (starting point for drawto)
40
41pub fn set_start_pos(x: i16, y: i16) {
42    unsafe {
43        *consts::OLDCOL = x as u16;
44        *consts::OLDROW = y as u8;
45    }
46}
47
48/// initialize BASIC-like graphics mode
49/// by opening 'S:' device on #6 CIO channel
50
51pub fn init_graphics(mode: u8, open_mode: u8) {
52    IOCB::new(6)
53        .cmd(Cmd::Open as u8)
54        .buffer(b"S:\x9b")
55        .icax1(open_mode)
56        .icax2(mode)
57        .call();
58}
59
60/// close #6 CIO channel
61pub fn close_graphics() {
62    IOCB::new(6).cmd(Cmd::Close as u8).call();
63}
64
65/// draw line from (x, y) to (dx, dy)
66pub fn draw_line(x: i16, y: i16, dx: i16, dy: i16) {
67    set_start_pos(x, y);
68    draw_to(dx, dy);
69}
70
71/// draw line from last position to (dx, dy)
72pub fn draw_to(dx: i16, dy: i16) {
73    set_pos(dx, dy);
74    IOCB::new(6).cmd(17).icax1(12).icax2(0).call();
75}
76
77/// set color for next plot / draw operation
78pub fn set_color(color: u8) {
79    unsafe {
80        *consts::ATACHR = color;
81    }
82}
83
84pub fn plot(x: i16, y: i16) {
85    set_pos(x, y);
86    let color = unsafe { *consts::ATACHR };
87    IOCB::new(6)
88        .cmd(Cmd::PutBytes as u8)
89        .buffer(&[])
90        .call_with_a(color);
91}
92
93/// clear screen (for now only text gr0 mode)
94pub fn clrscr() {
95    // TODO: support other graphics modes
96    let scr_slice = unsafe {
97        let scr_addr = *consts::SAVMSC;
98        core::slice::from_raw_parts_mut(scr_addr, 40 * 24)
99    };
100    scr_slice.fill(0);
101}
102
103/// convert atascii code to screen code
104pub fn atascii_to_screen(b: u8) -> u8 {
105    (match b & 0x7f {
106        0..=31 => b + 64,
107        32..=95 => b - 32,
108        _ => b,
109    }) | (b & 128)
110}
111
112/// [ufmt::uWrite] implementation for writing directly to screen memory
113/// (it converts atascii text to screen codes)
114pub struct ScreenMemoryWriter<'a> {
115    buffer: &'a mut [u8],
116    written: usize,
117}
118
119impl<'a> ScreenMemoryWriter<'a> {
120    pub fn new(buffer: &'a mut [u8]) -> ScreenMemoryWriter<'a> {
121        ScreenMemoryWriter { buffer, written: 0 }
122    }
123}
124
125impl<'a> uWrite for ScreenMemoryWriter<'a> {
126    type Error = ();
127
128    fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
129        for b in s.bytes().map(atascii_to_screen) {
130            if self.written >= self.buffer.len() {
131                return Err(());
132            }
133            self.buffer[self.written] = b;
134            self.written += 1;
135        }
136        Ok(())
137    }
138}