@@ -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