mirror of https://github.com/wwarthen/RomWBW.git
Browse Source
Thanks and credit to Paul de Bak for providing the compression utility. Co-Authored-By: PauldB <169483608+p42db@users.noreply.github.com>pull/626/head v3.6.0-dev.34
13 changed files with 342 additions and 7 deletions
@ -1,3 +1,7 @@ |
|||
@echo off |
|||
setlocal |
|||
|
|||
if exist *.upd del *.upd |
|||
if exist *.cmp del *.cmp |
|||
if exist *.hex del *.hex |
|||
if exist *.lst del *.lst |
|||
|
|||
@ -0,0 +1,151 @@ |
|||
; 251003 decomp_rom_v1.1 |
|||
; Mark Pruden: Fix for omitted Bank 4 |
|||
; |
|||
; 250208 decomp_rom v1.0 |
|||
; Paul de Bak: Renamed and used in RomWBW utility program 'compress_upd.c' |
|||
; to compress *.upd binary files |
|||
; |
|||
;1/17/25 |
|||
;decompress RomWBW v1.0 - Original version by Bill Shen |
|||
;Routine in ROM that decompress RomWBW data file into RAM and jump to it. |
|||
;copy data file to RAM starting from bank 0, addr 0. |
|||
;when encountered two consecutive bytes of same values, the tird byte following the consecutive values |
|||
; describe how many more identical bytes need to be added. |
|||
;decompression ends when 3 banks (96KB) are expanded |
|||
;compressed data is stored from address $3 to below $FF00 |
|||
;decompress program is located at $FF00 |
|||
|
|||
;register usage |
|||
;regC points to bankreg |
|||
;regB is ROM source ($20) |
|||
;regD is RAM destination bank |
|||
;regE is previous value |
|||
;regHL is destination pointer |
|||
;regIX is source pointer |
|||
;regIY points to byte count |
|||
|
|||
bankreg equ 0ch ;PIO port C is 512K RAM bank register |
|||
SIOAData equ 8h ;location of SIO chan A data |
|||
SIOACmd equ 9h ;location of SIO ch A command/status reg |
|||
SIOBData equ 0ah ;location of SIO chan B data |
|||
SIOBCmd equ 0bh ;location of SIO ch B command/status reg |
|||
PORTCData equ 0ch ;PIA port C data |
|||
PORTCCmd equ 0dh ;PIA port C command |
|||
|
|||
org 0 |
|||
jp 0ff00h ;do program at top of memory |
|||
dataRomWBW: |
|||
;compressed RomWBW data appended here |
|||
org 0ff00h |
|||
ld a,09h ;write to KIO command reg first, enable PIA mux |
|||
out (0eh),a ; SIO-CTC-PIO priority daisy chain |
|||
|
|||
ld hl,0ff00h ;copy self into RAM |
|||
ld de,0ff00h |
|||
ld bc,100h |
|||
ldir |
|||
|
|||
xor a ;PortC are all outputs. This will cause both RAM and ROM |
|||
out (PORTCCmd),a ; to be enabled until following OUT instruction, since RAM and |
|||
; ROM have same contents, this won't cause contention |
|||
ld a,00100010b ; ROM enable, RAM disable, bank$2, nRTSA, nRTSB enabled |
|||
out (PORTCData),a |
|||
;register usage |
|||
;regC points to bankreg |
|||
;regB is ROM source ($20) |
|||
;regD is RAM destination bank |
|||
;regE is previous value |
|||
;regHL is destination pointer |
|||
;regIX is source pointer |
|||
;regIY points to byte count |
|||
ld c,bankreg ;regC points to bank register IO address |
|||
ld e,0 ;make sure regE is not the same as very first byte which is 0xC3 |
|||
ld b,20h ;regB is ROM source |
|||
ld d,80h ;regD is RAM destination |
|||
ld hl,0 ;destination starts from bank 0, address 0 |
|||
ld ix,dataRomWBW ;regIX points to compressed RomWBW |
|||
ld iy,cSame ;regIY points to the count of same value |
|||
start: |
|||
out (c),b ;read data from ROM |
|||
;these two lines are executing program in ROM which has same program as RAM |
|||
ld a,(ix) ;read from source |
|||
out (c),d |
|||
;running in RAM |
|||
ld (hl),a ;write to destination |
|||
cp e ;two consecutive same values? |
|||
jp z,decomp |
|||
ld e,a ;update the 'previous' value |
|||
inc ix ;next value in ROM |
|||
inc hl ;next destination value |
|||
ld a,h |
|||
cp 80h ;check for address greater than 32KB |
|||
jp z,nxtDbank |
|||
jp start |
|||
nxtDbank: |
|||
ld hl,0 |
|||
inc d ;next bank |
|||
ld a,d |
|||
;; cp 83h ;compare to bank 3 |
|||
cp 84h ;MP compare to bank 4 |
|||
jp z,decompDone |
|||
jp start |
|||
decomp: |
|||
out (c),b ;read data from ROM |
|||
;running in ROM |
|||
inc ix ;point to byte count of same value |
|||
ld a,(ix) ;get the count value |
|||
out (c),d ;switch to RAM bank |
|||
;running in RAM |
|||
dec a ;reduce by 1 because one byte is already written |
|||
ld (iy),a ;store count to cSame pointed by regIY |
|||
jp z,DoDecomp2 ;just two consecutive values |
|||
DoDecomp: |
|||
inc hl ;next destination value |
|||
ld a,h |
|||
cp 80h |
|||
jp z,nxtDbank1 |
|||
DoDecomp1: |
|||
ld (hl),e ;write previous value (in regE) to destination |
|||
dec (iy) |
|||
jp nz,DoDecomp |
|||
DoDecomp2: |
|||
;done with block decompression, get ready to start over |
|||
out (c),b |
|||
;running in ROM |
|||
inc ix ;point to next data in ROM |
|||
ld e,(ix) |
|||
dec e ;make sure previous value does not match, in case of large |
|||
; block (>256) of same values |
|||
out (c),d |
|||
;running in RAM |
|||
inc hl |
|||
ld a,h |
|||
cp 80h ;check for address grater than 32KB |
|||
jp z,nxtDbank2 |
|||
jp start |
|||
nxtDbank1: |
|||
ld hl,0 |
|||
inc d ;next bank |
|||
ld a,d |
|||
cp 83h ;compare to bank3 |
|||
jp z,decompDone |
|||
out (c),d ;update bank before continuing |
|||
jp DoDecomp1 |
|||
nxtDbank2: |
|||
ld hl,0 |
|||
inc d ;next bank |
|||
ld a,d |
|||
cp 83h ;compare to bank 3 |
|||
jp z,decompDone |
|||
jp start |
|||
decompDone: |
|||
;decompression is done, reaching 96KB of data |
|||
ld a,80h ;ROM disable, RAM enable, bank$0, nRTSA, nRTSB enabled |
|||
out (c),a |
|||
jp 0 ;execute RomWBW |
|||
|
|||
cSame: db 0 ;count of the same value |
|||
|
|||
end |
|||
|
|||
|
|||
Binary file not shown.
@ -0,0 +1,12 @@ |
|||
APP := compress |
|||
OBJS := compress.o |
|||
UNAME := $(shell uname) |
|||
|
|||
all: $(APP) |
|||
cp -p $(APP) ../../$(UNAME) |
|||
|
|||
$(APP): $(OBJS) |
|||
$(CC) $^ -o $@ |
|||
|
|||
clean: |
|||
@rm -rf $(APP) $(OBJS) |
|||
@ -0,0 +1,150 @@ |
|||
/* =====================================================================
|
|||
* compress v1.1 - 251012 Paul de Bak, with help from Le Chat AI |
|||
* |
|||
* ===================================================================== |
|||
* This RomWBW utility program will attempt to compress any file using |
|||
* a straightforward RLE approach. |
|||
* |
|||
* The output file will have the name of the input file with ".cmp" |
|||
* appended. |
|||
* |
|||
* ===================================================================== |
|||
* To compile in Linux (or Windows using MinGW): |
|||
* |
|||
* gcc -o compress_upd(.exe) compress_upd.c |
|||
* |
|||
* ===================================================================== |
|||
* Usage: compress(.exe) <input file> |
|||
* |
|||
* If compression is successful, an ouput file is created with the name |
|||
* of the input file + ".cmp". |
|||
* |
|||
* ===================================================================== |
|||
* Program exit codes |
|||
* 0: Success. An output file is created. |
|||
* 1: Incorrect usage or input file not found. |
|||
* 2: Unable to create output file. |
|||
* |
|||
* ===================================================================== |
|||
*/ |
|||
|
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <stdint.h> |
|||
//#include <string.h>
|
|||
//#include <libgen.h> // For basename function
|
|||
|
|||
#define VERSION "compress v1.1 - 251012 Paul de Bak & Le Chat AI" |
|||
#define MAX_COUNT 0x100 // Maximum count is 256
|
|||
#define BUFFER_SIZE 65536 |
|||
|
|||
size_t calculate_original_size(FILE *input) { |
|||
fseek(input, 0, SEEK_END); |
|||
return ftell(input); |
|||
} |
|||
|
|||
size_t calculate_compressed_size(FILE *input) { |
|||
uint8_t buffer[BUFFER_SIZE]; |
|||
size_t size, compressed_size = 0; |
|||
while ((size = fread(buffer, 1, BUFFER_SIZE, input)) > 0) { |
|||
size_t i = 0; |
|||
while (i < size) { |
|||
uint8_t byte = buffer[i]; |
|||
size_t count = 1; |
|||
// Count the number of repetitions of the current byte
|
|||
while (i + count < size && buffer[i + count] == byte && count < MAX_COUNT) { |
|||
count++; |
|||
} |
|||
if (count >= 2) { |
|||
compressed_size += 3; // Two bytes + count
|
|||
} else { |
|||
compressed_size += 1; // Single byte
|
|||
} |
|||
i += count; |
|||
} |
|||
} |
|||
return compressed_size; |
|||
} |
|||
|
|||
int compress_file(FILE *input, FILE *output) { |
|||
uint8_t buffer[BUFFER_SIZE]; |
|||
size_t size; |
|||
uint8_t repeat_count; |
|||
while ((size = fread(buffer, 1, BUFFER_SIZE, input)) > 0) { |
|||
size_t i = 0; |
|||
while (i < size) { |
|||
uint8_t byte = buffer[i]; |
|||
size_t count = 1; |
|||
// Count the number of repetitions of the current byte
|
|||
while (i + count < size && buffer[i + count] == byte && count < MAX_COUNT) { |
|||
count++; |
|||
} |
|||
if (count >= 2) { |
|||
fwrite(&byte, 1, 1, output); |
|||
fwrite(&byte, 1, 1, output); |
|||
repeat_count = count - 1; |
|||
fwrite(&repeat_count, 1, 1, output); |
|||
} else { |
|||
fwrite(&byte, 1, 1, output); |
|||
} |
|||
i += count; |
|||
} |
|||
} |
|||
return 1; |
|||
} |
|||
|
|||
int main(int argc, char *argv[]) { |
|||
char *input_filename; |
|||
long free_bytes; |
|||
FILE *input; |
|||
size_t original_size; |
|||
size_t compressed_size; |
|||
char output_filename[256]; |
|||
char *base_name; |
|||
char *dot; |
|||
FILE *output; |
|||
|
|||
printf("\n%s\n\n", VERSION); |
|||
|
|||
if (argc != 2) { |
|||
printf("Usage: %s <input file>\n", argv[0]); |
|||
printf("Example: %s RCZ80_ez512_std.upd\n", argv[0]); |
|||
return 1; // Error: Incorrect usage
|
|||
} |
|||
|
|||
input_filename = argv[1]; |
|||
input = fopen(input_filename, "rb"); |
|||
if (!input) { |
|||
fprintf(stderr, "Error: Input file '%s' not found\n", input_filename); |
|||
return 1; // Error: Input file not found
|
|||
} |
|||
|
|||
// Calculate the original file size
|
|||
original_size = calculate_original_size(input); |
|||
rewind(input); |
|||
|
|||
// Calculate the compressed size first
|
|||
compressed_size = calculate_compressed_size(input); |
|||
rewind(input); |
|||
|
|||
// Construct the output filename
|
|||
snprintf(output_filename, sizeof(output_filename), "%s.cmp", input_filename); |
|||
|
|||
printf("Compressing %s to %s...\n", input_filename, output_filename); |
|||
|
|||
output = fopen(output_filename, "wb"); |
|||
if (!output) { |
|||
fprintf(stderr, "Error: Unable to create output file '%s'\n", output_filename); |
|||
fclose(input); |
|||
return 2; // Error: Unable to create output file
|
|||
} |
|||
|
|||
compress_file(input, output); |
|||
|
|||
fclose(input); |
|||
fclose(output); |
|||
|
|||
printf("Compressed from %lu bytes to %lu bytes (%lu%% reduction)\n", original_size, compressed_size, (((original_size - compressed_size) * 100) / original_size)); |
|||
|
|||
return 0; // Success
|
|||
} |
|||
Loading…
Reference in new issue