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 |
@echo off |
||||
setlocal |
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