Update to rev5, implement decompression.

This commit is contained in:
Zachary Hall 2023-11-21 19:13:53 -08:00
parent 887773ec81
commit 7530376cce
5 changed files with 194 additions and 82 deletions

View file

@ -6,22 +6,21 @@ vera_data0 := $9f23
.import popa .import popa
.segment "CODE" .segment "CODE"
_fill_vera: _fill_vera:
sta vera_fill_data sta vera_fill_data ; Save the value to fill the vera with
jsr popa jsr popa ; Get the length parameter
sta max sta max ; Store the max value
lda max cmp #0 ; Make sure it's not 0
cmp #0 beq @end ; If it is, we're already done
beq @end
@fill: @fill:
ldx #0 ldx #0 ; Initialize X
lda vera_fill_data lda vera_fill_data ; Load the fill value
@loop: @loop:
sta vera_data0 sta vera_data0 ; Store the fill value
inx inx ; Increment X
cpx max cpx max ; Compare it against the maximum value
bne @loop bne @loop ; If it's not the max yet, do it again
@end: @end:
rts rts ; We're done!
.segment "BSS" .segment "BSS"
max: max:
.res 1 .res 1

View file

@ -7,30 +7,31 @@ r1 := $04
r1l:= $04 r1l:= $04
r1h:= $05 r1h:= $05
.segment "CODE" .segment "CODE"
; ASM wrapper for MACPTR
_cx16_k_macptr: _cx16_k_macptr:
sta r0l sta r0l ; Store pointer in ZP
stx r0h stx r0h
jsr popa jsr popa ; Store increment boolean
sta r1l sta r1l
jsr popa jsr popa ; Store size byte
sta r1h sta r1h
ldx r0l ldx r0l ; Load parameters
ldy r0h ldy r0h
lda r1l lda r1l
beq @increment beq @increment ; If A is 0, set the carry flag
clc clc ; Otherwise clear it
jmp @loadregs jmp @loadregs ; And don't set it again
@increment: @increment:
sec sec ; Set the carry flag
@loadregs: @loadregs:
lda r1h lda r1h ; Load the size parameter
jsr $FF44 jsr $FF44 ; Call MACPTR
bcs @error bcs @error ; If there was an error, return 0
txa txa ; Otherwise, set up registers to hold the return value
sty r0 sty r0
ldx r0 ldx r0
rts rts
@error: @error:
lda #0 lda #0 ; Return 0 in case of error
ldx #0 ldx #0
rts rts

View file

@ -5,98 +5,138 @@
#include <cbm.h> #include <cbm.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "vera.h" #include "vera.h"
#include "macptr.h" #include "macptr.h"
#include "debug.h" #include "debug.h"
#include "memory_decompress.h"
#include "fill.h" #include "fill.h"
uint8_t palette[512]; uint8_t palette[512];
uint8_t vera_bit_depth; uint8_t vera_bit_depth;
uint8_t bitdepth; uint8_t bitdepth;
uint16_t width, height; uint16_t width, height;
bool compress;
bool direct_to_vera;
uint16_t imgdatastart;
uint32_t imgdatabytes; uint32_t imgdatabytes;
uint8_t pixels_per_byte; uint8_t pixels_per_byte;
uint8_t filleridx = 255; uint8_t borderidx = 255;
bool over320; bool over320;
uint8_t oldhscale, oldvscale; uint8_t oldhscale, oldvscale;
uint8_t significant_palette_entries; uint8_t significant_palette_entries;
uint8_t significant_palette_start; uint8_t significant_palette_start;
uint8_t *image_start = BANK_RAM;
/// @brief Reads a 16 bit little endian value from LFN 2
/// @return The 16 bit value
uint16_t read16() { uint16_t read16() {
cbm_k_chkin(2); cbm_k_chkin(2);
return ((cbm_k_chrin())) | ((uint16_t)cbm_k_chrin() << 8); return ((cbm_k_chrin())) | ((uint16_t)cbm_k_chrin() << 8);
} }
/// @brief Reads an 8 bit value and returns it as a character
/// @return The character
char readchar() { char readchar() {
cbm_k_chkin(2); cbm_k_chkin(2);
return cbm_k_chrin(); return cbm_k_chrin();
} }
/// @brief Reads an unsigned 8 bit value and returns it as is
/// @return The unsigned 8 bit value
uint8_t read8() { uint8_t read8() {
cbm_k_chkin(2); cbm_k_chkin(2);
return cbm_k_getin(); return cbm_k_getin();
} }
/// @brief Checks if the header in LFN 2 is correct
/// @return True if it is, false otherwise
bool checkheader() { bool checkheader() {
char header[4] = {0x42, 0x4D, 0x58, 0}; // X16BM char header[4] = {0x42, 0x4D, 0x58, 0}; // BMX
char tested[4] = {0, 0, 0, 0}; char tested[4] = {0, 0, 0, 0}; // The tested values, for debugging
uint8_t i = 0; uint8_t i = 0;
bool valid = true; bool valid = true; // Set to true so that if it's wrong, the loop can set it to false, but if it's correct, the loop doesn't have to set it
char chr = 0; char chr = 0;
printf("Reading magic bytes...\n"); printf("Reading magic bytes...\n");
// Check the magic bytes one-by-one
for (; i < 3; i++) { for (; i < 3; i++) {
chr = readchar(); chr = readchar(); // Get a character
tested[i] = chr; tested[i] = chr; // Put it in the tested value list
printf("%2x%s", chr, i >= 4 ? "\n" : ", "); printf("%2x%s", chr, i >= 4 ? "\n" : ", ");
if (chr != header[i]) { if (chr != header[i]) { // Check the value
valid = false; valid = false;
} }
} }
// Make sure the tested values don't contain any unprintable characters
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
if (tested[i] < 0x20 || tested[i] >= 0x7F) { if (tested[i] < 0x20 || tested[i] >= 0x7F) {
tested[i] = '.'; tested[i] = '.';
} }
} }
printf("%s%c=%s\n", tested, valid ? '=' : '!', header); printf("%s%c=%s\n", tested, valid ? '=' : '!', header); // Display the comparison
return valid; return valid;
} }
/// @brief Updates the scale of the VERA based on the minimum required width/height to display the image
void updatescale() { void updatescale() {
uint8_t hscale; uint8_t hscale;
uint8_t vscale; uint8_t vscale;
// Set DCSEL to 0
VERA.control &= 0b10000001; VERA.control &= 0b10000001;
// Get the scale to check against and later set back to
hscale = VERA.display.hscale; hscale = VERA.display.hscale;
vscale = VERA.display.vscale; vscale = VERA.display.vscale;
// Back up the current scale
oldhscale = hscale; oldhscale = hscale;
oldvscale = vscale; oldvscale = vscale;
// If it's over 320, the scale's probably correct.
if (over320) return; if (over320) return;
// Make sure the scale doesn't end up showing garbage data.
if (hscale > 64) VERA.display.hscale = 64; if (hscale > 64) VERA.display.hscale = 64;
if (vscale > 64) VERA.display.vscale = 64; if (vscale > 64) VERA.display.vscale = 64;
} }
/// @brief Restores the scale to what it was before updating it to fit the image
void restorescale() { void restorescale() {
VERA.control &= 0b10000001; VERA.control &= 0b10000001;
VERA.display.hscale = oldhscale; VERA.display.hscale = oldhscale;
VERA.display.vscale = oldvscale; VERA.display.vscale = oldvscale;
} }
/// @brief Loads and uploads an image to the VERA
/// @param filename The file to load
/// @return 0 on success, exit code on failure
int uploadimage(const char *filename) { int uploadimage(const char *filename) {
// Used for decompression.
uint8_t *image_end = BANK_RAM;
// 16 bit variables
size_t i = 0, j = 0, x = 0, y = 0, value = 0, bitmask = 0; size_t i = 0, j = 0, x = 0, y = 0, value = 0, bitmask = 0;
// Variables for the VERA width and height values
size_t vera_w = 320, vera_h = 240; size_t vera_w = 320, vera_h = 240;
// The max address of the VERA
uint16_t vera_max = 0; uint16_t vera_max = 0;
// 32 bit variables, used sparingly
uint32_t tmp = 0, vera_max_32 = 0; uint32_t tmp = 0, vera_max_32 = 0;
uint16_t vera_adr_bak; // The bank the VERA will be at when the operation finishes
uint8_t vera_adr_h_bak;
bool vera_max_bank = 1; bool vera_max_bank = 1;
// Base layer config
uint8_t config = 0b00000100; uint8_t config = 0b00000100;
// The counter for the amount of bytes read
uint16_t bytes_read; uint16_t bytes_read;
// The version of the BMX format
uint8_t version; uint8_t version;
// True if all palette entries are significant
bool all_significant; bool all_significant;
// Open the file
cbm_k_setnam(filename); cbm_k_setnam(filename);
cbm_k_setlfs(2, 8, 2); cbm_k_setlfs(2, 8, 2);
cbm_k_open(); cbm_k_open();
// Check if the open was successful
if (cbm_k_readst()) { if (cbm_k_readst()) {
printf("Error opening file.\n"); printf("Error opening file.\n");
return 1; return 1;
} }
// Make sure it's a BMX file
if (!checkheader()) { if (!checkheader()) {
printf("Invalid file!\n"); printf("Invalid file!\n");
return 1; return 1;
} }
// Read the header
version = read8(); version = read8();
bitdepth = read8(); bitdepth = read8();
// Verify the bitdepth
if (bitdepth == 0) { if (bitdepth == 0) {
printf("Error: bitdepth was 0.\n"); printf("Error: bitdepth was 0.\n");
return 1; return 1;
@ -104,19 +144,18 @@ int uploadimage(const char *filename) {
vera_bit_depth = read8(); vera_bit_depth = read8();
vera_bit_depth &= 0b11; vera_bit_depth &= 0b11;
config |= vera_bit_depth; config |= vera_bit_depth;
if (vera_bit_depth > 3) { // Calculate the amount of pixels that can fit in a byte, used later
printf("Error: VERA bit depth was invalid!\n");
}
pixels_per_byte = 8 / bitdepth; pixels_per_byte = 8 / bitdepth;
printf("Bit depth: %u => %u pixels per byte\n", bitdepth, pixels_per_byte); printf("Bit depth: %u => %u pixels per byte\n", bitdepth, pixels_per_byte); // Debugging
width = read16(); width = read16(); // Get the image dimensions
height = read16(); height = read16();
printf("Width: %u, height: %u\n", width, height); printf("Width: %u, height: %u\n", width, height);
// Make sure the later update_scale call sets the correct scale
over320 = width > 320 || height > 240; over320 = width > 320 || height > 240;
filleridx = read8(); // Calculate the amount of bytes used within the image
printf("Border color: %02x\n", filleridx);
imgdatabytes = ((uint32_t)width * (uint32_t)height) / (uint32_t)pixels_per_byte; imgdatabytes = ((uint32_t)width * (uint32_t)height) / (uint32_t)pixels_per_byte;
printf("Bytes: %lu\n", imgdatabytes); printf("Bytes: %lu\n", imgdatabytes);
// Get the list of significant palette entires
significant_palette_entries = read8(); significant_palette_entries = read8();
significant_palette_start = read8(); significant_palette_start = read8();
if (significant_palette_entries == 0) { if (significant_palette_entries == 0) {
@ -126,60 +165,104 @@ int uploadimage(const char *filename) {
} else { } else {
printf("%u palette entries significant\nstarting at %u.\n", significant_palette_entries, significant_palette_start); printf("%u palette entries significant\nstarting at %u.\n", significant_palette_entries, significant_palette_start);
} }
imgdatastart = read16();
--imgdatastart;
printf("Image starts at 0-indexed offset %4x\n", imgdatastart);
direct_to_vera = width == vera_w && height == vera_h;
if ((int8_t)read8() == -1) {
compress = true;
};
printf("Skipping reserved bytes...\n"); printf("Skipping reserved bytes...\n");
for (i = 0; i < 19; i++) { for (i = 0; i < 16; i++) {
read8(); read8();
} }
// Load the border color
borderidx = read8();
printf("Border color: %02x\n", borderidx);
printf("Reading palette entries...\n"); printf("Reading palette entries...\n");
for (i = 0; i < 512; i++) { for (i = 0; i < (uint16_t)significant_palette_entries*2; i++) {
j = i >> 1; j = i >> 1;
if (all_significant || j >= significant_palette_start && j <= significant_palette_entries) { // Only load the required palette entries
if (all_significant || j >= significant_palette_start && j < significant_palette_start+significant_palette_entries || j == borderidx) {
palette[i] = read8(); palette[i] = read8();
} else { } else {
read8(); read8();
palette[i] = get_from_backed_up_palette(i); palette[i] = get_from_backed_up_palette(i);
} }
} }
// If the VERA was set up for a 640p image, make sure to keep track of that
if (over320) { if (over320) {
vera_w *= 2; vera_w *= 2;
vera_h *= 2; vera_h *= 2;
} }
// Calculate the maximum address and bank
vera_max_32 = ((uint32_t)vera_w * (uint32_t)vera_h)/(uint32_t)pixels_per_byte; vera_max_32 = ((uint32_t)vera_w * (uint32_t)vera_h)/(uint32_t)pixels_per_byte;
// Get just the max address
vera_max = vera_max_32; vera_max = vera_max_32;
// ... and the max bank all by itself
vera_max_bank = vera_max_32 >> 16; vera_max_bank = vera_max_32 >> 16;
// Update the VERA layer config
VERA.layer0.config = config; VERA.layer0.config = config;
// Make sure the TILEW value is set up correctly
VERA.layer0.tilebase = over320 ? 1 : 0; VERA.layer0.tilebase = over320 ? 1 : 0;
printf("Loading image bytes: $%lx\n", imgdatabytes); // Debugging
printf("Loading %simage bytes: $%lx\n", compress ? "compressed " : "", imgdatabytes);
printf("Image width: %u, image height: %u\npixels per byte: %u\n", width, height, pixels_per_byte); printf("Image width: %u, image height: %u\npixels per byte: %u\n", width, height, pixels_per_byte);
i = 0;
// Calculate the border value.
value = 0;
bitmask = (1 << bitdepth) - 1;
if (compress && !direct_to_vera) {
printf("Error: The image cannot be loaded due to limitations in memory_decompress.");
return 1;
}
for (i = 0; i < pixels_per_byte; i++) {
value |= (borderidx & bitmask) << (i * bitdepth);
}
if (compress) {
RAM_BANK = 1;
do {
i = cx16_k_macptr(0, true, image_end);
image_end += i;
if (RAM_BANK > 1) {
printf("Error: Cannot decompress memory because compressed data is too large.\n");
return 1;
}
if (i == 0) {
break;
}
} while (i != 0);
}
// Set up the VERA's data0 address
VERA.control &= ~0b1; VERA.control &= ~0b1;
VERA.address = 0; VERA.address = 0;
VERA.address_hi = 0b00010000; VERA.address_hi = 0b00010000;
i = 0; if (direct_to_vera) {
// Calculate the filler value. if (compress) {
value = 0; RAM_BANK = 1;
bitmask = (1 << bitdepth) - 1; image_start = BANK_RAM;
for (i = 0; i < pixels_per_byte; i++) { memory_decompress(image_start, &VERA.data0);
value |= (filleridx & bitmask) << (i * bitdepth); DEBUG_PORT1 = 0;
} else {
do {
bytes_read = cx16_k_macptr(0, false, &VERA.data0);
} while (bytes_read > 0);
} }
} else {
// Upload the bitmap // Upload the bitmap
for (x = 0; VERA.address < vera_max || (vera_max_bank & 0b1) != (VERA.address_hi & 0b1);) { for (x = 0; VERA.address < vera_max || (vera_max_bank & 0b1) != (VERA.address_hi & 0b1);) {
tmp = ((((uint32_t)VERA.address) + ((uint32_t)(VERA.address_hi & 0b1) << 16))); tmp = ((((uint32_t)VERA.address) + ((uint32_t)(VERA.address_hi & 0b1) << 16)));
x = (tmp % (uint32_t)(vera_w/pixels_per_byte))*pixels_per_byte; x = (tmp % (uint32_t)(vera_w/pixels_per_byte))*pixels_per_byte;
y = tmp / (uint32_t)(vera_w/pixels_per_byte); y = tmp / (uint32_t)(vera_w/pixels_per_byte);
#if 0
vera_adr_bak = VERA.address;
vera_adr_h_bak = VERA.address_hi;
printf("Y: %u, X: %u, tmp: %lu,\nvera_w: %u\nvera_adr: %u, vera_adr_h: %s\n", y, x, tmp, vera_w, vera_adr_bak, (vera_adr_h_bak & 1) ? "1" : "0");
VERA.address = vera_adr_bak;
VERA.address_hi = vera_adr_h_bak;
#endif
if (y >= height) { if (y >= height) {
break; break;
} }
if (x < width && y < height) { if (x < width && y < height) {
// Load the image's bytes, up to the bytes per row - the x value in bytes
bytes_read = cx16_k_macptr((((width) - x))/pixels_per_byte, false, &VERA.data0); bytes_read = cx16_k_macptr((((width) - x))/pixels_per_byte, false, &VERA.data0);
} else { } else {
//VERA.data0 = value; // Fill the remaining pixels in the row with the border color using a fast assembly routine
tmp = (vera_w-x)/pixels_per_byte; tmp = (vera_w-x)/pixels_per_byte;
if (tmp >= ((uint32_t)1 << 8)) { if (tmp >= ((uint32_t)1 << 8)) {
fill_vera(0xFF, value); fill_vera(0xFF, value);
@ -188,6 +271,7 @@ int uploadimage(const char *filename) {
} }
} }
} }
// Fill the rest of the image with the border color using the same fast assembly routine
for (; VERA.address < vera_max || (vera_max_bank & 0b1) != (VERA.address_hi & 0b1);) { for (; VERA.address < vera_max || (vera_max_bank & 0b1) != (VERA.address_hi & 0b1);) {
tmp = vera_max_32 - (((uint32_t)VERA.address) | ((uint32_t)(VERA.address_hi & 0b1) << 16)); tmp = vera_max_32 - (((uint32_t)VERA.address) | ((uint32_t)(VERA.address_hi & 0b1) << 16));
if (tmp >= ((uint32_t)1 << 8)) { if (tmp >= ((uint32_t)1 << 8)) {
@ -196,9 +280,12 @@ int uploadimage(const char *filename) {
fill_vera(tmp, value); fill_vera(tmp, value);
} }
} }
}
// Set up the VERA to display the bitmap
upload_palette(palette); upload_palette(palette);
updatescale(); updatescale();
vera_layer_enable(0b11); vera_layer_enable(0b11);
// Close the file
cbm_k_clrch(); cbm_k_clrch();
cbm_k_close(2); cbm_k_close(2);
return 0; return 0;

6
src/memory_decompress.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef MEMORY_DECOMPRESS_H
#define MEMORY_DECOMPRESS_H
extern void * __cdecl__ memory_decompress(const void *input, void *output);
#endif

19
src/memory_decompress.s Normal file
View file

@ -0,0 +1,19 @@
.export _memory_decompress
.import popax
r0 := $02
r0l := $02
r0h := $03
r1l := $04
r1h := $05
.segment "CODE"
_memory_decompress:
jsr popax
sta r1l
stx r1h
jsr popax
sta r0l
stx r0h
jsr $FEED
lda r1l
ldx r1h
rts