bmx can load "stamps"

This commit is contained in:
Irmen de Jong 2023-11-29 21:04:31 +01:00
parent 278e2f5605
commit 5d9caef45f
5 changed files with 123 additions and 32 deletions

View File

@ -2,6 +2,12 @@
; Only uncompressed images, of width 320 or 640, are supported for now.
; BMX Specification: https://cx16forum.com/forum/viewtopic.php?t=6945
; TODO: refactor loading: bmx.open() - reads header . bmx.continue_load() - reads the palette+bitmap. bmx.close() -cleanup bmx.save() stays the same
; TODO: ability to save "stamps" , bitmaps that are only a region of the screen.
; TODO: load 4bpp/2bpp/1bpp images
; TODO: save 4bpp/2bpp/1bpp images
%import diskio
bmx {
@ -47,7 +53,8 @@ bmx {
goto load_end
}
if width!=320 and width!=640 {
error_message = "width not 320 or 640" ; TODO: deal with other widths
; note: use load_stamp() to read other sizes
error_message = "width not 320 or 640"
goto load_end
}
if compression {
@ -66,6 +73,57 @@ bmx {
} else
error_message = diskio.status()
load_end:
diskio.f_close()
diskio.drivenumber = old_drivenumber
return error_message==0
}
sub load_stamp(ubyte drivenumber, str filename, ubyte vbank, uword vaddr, uword screenwidth) -> bool {
; Loads a BMX bitmap "stamp" image and palette into vram. (and Header info into the bmx.* variables)
; "Stamp" means: load an image that is smaller than the screen (so we need to pad around it)
; Parameters:
; the drive number and filename to load,
; and the vram bank and address where the bitmap data should go,
; finally the screen width that the stamp image is loaded into.
; You can set the max_width and max_height variables first, if you want this routine to check those.
; Note: does not change vera screen mode or colordepth! You have to do that yourself!
; Returns: success status. If false, error_message points to the error message string.
error_message = 0
ubyte old_drivenumber = diskio.drivenumber
diskio.drivenumber = drivenumber
if diskio.f_open(filename) {
diskio.reset_read_channel()
if read_header() {
if parse_header() {
if max_width and width>max_width {
error_message = "image too large"
goto load_end
}
if max_height and height>max_height {
error_message = "image too large"
goto load_end
}
if width>screenwidth {
error_message = "image too large"
goto load_end
}
if compression {
error_message = "compression not supported"
goto load_end
}
if read_palette() {
if not read_bitmap_padded(vbank, vaddr, screenwidth)
error_message = "bitmap error"
} else
error_message = "palette error"
} else
error_message = "invalid bmx file"
} else
error_message = "invalid bmx file"
} else
error_message = diskio.status()
load_end:
diskio.f_close()
diskio.drivenumber = old_drivenumber
@ -129,14 +187,14 @@ load_end:
; drive number and filename to save to,
; vram bank and address of the bitmap data to save.
; Returns: success status. If false, error_message points to the error message string.
; TODO: how to save bitmaps that are not the full visible screen width (non-contiguous scanlines)
; TODO: how to save bitmaps that are not the full visible screen width (non-contiguous scanlines)?
if compression {
error_message = "compression not supported"
return false
}
error_message = 0
if width!=320 and width!=640 {
error_message = "width not 320 or 640" ; TODO: deal with other widths
error_message = "width not 320 or 640"
goto save_end
}
ubyte old_drivenumber = diskio.drivenumber
@ -211,27 +269,39 @@ save_end:
return cbm.READST()==0 or cbm.READST()&$40 ; no error or eof?
}
sub read_bitmap_padded(ubyte vbank, uword vaddr, uword screenwidth) -> bool {
; load bitmap "stamp" into vram from the currently active input file
cx16.r3 = bytes_per_scanline(width) ; num bytes per image scanline
cx16.r2 = bytes_per_scanline(screenwidth) ; num bytes per screen scanline
repeat height {
cx16.vaddr(vbank, vaddr, 0, 1)
read_scanline(cx16.r3)
vaddr += cx16.r2
if_cs
vbank++
}
return cbm.READST()==0 or cbm.READST()&$40 ; no error or eof?
}
sub read_bitmap(ubyte vbank, uword vaddr) -> bool {
; load contiguous bitmap into vram from the currently active input file
; TODO how to deal with bitmaps that are smaller than the screen?
cx16.vaddr(vbank, vaddr, 0, 1)
cx16.r3 = bytes_per_scanline(width)
cx16.vaddr(vbank, vaddr, 0, 1)
repeat height
read_scanline(cx16.r3)
return cbm.READST()==0 or cbm.READST()&$40 ; no error or eof?
}
sub read_scanline(uword size) {
while size {
cx16.r0 = cx16.MACPTR(min(255, size) as ubyte, &cx16.VERA_DATA0, true)
if_cs {
; no MACPTR support
repeat size
cx16.VERA_DATA0 = cbm.CHRIN()
return
}
size -= cx16.r0
sub read_scanline(uword size) {
while size {
cx16.r0 = cx16.MACPTR(min(255, size) as ubyte, &cx16.VERA_DATA0, true)
if_cs {
; no MACPTR support
repeat size
cx16.VERA_DATA0 = cbm.CHRIN()
return
}
return
size -= cx16.r0
}
}
@ -268,7 +338,6 @@ save_end:
sub write_bitmap(ubyte vbank, uword vaddr) -> bool {
; save contiguous bitmap from vram to the currently active output file
; TODO how to deal with bitmaps that are smaller than the screen
cx16.vaddr(vbank, vaddr, 0, 1)
cx16.r3 = bytes_per_scanline(width)
repeat height
@ -293,14 +362,8 @@ save_end:
}
sub bytes_per_scanline(uword w) -> uword {
when bitsperpixel {
1 -> cx16.r0L = 3
2 -> cx16.r0L = 2
4 -> cx16.r0L = 1
8 -> return w
else -> return 0
}
return w >> cx16.r0L
ubyte[4] shifts = [3,2,1,0]
return w >> shifts[vera_colordepth]
}
sub parse_header() -> bool {

View File

@ -281,7 +281,8 @@ close_end:
; note: only a single iteration loop can be active at a time!
; Returns true if the file is successfully opened and readable.
; No need to check status(), unlike f_open_w() !
; NOTE: the default input isn't yet set to this logical file, you can use reset_read_channel() to do this!
; NOTE: the default input isn't yet set to this logical file, you must use reset_read_channel() to do this,
; if you're going to read from it yourself instead of using f_read()!
f_close()
cbm.SETNAM(string.length(filenameptr), filenameptr)

View File

@ -273,7 +273,8 @@ close_end:
; note: only a single iteration loop can be active at a time!
; Returns true if the file is successfully opened and readable.
; No need to check status(), unlike f_open_w() !
; NOTE: the default input isn't yet set to this logical file, you can use reset_read_channel() to do this!
; NOTE: the default input isn't yet set to this logical file, you must use reset_read_channel() to do this,
; if you're going to read from it yourself instead of using f_read()!
f_close()
cbm.SETNAM(string.length(filenameptr), filenameptr)

View File

@ -18,6 +18,7 @@ main {
repeat {
txt.print("\nenter bmx image filename: ")
if txt.input_chars(&filename) {
if bmx.load_header(8, filename) {
txt.print("\nsize: ")
txt.print_uw(bmx.width)
@ -34,15 +35,36 @@ main {
bmx.palette_buffer_ptr = memory("palette", 512, 0)
sys.memset(bmx.palette_buffer_ptr, 512, 0)
palette.set_rgb(bmx.palette_buffer_ptr, 256)
bmx.max_width = 320
bmx.max_height = 240
; switch to correct screen mode and color depth
void cx16.screen_mode($80, false)
; switch to bitmap screen mode and color depth: 320*240
void cx16.screen_mode($80, false) ; we're lazy and just use a kernal routine to set up the basics
cx16.VERA_L0_CONFIG = cx16.VERA_L0_CONFIG & %11111100 | bmx.vera_colordepth
; now load the image
if bmx.load(8, filename, 0, 0) {
activate_palette()
void txt.waitkey()
if bmx.width==320 {
; can use the fast, full-screen load routine
if bmx.load(8, filename, 0, 0) {
if bmx.height<240 {
; fill the remaining bottom part of the screen
cx16.GRAPH_set_colors(bmx.border, bmx.border, 99)
cx16.GRAPH_draw_rect(0, bmx.height, 320, 240-bmx.height, 0, true)
}
activate_palette()
void txt.waitkey()
}
} else {
; clear the screen with the border color
cx16.GRAPH_set_colors(0, 0, bmx.border)
cx16.GRAPH_clear()
; need to use the slower load routine that does padding
; center the image on the screen nicely
uword offset = (320-bmx.width)/2 + (240-bmx.height)/2*320
if bmx.load_stamp(8, filename, 0, offset, 320) {
activate_palette()
void txt.waitkey()
}
}
}
@ -60,6 +82,7 @@ main {
sub activate_palette() {
; copies the pallette data from the memory buffer into vram
cx16.VERA_DC_BORDER = bmx.border
cx16.r1 = bmx.palette_buffer_ptr
cx16.r2L = bmx.palette_start
cx16.r3L = bmx.palette_entries

View File

@ -13,6 +13,9 @@ main {
txt.print("\nprog8's parse_f: ")
float value = floats.parse_f(buffer)
floats.print_f(value)
; floats.VAL_1 is defined as:
; romsub $fe09 = VAL_1(uword string @XY, ubyte length @A) clobbers(A,X,Y) -> float @FAC1
txt.print("\nrom val_1: ")
value = floats.VAL_1(buffer, string.length(buffer))
floats.print_f(value)