Skip to content

Commit 5c442a1

Browse files
committed
Now supports 320x480 @ 256 colours.
1 parent 2443943 commit 5c442a1

File tree

2 files changed

+89
-3
lines changed

2 files changed

+89
-3
lines changed

memory.x

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ MEMORY {
2525
/*
2626
* This is the bottom of the four striped banks of SRAM in the RP2040.
2727
*/
28-
RAM_OS : ORIGIN = 0x20000000, LENGTH = 0x42000 - 0x9630
28+
RAM_OS : ORIGIN = 0x20000000, LENGTH = 0x42000 - 0x9690
2929
/*
3030
* This is the top of the four striped banks of SRAM in the RP2040, plus
3131
* SRAM_BANK4 and SRAM_BANK5.
3232
*
3333
* This is carefully calculated to give us 8 KiB of stack space and ensure
3434
* the defmt buffer doesn't span across SRAM_BANK3 and SRAM_BANK4.
3535
*
36-
* 0x9630 should be the (size of .data + size of .bss + size of .uninit +
36+
* 0x9690 should be the (size of .data + size of .bss + size of .uninit +
3737
* 0x2000 for the stack).
3838
*/
39-
RAM : ORIGIN = 0x20042000 - 0x9630, LENGTH = 0x9630
39+
RAM : ORIGIN = 0x20042000 - 0x9690, LENGTH = 0x9690
4040
}
4141

4242
/*

src/vga/mod.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ impl RenderEngine {
232232
// Bitmap with 4 bits per pixel
233233
self.draw_next_line_chunky4(scan_line_buffer, current_line_num);
234234
}
235+
neotron_common_bios::video::Format::Chunky8 => {
236+
// Bitmap with 8 bits per pixel
237+
self.draw_next_line_chunky8(scan_line_buffer, current_line_num);
238+
}
235239
_ => {
236240
// Draw nothing
237241
}
@@ -477,6 +481,7 @@ impl RenderEngine {
477481
// }
478482

479483
// So I wrote it by hand in assembly instead, saving two clock cycles per loop
484+
// We have 320x8 input and must produce 320x32 output
480485
unsafe {
481486
core::arch::asm!(
482487
"0:",
@@ -503,6 +508,81 @@ impl RenderEngine {
503508
}
504509
}
505510

511+
/// Draw a line of 8-bpp bitmap as pixels.
512+
///
513+
/// Writes into the relevant pixel buffer (either [`PIXEL_DATA_BUFFER_ODD`]
514+
/// or [`PIXEL_DATA_BUFFER_EVEN`]) assuming the framebuffer is a bitmap.
515+
///
516+
/// The `current_line_num` goes from `0..NUM_LINES`.
517+
#[link_section = ".data"]
518+
pub fn draw_next_line_chunky8(&mut self, scan_line_buffer: &LineBuffer, current_line_num: u16) {
519+
let is_double = self.current_video_mode.is_horiz_2x();
520+
let base_ptr = self.current_video_ptr as *const u8;
521+
let line_len_bytes = self.current_video_mode.line_size_bytes();
522+
let line_start_offset_bytes = usize::from(current_line_num) * line_len_bytes;
523+
let line_start_bytes = unsafe { base_ptr.add(line_start_offset_bytes) };
524+
// Get a pointer into our scan-line buffer
525+
let mut scan_line_buffer_ptr = scan_line_buffer.pixel_ptr();
526+
let palette_ptr = VIDEO_PALETTE.as_ptr() as *const RGBColour;
527+
if is_double {
528+
// Double-width mode.
529+
// two RGB pixels (one pair) per byte
530+
531+
// This code optimises poorly
532+
// for col in 0..line_len_bytes {
533+
// unsafe {
534+
// let chunky_pixel = line_start_bytes.add(col).read() as usize;
535+
// let rgb = palette_ptr.add(chunky_pixel).read();
536+
// scan_line_buffer_ptr.write(RGBPair::from_pixels(rgb, rgb));
537+
// scan_line_buffer_ptr = scan_line_buffer_ptr.add(1);
538+
// }
539+
// }
540+
541+
// So I wrote it by hand in assembly instead, saving two clock cycles per loop
542+
// We have 320x8 input and must produce 320x32 output
543+
unsafe {
544+
core::arch::asm!(
545+
"0:",
546+
// load a byte from line_start_bytes
547+
"ldrb {tmp}, [{lsb}]",
548+
// multiply it by sizeof(u16)
549+
"lsls {tmp}, {tmp}, #0x1",
550+
// load a 32-bit word from the palette
551+
"ldrh {tmp}, [{palette}, {tmp}]",
552+
// double it up
553+
"lsls {tmp2}, {tmp}, #16",
554+
"adds {tmp}, {tmp}, {tmp2}",
555+
// store the 32-bit word to the scanline buffer, and increment
556+
"stm {slbp}!, {{ {tmp} }}",
557+
// increment the lsb
558+
"adds {lsb}, {lsb}, #0x1",
559+
// loop until we're done
560+
"cmp {lsb}, {lsb_max}",
561+
"bne 0b",
562+
lsb = in(reg) line_start_bytes,
563+
lsb_max = in(reg) line_start_bytes.add(line_len_bytes),
564+
palette = in(reg) core::ptr::addr_of!(VIDEO_PALETTE),
565+
tmp = in(reg) 0,
566+
tmp2 = in(reg) 1,
567+
slbp = in(reg) scan_line_buffer_ptr,
568+
);
569+
}
570+
} else {
571+
// Single-width mode.
572+
// one RGB pixel per byte
573+
for col in 0..line_len_bytes / 2 {
574+
unsafe {
575+
let chunky_pixel_left = line_start_bytes.add(col * 2).read() as usize;
576+
let rgb_left = palette_ptr.add(chunky_pixel_left).read();
577+
let chunky_pixel_right = line_start_bytes.add((col * 2) + 1).read() as usize;
578+
let rgb_right = palette_ptr.add(chunky_pixel_right).read();
579+
scan_line_buffer_ptr.write(RGBPair::from_pixels(rgb_left, rgb_right));
580+
scan_line_buffer_ptr = scan_line_buffer_ptr.add(1);
581+
}
582+
}
583+
}
584+
}
585+
506586
/// Draw a line text as pixels.
507587
///
508588
/// Writes into the relevant pixel buffer (either [`PIXEL_DATA_BUFFER_ODD`]
@@ -2010,6 +2090,12 @@ pub fn test_video_mode(mode: neotron_common_bios::video::Mode) -> bool {
20102090
| neotron_common_bios::video::Format::Chunky4,
20112091
true,
20122092
false,
2093+
) | (
2094+
neotron_common_bios::video::Timing::T640x480
2095+
| neotron_common_bios::video::Timing::T640x400,
2096+
neotron_common_bios::video::Format::Chunky8,
2097+
true,
2098+
false
20132099
)
20142100
)
20152101
}

0 commit comments

Comments
 (0)