init files

This commit is contained in:
AArt1256 2025-11-13 19:07:39 +03:00
commit 8197a022bd
1409 changed files with 139317 additions and 0 deletions

View file

@ -0,0 +1,42 @@
# Makefile for subsizer
# configuration
CPPFLAGS += -MMD -MP
CFLAGS= -O3 -march=native -Wall
# top level targets
all: subdirs subsizer
# source files
SRC = subsizer.c \
params.c match.c pathfinder.c universal.c bits-base.c \
crunch_normal.c \
buffer.c memory.c \
histogram.c \
bitfunc.c \
message.c \
utils.c \
global.c
# targets
subsizer: $(SRC:%.c=%.o) sfx/sfx.o
$(CC) $(CFLAGS) -o $@ $^ -lm
cp $@ ..
# clean
clean: subdirs
rm -f *~ \#*\#
rm -f *.o
rm -f *.d
rm -f a.out
rm -f subsizer
# handle dependencies
-include $(SRC:%.c=%.d)
# handle sub directories
export CC LD CPPFLAGS CFLAGS
subdirs:
$(MAKE) -C sfx $(MAKECMDGOALS)
# eof

View file

@ -0,0 +1,211 @@
/**************************************************************************
*
* FILE bitfunc.c
* Copyright (c) 2009-2011, 2013-2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* bitstream read/write functions.
*
******/
#include <stdint.h>
#include <stdlib.h>
#include "bitfunc.h"
/**************************************************************************
*
* SECTION bit writer
*
******/
void bitwr_init(BitWriteState *bws, uint8_t *destbuf, unsigned flags)
{
bws->ptr = destbuf;
bws->pos = 0;
bws->bits = 0;
bws->bit = 0;
bws->buf = 0;
bws->bpos = 0;
bws->flags = flags;
if (bws->flags & BITMODE_PRESHIFT) {
/*
* first byte shall be rol:ed one bit with a 1 inserted
* we reserve this bit here and apply the shift afterwards
*/
bitwr_write(bws, 0, 1);
}
}
void bitwr_write(BitWriteState *bws, uint32_t data, int n)
{
bws->bits += n;
/* mask source data */
data &= (1 << n) - 1;
/* process bits */
while (n) {
int fr = 8-bws->bit;
int nn = (n > fr) ? fr : n;
if (bws->bit == 0) {
bws->bpos = bws->pos;
bws->pos++;
}
bws->buf <<= nn;
bws->buf |= data >> (n-nn);
bws->bit += nn;
if (bws->bit == 8) {
bws->ptr[bws->bpos] = bws->buf;
bws->bit = 0;
}
n -= nn;
}
}
void bitwr_write8s(BitWriteState *bws, uint8_t data)
{
if ( !(bws->flags & BITMODE_SIDEBYTE) ) {
bitwr_write(bws, data, 8);
return;
}
bws->bits += 8;
bws->ptr[bws->pos] = data;
bws->pos++;
}
int bitwr_flush(BitWriteState *bws)
{
if (bws->bit > 0) {
bitwr_write(bws, 0, 8-bws->bit);
}
if (bws->flags & BITMODE_PRESHIFT) {
/*
* first byte shall be rol:ed one bit with a 1 inserted
* apply the shift here (MSB was unused since before)
*/
bws->ptr[0] = (bws->ptr[0] << 1) | 0x01;
}
return bws->pos;
}
/**************************************************************************
*
* SECTION bit reader
*
******/
void bitrd_init(BitReadState *brs, uint8_t *srcbuf, unsigned int flags)
{
brs->ptr = srcbuf;
brs->pos = 0;
brs->bits = 0;
brs->bit = 0;
brs->buf = 0;
brs->flags = flags;
if (brs->flags & BITMODE_PRESHIFT) {
/*
* first byte shall be ror:ed one bit with a 1 inserted
* and one bit pop:ed. Preserve buf[0] for later.
*/
uint8_t tmp = brs->ptr[0];
brs->ptr[0] >>= 1;
bitrd_read(brs, 1);
brs->ptr[0] = tmp;
}
}
uint32_t bitrd_read(BitReadState *brs, int n)
{
brs->bits += n;
uint32_t data = 0;
while (n) {
if (brs->bit == 0) {
brs->buf = brs->ptr[brs->pos];
brs->pos++;
brs->bit = 8;
}
int nn = (n > brs->bit) ? brs->bit : n;
data <<= nn;
data |= brs->buf >> (8-nn);
brs->buf <<= nn;
brs->bit -= nn;
n -= nn;
}
return data;
}
uint8_t bitrd_read8s(BitReadState *brs)
{
if ( !(brs->flags & BITMODE_SIDEBYTE) ) {
return bitrd_read(brs, 8);
}
brs->bits += 8;
uint8_t data = 0;
data = brs->ptr[brs->pos];
brs->pos++;
return data;
}
#if 0
/**************************************************************************
*
* SECTION test functions
*
******/
void bit_test(void)
{
int i;
BitWriteState bws;
BitReadState brs;
uint8_t buf[256];
int len;
bitwr_init(&bws, buf, 0);
bitwr_write(&bws, 1, 1);
bitwr_write(&bws, 0, 1);
bitwr_write(&bws, 3, 2);
bitwr_write(&bws, 0x5a6b7c, 24);
len=bitwr_flush(&bws);
printf("buf (%d): ",bws.bits);
for (i=0; i<len; i++) {
printf("%02X ",buf[i]);
}
printf("\n");
bitrd_init(&brs, buf, 0);
uint32_t d;
d=bitrd_read(&brs, 1);
printf("rd: %08X\n",d);
d=bitrd_read(&brs, 0);
printf("rd: %08X\n",d);
d=bitrd_read(&brs, 3);
printf("rd: %08X\n",d);
d=bitrd_read(&brs, 24);
printf("rd: %08X\n",d);
}
#endif
/* eof */

View file

@ -0,0 +1,51 @@
/**************************************************************************
*
* FILE bitfunc.h
* Copyright (c) 2009-2011, 2013-2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* bitstream read/write functions.
*
******/
#ifndef BITFUNC_H
#define BITFUNC_H
#include <stdint.h>
#define BITMODE_SIDEBYTE (1<<0)
#define BITMODE_PRESHIFT (1<<1)
//#define BITMODE_ROR (1<<2)
/* bit writer */
typedef struct {
uint8_t *ptr;
int pos;
int bits;
uint8_t buf;
int bit;
int bpos;
unsigned int flags;
} BitWriteState;
void bitwr_init(BitWriteState *bws, uint8_t *destbuf, unsigned int flags);
void bitwr_write(BitWriteState *bws, uint32_t data, int n);
void bitwr_write8s(BitWriteState *bws, uint8_t data);
int bitwr_flush(BitWriteState *bws);
/* bit reader */
typedef struct {
uint8_t *ptr;
int pos;
int bits;
uint8_t buf;
int bit;
unsigned int flags;
} BitReadState;
void bitrd_init(BitReadState *brs, uint8_t *srcbuf, unsigned int flags);
uint32_t bitrd_read(BitReadState *brs, int n);
uint8_t bitrd_read8s(BitReadState *brs);
#endif /* BITFUNC_H */
/* eof */

View file

@ -0,0 +1,390 @@
/**************************************************************************
*
* FILE bits-base.c
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* bits-base optimization
*
******/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "bitfunc.h"
#include "bits-base.h"
#include "global.h"
#include "histogram.h"
#include "universal.h"
#define DEBUG_COMPLEXITY 0
#define HAVE_CACHE 1
#define MAX_VALUES 0x10000
static unsigned int base_cost[MAX_PARTS];
static unsigned int so_far[MAX_VALUES+1];
static unsigned int cost_left[MAX_VALUES+1];
#if DEBUG_COMPLEXITY
static size_t n_entry, n_tested, n_copy, n_hits;
#endif
#if HAVE_CACHE
/**************************************************************************
*
* SECTION optimization cache
*
******/
#define CACHE_ENTRIES MAX_VALUES
typedef struct {
int len;
char parts[MAX_PARTS];
} CacheEntry;
static CacheEntry cache[CACHE_ENTRIES][MAX_PARTS];
static void invalidate_cache(void)
{
int id, bit;
for (id = 0; id < CACHE_ENTRIES; id++) {
for (bit = 0; bit < MAX_PARTS; bit++) {
cache[id][bit].len = -1;
}
}
}
static inline int hash_func(int base)
{
return base;
}
static inline void add_cache(int n_b, int bit, int base, char *enc, int len)
{
int i;
int id = hash_func(base);
cache[id][bit].len = len;
for (i = bit; i < n_b; i++) {
cache[id][bit].parts[i] = enc[i];
}
}
static inline int find_cache(int n_b, int bit, int base, char *enc)
{
int i;
int id = hash_func(base);
int l = cache[id][bit].len;
if (l >= 0) {
for (i = bit; i < n_b; i++) {
enc[i] = cache[id][bit].parts[i];
}
}
return l;
}
#endif
/**************************************************************************
*
* SECTION optimize encoding
*
******/
static int calc_enc(int n_b, int bit, int base, char *enc)
{
int i, min_len = 0x10000000;
char min_enc[MAX_PARTS];
#if DEBUG_COMPLEXITY
n_entry++;
#endif
for (i = 0; i < 16; i++) {
int len;
int lim = base + (1 << i);
/*
* this should consider the length encodings mapped to
* the particular offset.
* maybe it can be handled by subtracting from the costs.
*/
int cost = base_cost[bit] + i;
if (lim > MAX_VALUES-1) {
if (bit < n_b-1) {
break;
}
lim = MAX_VALUES;
}
len = (so_far[lim] - so_far[base]) * cost;
if (bit < n_b-1) {
if (cost_left[lim]) {
#if HAVE_CACHE
int tmp = find_cache(n_b, bit + 1, lim, min_enc);
if (tmp >= 0) {
len += tmp;
#if DEBUG_COMPLEXITY
n_hits++;
#endif
} else {
len += calc_enc(n_b, bit + 1, lim, min_enc);
}
#else
len += calc_enc(n_b, bit + 1, lim, min_enc);
#endif
} else {
/* didn't use all entries */
break;
}
} else {
/* out of bits, return the alternative cost */
len += cost_left[lim];
}
#if DEBUG_COMPLEXITY
n_tested++;
#endif
if (len < min_len) {
int j;
min_len = len;
enc[bit] = i;
for (j = bit+1; j < n_b; j++) {
enc[j] = min_enc[j];
}
#if DEBUG_COMPLEXITY
n_copy++;
#endif
}
}
#if HAVE_CACHE
add_cache(n_b, bit, base, enc, min_len);
#endif
return min_len;
}
static void build_arrays(Hist *h)
{
int i;
memset(so_far, 0, sizeof(so_far));
memset(cost_left, 0, sizeof(cost_left));
int acc = 0;
for (i = 0; i < h->range; i++) {
so_far[i] = acc;
acc += h->bin[i];
}
so_far[i] = acc;
double cost = 0;
cost_left[h->range] = 0;
for (i = h->range-1; i >= 0; i--) {
cost += h->cost[i];
cost_left[i] = cost;
}
}
void optimize_enc(Hist *h, int floor, int n, prefix_t prefix, Encoding *enc)
{
int i;
Encoding tmp;
if (!enc) {
enc = &tmp;
}
enc->floor = floor;
enc->n = n;
enc->prefix = prefix;
build_arrays(h);
for (i = 0; i < n; i++) {
switch (prefix) {
case PRE_BINARY:
base_cost[i] = ceil( log(n) / log(2) );
break;
case PRE_UNARY:
case PRE_UNARY_INV:
base_cost[i] = cost_unary(i, n);
break;
}
}
#if DEBUG_COMPLEXITY
n_entry = 0;
n_tested = 0;
n_copy = 0;
n_hits = 0;
#endif
#if HAVE_CACHE
invalidate_cache();
#endif
char bits[MAX_PARTS];
for (i = 0; i < n; i++) {
bits[i] = 0;
}
calc_enc(n, 0, floor, bits);
for (i = 0; i < n; i++) {
enc->parts[i] = bits[i];
}
#if DEBUG_COMPLEXITY
printf(" n_entry=%zu, n_tested=%zu, n_copy=%zu, n_hits=%zu\n", n_entry, n_tested, n_copy, n_hits);
#endif
}
/**************************************************************************
*
* NAME print_enc, print_enc_long
*
* DESCRIPTION
* Print out encoding
*
******/
void print_enc(Encoding *enc)
{
int i;
for (i = 0; i < enc->n; i++) {
printf("%X", enc->parts[i]);
}
}
void print_enc_long(Encoding *enc)
{
int i;
int lim = enc->floor;
printf("enc = { ");
for (i = 0; i < enc->n; i++) {
lim += 1 << enc->parts[i];
if (i != 0) {
printf(", ");
}
printf("%d", enc->parts[i]);
}
lim--;
printf(" } %d..%d\n", enc->floor, lim);
}
/**************************************************************************
*
* SECTION encoding
*
******/
int cost_enc(Encoding *enc, int v)
{
int i, of, base;
base = enc->floor;
of = -1;
for (i = 0; i < enc->n; i++) {
base += 1 << enc->parts[i];
if (v < base) {
of = i;
break;
}
}
if (of < 0) {
/* fault */
return 0x100000;
}
switch (enc->prefix) {
case PRE_BINARY:
return ceil( log(enc->n) / log(2) ) + enc->parts[of];
case PRE_UNARY:
case PRE_UNARY_INV:
return cost_unary(of, enc->n) + enc->parts[of];
default:
/* should never happen */
return 0x100000;
}
}
void write_enc(BitWriteState *bws, Encoding *enc, int v)
{
int i, of, base;
base = enc->floor;
of = -1;
for (i = 0; i < enc->n; i++) {
base += 1 << enc->parts[i];
if (v < base) {
of = i;
break;
}
}
if (of < 0) {
/* fault */
//return 0x100000;
}
switch (enc->prefix) {
case PRE_BINARY:
bitwr_write(bws, of, ceil( log(enc->n) / log(2) ));
break;
case PRE_UNARY:
write_unary(bws, of, enc->n, 0);
break;
case PRE_UNARY_INV:
write_unary(bws, of, enc->n, 1);
break;
}
bitwr_write(bws, v-base, enc->parts[of]);
}
int read_enc(BitReadState *brs, Encoding *enc)
{
int i, of, base;
switch (enc->prefix) {
case PRE_BINARY:
of = bitrd_read(brs, ceil( log(enc->n) / log(2) ));
break;
case PRE_UNARY:
of = read_unary(brs, enc->n, 0);
break;
case PRE_UNARY_INV:
of = read_unary(brs, enc->n, 1);
break;
default:
/* should never happen */
of = 0;
break;
}
base = enc->floor;
for (i = 0; i < of; i++) {
base += 1 << enc->parts[i];
}
return base + bitrd_read(brs, enc->parts[of]);
}
/* eof */

View file

@ -0,0 +1,42 @@
/**************************************************************************
*
* FILE bits-base.h
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* bits-base optimization
*
******/
#ifndef BITS_BASE_H
#define BITS_BASE_H
#include "bitfunc.h"
#include "histogram.h"
typedef enum {
PRE_BINARY = 0,
PRE_UNARY = 1,
PRE_UNARY_INV,
} prefix_t;
#define MAX_PARTS 16
typedef struct {
int floor;
int n;
prefix_t prefix;
char parts[MAX_PARTS];
} Encoding;
void optimize_enc(Hist *h, int floor, int n, prefix_t prefix, Encoding *enc);
void print_enc(Encoding *enc);
void print_enc_long(Encoding *enc);
int cost_enc(Encoding *enc, int v);
void write_enc(BitWriteState *bws, Encoding *enc, int v);
int read_enc(BitReadState *brs, Encoding *enc);
#endif /* BITS_BASE_H */
/* eof */

View file

@ -0,0 +1,164 @@
/**************************************************************************
*
* FILE buffer.c
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* handling of buffers
*
******/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "global.h"
#include "buffer.h"
#include "utils.h"
/**************************************************************************
*
* NAME create_buffer, destroy_buffer
*
* DESCRIPTION
* Create/destroy buffer.
*
******/
Buffer *create_buffer(size_t size)
{
Buffer *bf;
bf = safe_malloc(sizeof(Buffer), "Buffer");
bf->buf = safe_malloc(size, "Buffer buf");
bf->size = size;
bf->pos = 0;
bf->len = 0;
return bf;
}
void destroy_buffer(Buffer *bf)
{
free(bf->buf);
free(bf);
}
/**************************************************************************
*
* NAME create_buffer_from_file
*
* DESCRIPTION
* Create a new buffer from file.
*
******/
Buffer *create_buffer_from_file(const char *name)
{
Buffer *bf;
size_t len;
FILE *fp;
fp = fopen(name, "rb");
if (!fp) {
panic("couldn't open file for reading");
}
fseek(fp, 0, SEEK_END);
len = ftell(fp);
bf = create_buffer(len);
fseek(fp, 0, SEEK_SET);
uint8_t *b = bf->buf;
int l = len;
while (l > 0) {
size_t n;
n = fread(b, 1, l, fp);
b += n;
l -= n;
}
fclose(fp);
bf->len = len;
return bf;
}
/**************************************************************************
*
* NAME write_buffer_to_file
*
* DESCRIPTION
* Write buffer contents to file.
*
******/
void write_buffer_to_file(Buffer *bf, const char *name)
{
FILE *fp;
fp = fopen(name, "wb");
if (!fp) {
panic("couldn't open file for writing");
}
fwrite(bf->buf, 1, bf->len, fp);
fclose(fp);
}
/**************************************************************************
*
* NAME compare_buffer
*
* DESCRIPTION
* Compare buffer contents
*
******/
int compare_buffer(Buffer *bf1, Buffer *bf2)
{
if (bf1->len != bf2->len) {
return -1;
}
if (memcmp(bf1->buf, bf2->buf, bf1->len) != 0) {
return -1;
}
return 0;
}
/**************************************************************************
*
* NAME reverse_buffer
*
* DESCRIPTION
* Reverse buffer contents
*
******/
void reverse_buffer(Buffer *bf)
{
size_t i, l;
uint8_t *buf;
l = bf->len;
buf = bf->buf;
for (i = 0; i < l/2; i++) {
uint8_t b;
b = buf[l-i-1];
buf[l-i-1] = buf[i];
buf[i] = b;
}
}
/* eof */

View file

@ -0,0 +1,36 @@
/**************************************************************************
*
* FILE buffer.h
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* handling of buffers
*
******/
#ifndef BUFFER_H
#define BUFFER_H
#include <stdint.h>
#include <stdlib.h>
typedef struct {
uint8_t *buf;
size_t size;
size_t pos;
size_t len;
} Buffer;
Buffer *create_buffer(size_t size);
void destroy_buffer(Buffer *bf);
Buffer *create_buffer_from_file(const char *name);
void write_buffer_to_file(Buffer *bf, const char *name);
int compare_buffer(Buffer *bf1, Buffer *bf2);
void reverse_buffer(Buffer *bf);
#endif /* BUFFER_H */
/* eof */

View file

@ -0,0 +1,890 @@
/**************************************************************************
*
* FILE crunch_normal.c
* Copyright (c) 2015, 2016, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* optimize encoding and generate
*
******/
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "bitfunc.h"
#include "bits-base.h"
#include "buffer.h"
#include "pathfinder.h"
#include "global.h"
#include "histogram.h"
#include "match.h"
#include "memory.h"
#include "message.h"
#include "utils.h"
#include "sfx/fold.h"
#include "sfx/generate_sfx.h"
#define HAVE_THREE_BYTE 1
#define LEN_PARTS 16
#define SINGLE_BYTE_PARTS 4
#define TWO_BYTE_PARTS 16
#define THREE_BYTE_PARTS 16
#define LONG_MATCH_PARTS 16
#define MIN_MATCH 1
/**************************************************************************
*
* NAME optimize_encoding
*
* DESCRIPTION
* Use statistics of the primary units on the cheapest path to calculate
* an optimized encoding.
*
******/
struct EncodingSet {
int endm;
Encoding bitsl;
Encoding bits1;
Encoding bits2;
#if HAVE_THREE_BYTE
Encoding bits3;
#endif
Encoding bits;
};
static void print_enc_set(EncodingSet *es)
{
print_enc(&es->bitsl);
printf(",");
print_enc(&es->bits1);
printf(",");
print_enc(&es->bits2);
#if HAVE_THREE_BYTE
printf(",");
print_enc(&es->bits3);
#endif
printf(",");
print_enc(&es->bits);
printf("\n");
}
static void print_enc_stats(Hist *h, Encoding *enc, char *label)
{
printf("%s (n=%zu, e_min=%.2f): ", label, get_number(h), get_entropy(h));
print_enc_long(enc);
}
typedef struct {
Hist *h_len;
Hist *h_offs1;
Hist *h_offs2;
Hist *h_offs3;
Hist *h_offs;
} Stats;
static Stats *create_stats(void)
{
Stats *st;
st = safe_malloc(sizeof(Stats), "Stats");
st->h_len = create_histogram(0x10000, 0);
st->h_offs1 = create_histogram(0x10000, 0);
st->h_offs2 = create_histogram(0x10000, 0);
#if HAVE_THREE_BYTE
st->h_offs3 = create_histogram(0x10000, 0);
#endif
st->h_offs = create_histogram(0x10000, 0);
return st;
}
static void destroy_stats(Stats *st)
{
if (st) {
destroy_histogram(st->h_len);
destroy_histogram(st->h_offs1);
destroy_histogram(st->h_offs2);
#if HAVE_THREE_BYTE
destroy_histogram(st->h_offs3);
#endif
destroy_histogram(st->h_offs);
}
free(st);
}
static void collect_statistics(Stats *st, PrimaryPath *pp, CostFuncSet *cfs, EncodingSet *es)
{
Hist *h_lit = create_histogram(0x100, 0);
Hist *h_lit_run = create_histogram(0x10000, 0);
Hist *h_mat_run = create_histogram(0x10000, 0);
int src = 0;
int r_lit = 0;
int r_mat = 0;
Match *m = pp->path;
while (!is_end(m)) {
if (debug_g) {
printf("%d: ", src);
}
if (is_match(m) && get_match_len(m) >= MIN_MATCH) {
int l = get_match_len(m);
int of = get_match_offs(m);
if (debug_g) {
printf("match of=%d, l=%d", of, l);
}
/* accumulate histograms to optimize encoding */
/*
* these should also record the alternative cost of allowing a
* particular offset.
*
*/
double cost_lt = cfs->cost_lit(es, l);
double cost_of = 1 + cfs->cost_moffs(es, of, l);
double cost_l = 1 + cfs->cost_mlen(es, l);
hist_add(st->h_len, l, cost_lt - cost_of);
switch (l) {
case 1:
hist_add(st->h_offs1, of, cost_lt - cost_l);
break;
case 2:
hist_add(st->h_offs2, of, cost_lt - cost_l);
break;
#if HAVE_THREE_BYTE
case 3:
hist_add(st->h_offs3, of, cost_lt - cost_l);
break;
#endif
default:
hist_add(st->h_offs, of, cost_lt - cost_l);
break;
}
src += l;
/* handle histograms for literal and match runs. */
/*
* this should probably record the first occurrance
* and fill in the repeat there when flushed.
*/
if (r_lit) {
hist_add(h_lit_run, r_lit, 0);
r_lit = 0;
}
r_mat++;
} else {
int l = get_literal_len(m);
if (debug_g) {
printf("literal l=%d (", l);
}
while (l) {
uint8_t lit = pp->buf[src];
if (debug_g) {
printf("$%02x", lit);
if (l > 1) {
printf(" ");
}
}
hist_add(h_lit, lit, 0);
src++;
--l;
}
if (debug_g) {
printf(")");
}
/* handle histograms for literal and match runs. */
/*
* this should probably record the first occurrance
* and fill in the repeat there when flushed.
*/
if (r_mat) {
hist_add(h_mat_run, r_mat, 0);
r_mat = 0;
}
r_lit++;
}
m++;
if (debug_g) {
printf("\n");
}
}
/* flush any unflushed runs */
if (r_lit) {
hist_add(h_lit_run, r_lit, 0);
}
if (r_mat) {
hist_add(h_mat_run, r_mat, 0);
}
if (debug_g) {
printf("length=%d\n", src);
printf("max_lit_run=%d, max_mat_run=%d\n", get_histrange(h_lit_run), get_histrange(h_mat_run));
size_t n_lit = get_number(h_lit);
double e_lit = get_entropy(h_lit);
printf("literals (n=%zu, e_min=%.2f), theoretical gain = %d bytes\n", n_lit, e_lit, (int)(n_lit - (n_lit * e_lit / 8.0)));
#if 1
printf(" val len l=1 l=2 l=3 l>3 lit\n");
int i;
for (i = 0; i < 256; i++) {
printf(" %4d: %5zu %5zu %5zu %5zu %5zu %5zu\n", i, st->h_len->bin[i], st->h_offs1->bin[i], st->h_offs2->bin[i], st->h_offs3->bin[i], st->h_offs->bin[i], h_lit->bin[i]);
}
#endif
}
destroy_histogram(h_lit);
destroy_histogram(h_lit_run);
destroy_histogram(h_mat_run);
}
static void optimize_encoding(Stats *st, EncodingSet *es)
{
optimize_enc(st->h_len, MIN_MATCH, LEN_PARTS, PRE_UNARY, &es->bitsl);
optimize_enc(st->h_offs1, 1, SINGLE_BYTE_PARTS, PRE_BINARY, &es->bits1);
optimize_enc(st->h_offs2, 1, TWO_BYTE_PARTS, PRE_BINARY, &es->bits2);
#if HAVE_THREE_BYTE
optimize_enc(st->h_offs3, 1, THREE_BYTE_PARTS, PRE_BINARY, &es->bits3);
#endif
optimize_enc(st->h_offs, 1, LONG_MATCH_PARTS, PRE_BINARY, &es->bits);
/* find end marker candidate */
es->endm = 0; /* impossible marker */
int i;
for (i = MIN_MATCH; i < st->h_len->range; i++) {
if (st->h_len->bin[i] == 0) {
es->endm = i;
break;
}
}
#if 1
/* see if end marker is outside the encodable range (ugly, fixme!) */
if (cost_enc(&es->bitsl, es->endm) > 0x1000) {
printf("Warning: end marker out of range, forcing allocation!\n");
hist_add(st->h_len, es->endm, 1);
optimize_enc(st->h_len, MIN_MATCH, LEN_PARTS, PRE_UNARY, &es->bitsl);
}
#endif
if (debug_g) {
print_enc_stats(st->h_len, &es->bitsl, "lengths");
print_enc_stats(st->h_offs1, &es->bits1, "offs (l=1)");
print_enc_stats(st->h_offs2, &es->bits2, "offs (l=2)");
#if HAVE_THREE_BYTE
print_enc_stats(st->h_offs3, &es->bits3, "offs (l=3)");
print_enc_stats(st->h_offs, &es->bits, "offs (l>3)");
#else
print_enc_stats(st->h_offs, &es->bits, "offs (l>2)");
#endif
printf("end marker: l=%d\n", es->endm);
}
}
/**************************************************************************
*
* SECTION packer
*
******/
static inline double cost_lit(EncodingSet *es, int l)
{
return l * 9;
}
static inline double cost_mlen(EncodingSet *es, int l)
{
return cost_enc(&es->bitsl, l);
}
static void write_mlen(BitWriteState *bws, EncodingSet *es, int l)
{
write_enc(bws, &es->bitsl, l);
}
static int read_mlen(BitReadState *brs, EncodingSet *es)
{
return read_enc(brs, &es->bitsl);
}
static inline double cost_moffs(EncodingSet *es, int of, int l)
{
switch (l) {
case 1:
return cost_enc(&es->bits1, of);
case 2:
return cost_enc(&es->bits2, of);
#if HAVE_THREE_BYTE
case 3:
return cost_enc(&es->bits3, of);
#endif
default:
return cost_enc(&es->bits, of);
}
}
static void write_moffs(BitWriteState *bws, EncodingSet *es, int of, int l)
{
switch (l) {
case 1:
write_enc(bws, &es->bits1, of);
break;
case 2:
write_enc(bws, &es->bits2, of);
break;
#if HAVE_THREE_BYTE
case 3:
write_enc(bws, &es->bits3, of);
break;
#endif
default:
write_enc(bws, &es->bits, of);
break;
}
}
static int read_moffs(BitReadState *brs, EncodingSet *es, int l)
{
switch (l) {
case 1:
return read_enc(brs, &es->bits1);
case 2:
return read_enc(brs, &es->bits2);
#if HAVE_THREE_BYTE
case 3:
return read_enc(brs, &es->bits3);
#endif
default:
return read_enc(brs, &es->bits);
}
}
static inline int cost_endm(EncodingSet *es)
{
return 1 + cost_mlen(es, es->endm);
}
static void write_endm(BitWriteState *bws, EncodingSet *es)
{
bitwr_write(bws, 0, 1);
write_mlen(bws, es, es->endm);
}
static CostFuncSet cfs = {
cost_lit,
cost_mlen,
cost_moffs
};
static inline double cost_lit_init(EncodingSet *es, int l)
{
return l * 9;
}
static inline double cost_mlen_init(EncodingSet *es, int l)
{
return ceil( 0 + log(l) / log(2) );
}
static inline double cost_moffs_init(EncodingSet *es, int of, int l)
{
if (l == 1) {
return ceil( 0 + log(of) / log(2) );
}
return ceil( 2 + log(of) / log(2) );
}
static CostFuncSet cfs_init = {
cost_lit_init,
cost_mlen_init,
cost_moffs_init
};
static PrimaryPath *optimize_tree(MatchTree *mt, EncodingSet *es)
{
int max_passes = 16; /* should be configurable. */
int i;
PrimaryPath *pp;
Stats *st;
/* calculate initial encoding using doctored cost */
pp = find_cheapest_path(mt, &cfs_init, es, FCP_INITIAL_LITERAL);
st = create_stats();
collect_statistics(st, pp, &cfs_init, es);
optimize_encoding(st, es);
destroy_stats(st);
/*
* keep iterating find_cheapest_path + optimize_encoding until encoding
* no longer changes.
*/
EncodingSet last_es;
for (i = 0; i < max_passes; i++) {
memcpy(&last_es, es, sizeof(EncodingSet));
destroy_primarypath(pp);
if (verbose_g) {
print_enc_set(es);
printf("end marker: l=%d (cost=%d)\n", es->endm, cost_endm(es));
}
pp = find_cheapest_path(mt, &cfs, es, FCP_INITIAL_LITERAL);
/* endm + parts + space of end marker */
int overhead = 8 + 4 * (4*16 + 4) + cost_endm(es);
size_t cost = (pp->cost + overhead + 7) / 8;
msg(MSG_VERBOSE, " %zu (left %.2f%%)\n", cost, ((100.0 * cost) / pp->len));
st = create_stats();
collect_statistics(st, pp, &cfs, es);
optimize_encoding(st, es);
destroy_stats(st);
if (memcmp(es, &last_es, sizeof(EncodingSet)) == 0) {
break;
}
}
/* check for impossible marker */
if (es->endm == 0) {
fprintf(stderr, "error: couldn't find an end marker!\n");
}
return pp;
}
/**************************************************************************
*
* NAME generate
*
* DESCRIPTION
* Generate packed output from optimized structure.
*
******/
static int generate(PrimaryPath *pp, EncodingSet *es, uint8_t *buf, int flags)
{
int i;
BitWriteState bws;
int l;
bitwr_init(&bws, buf, BITMODE_SIDEBYTE | flags);
bitwr_write8s(&bws, es->endm);
for (i = 0; i < es->bitsl.n; i++) {
bitwr_write(&bws, es->bitsl.parts[i], 4);
}
for (i = 0; i < es->bits2.n; i++) {
bitwr_write(&bws, es->bits2.parts[i], 4);
}
#if HAVE_THREE_BYTE
for (i = 0; i < es->bits3.n; i++) {
bitwr_write(&bws, es->bits3.parts[i], 4);
}
#endif
for (i = 0; i < es->bits.n; i++) {
bitwr_write(&bws, es->bits.parts[i], 4);
}
for (i = 0; i < es->bits1.n; i++) {
bitwr_write(&bws, es->bits1.parts[i], 4);
}
l = 0;
Match *m = pp->path;
/* initial literal */
if (is_literal(m) && get_literal_len(m) == 1) {
bitwr_write8s(&bws, pp->buf[l]);
l++;
m++;
} else {
panic("internal fault");
}
while (!is_end(m)) {
if (is_match(m)) {
/* match */
bitwr_write(&bws, 0, 1);
write_mlen(&bws, es, get_match_len(m));
write_moffs(&bws, es, get_match_offs(m), get_match_len(m));
l += get_match_len(m);
} else {
int n = get_literal_len(m);
while (n) {
/* literal */
bitwr_write(&bws, 1, 1);
bitwr_write8s(&bws, pp->buf[l]);
l++;
--n;
}
}
m++;
}
/* end marker */
write_endm(&bws, es);
return bitwr_flush(&bws);
}
/**************************************************************************
*
* NAME crunch_normal_int
*
* DESCRIPTION
* Common cruncher parts
*
******/
static int crunch_normal_int(Buffer *sbf, Buffer *dbf, int flags)
{
MatchTree *mt = create_matchtree();
double t1, t2;
EncodingSet es;
memset(&es, 0, sizeof(EncodingSet));
msg(MSG_VERBOSE, "build matches...\n");
t1 = get_time();
build_match(mt, sbf->buf, sbf->len);
t2 = get_time();
msg(MSG_VERBOSE, "...%.2f s\n", t2 - t1);
msg(MSG_VERBOSE, "optimizing matches...\n");
t1 = get_time();
PrimaryPath *pp = optimize_tree(mt, &es);
t2 = get_time();
msg(MSG_VERBOSE, "...%.2f s\n", t2 - t1);
destroy_matchtree(mt);
msg(MSG_VERBOSE, "generating output...\n");
t1 = get_time();
dbf->len = generate(pp, &es, dbf->buf, flags);
t2 = get_time();
msg(MSG_VERBOSE, "...%.2f s\n", t2 - t1);
destroy_primarypath(pp);
return 0;
}
/**************************************************************************
*
* NAME decrunch_normal_int
*
* DESCRIPTION
* Common decruncher parts
*
******/
static int decrunch_normal_int(Buffer *sbf, Buffer *dbf, int flags, size_t *safep)
{
int i;
BitReadState brs;
int cur;
EncodingSet es;
int safe;
int spos;
uint8_t *destbuf = dbf->buf;
bitrd_init(&brs, sbf->buf, BITMODE_SIDEBYTE | flags);
es.endm = bitrd_read8s(&brs);
es.bitsl.floor = MIN_MATCH;
es.bitsl.n = LEN_PARTS;
es.bitsl.prefix = PRE_UNARY;
for (i = 0; i < es.bitsl.n; i++) {
es.bitsl.parts[i] = bitrd_read(&brs, 4);
}
es.bits2.floor = 1;
es.bits2.n = TWO_BYTE_PARTS;
es.bits2.prefix = PRE_BINARY;
for (i = 0; i < es.bits2.n; i++) {
es.bits2.parts[i] = bitrd_read(&brs, 4);
}
#if HAVE_THREE_BYTE
es.bits3.floor = 1;
es.bits3.n = THREE_BYTE_PARTS;
es.bits3.prefix = PRE_BINARY;
for (i = 0; i < es.bits3.n; i++) {
es.bits3.parts[i] = bitrd_read(&brs, 4);
}
#endif
es.bits.floor = 1;
es.bits.n = LONG_MATCH_PARTS;
es.bits.prefix = PRE_BINARY;
for (i = 0; i < es.bits.n; i++) {
es.bits.parts[i] = bitrd_read(&brs, 4);
}
es.bits1.floor = 1;
es.bits1.n = SINGLE_BYTE_PARTS;
es.bits1.prefix = PRE_BINARY;
for (i = 0; i < es.bits1.n; i++) {
es.bits1.parts[i] = bitrd_read(&brs, 4);
}
cur = 0;
safe = 0;
spos = dbf->len - sbf->len; /* extremely ugly: assumes dbf contains the unpacked data */
while (1) {
if (cur == 0 || bitrd_read(&brs, 1)) {
uint8_t c = bitrd_read8s(&brs);
destbuf[cur] = c;
cur++;
} else {
int len, offs;
len = read_mlen(&brs, &es);
if (len == es.endm) {
break;
}
offs = read_moffs(&brs, &es, len);
if (offs > cur) {
fprintf(stderr, "error: offset out of range\n");
break;
}
for (i = 0; i < len; i++) {
destbuf[cur] = destbuf[cur-offs];
cur++;
}
}
/* check safe distance */
int dist = cur - (brs.pos + spos);
if (dist > safe) {
safe = dist;
}
}
dbf->len = cur;
msg(MSG_DEBUG, "safe = %d\n", safe);
if (safep) {
*safep = safe;
}
return 0;
}
/**************************************************************************
*
* NAME crunch_normal
*
* DESCRIPTION
* Cruncher for the "normal" algorithm
*
******/
int crunch_normal(Buffer *sbf, Buffer *dbf)
{
return crunch_normal_int(sbf, dbf, 0);
}
/**************************************************************************
*
* NAME decrunch_normal
*
* DESCRIPTION
* Decruncher for the "normal" algorithm
*
******/
int decrunch_normal(Buffer *sbf, Buffer *dbf)
{
return decrunch_normal_int(sbf, dbf, 0, 0);
}
/**************************************************************************
*
* NAME crunch_normal_mem
*
* DESCRIPTION
* Memory cruncher for the "normal" algorithm
*
******/
int crunch_normal_mem(Memory *smem, Memory *dmem, int num_opts, char **opts)
{
int ret;
Buffer *sbf;
Buffer *dbf;
size_t safe = 0;
mem_ptr_t sa, ea, ca;
uint8_t endm, first;
int forwards_mode = num_opts; // FIXME: kludge!!!!
sbf = create_buffer(smem->high - smem->low);
sbf->len = smem->high - smem->low;
memcpy(sbf->buf, smem->buf + smem->low, sbf->len);
dbf = create_buffer(0x100000);
if (!forwards_mode) {
/* crunch in reverse */
reverse_buffer(sbf);
ret = crunch_normal_int(sbf, dbf, BITMODE_PRESHIFT);
/* check safe distance (reuse sbf as target) */
decrunch_normal_int(dbf, sbf, BITMODE_PRESHIFT, &safe);
destroy_buffer(sbf);
/* reverse result */
reverse_buffer(dbf);
} else {
/* crunch forwards */
ret = crunch_normal_int(sbf, dbf, BITMODE_PRESHIFT);
/* check safe distance (reuse sbf as target) */
decrunch_normal_int(dbf, sbf, BITMODE_PRESHIFT, &safe);
destroy_buffer(sbf);
}
msg(MSG_VERBOSE, "safe = %zu\n", safe);
//sa = 0x1000;
if (!forwards_mode) {
/* default output address is the minimum margin required for safe uncrunch */
sa = smem->low - safe;
/* generate output */
ca = sa;
ca += insert_mem(dmem, sa, dbf->buf, dbf->len);
/* pop first */
first = get_byte(dmem, --ca);
/* pop endm */
endm = get_byte(dmem, --ca);
/* add first */
set_byte(dmem, ca++, first);
/* add target address (in reverse) */
set_byte(dmem, ca++, (smem->high) & 0xff);
set_byte(dmem, ca++, (smem->high) >> 8);
/* add endm (adjusted) */
set_byte(dmem, ca++, endm - 1);
ea = ca;
} else {
/* default output address is the minimum margin required for safe uncrunch */
ea = smem->high + safe;
/* generate output */
ca = ea - dbf->len;
insert_mem(dmem, ca, dbf->buf, dbf->len);
/* pop first */
first = get_byte(dmem, ca++);
/* pop endm */
endm = get_byte(dmem, ca++);
/* add first */
set_byte(dmem, --ca, first);
/* add target address (in reverse) */
set_byte(dmem, --ca, (smem->low) & 0xff);
set_byte(dmem, --ca, (smem->low) >> 8);
/* add endm (adjusted) */
set_byte(dmem, --ca, endm - 1);
sa = ca;
}
dmem->low = sa;
dmem->high = ea;
destroy_buffer(dbf);
return ret;
}
/**************************************************************************
*
* NAME crunch_normal_sfx
*
* DESCRIPTION
* Executable cruncher for the "normal" algorithm
*
******/
int crunch_normal_sfx(Memory *smem, Memory *dmem, int num_opts, char **opts, int jmp)
{
int ret;
Buffer *sbf;
Buffer *dbf;
int endm;
uint8_t tmp;
size_t safe = 0;
SfxConfig *conf;
/* get initial options */
conf = prepare_sfx(num_opts, opts, jmp);
/* fold if applicable */
if (conf->fold) {
/* this should probably not change smem */
conf = fold(smem, 0, conf->fold, 0x10000, conf);
}
sbf = create_buffer(smem->high - smem->low);
sbf->len = smem->high - smem->low;
memcpy(sbf->buf, smem->buf + smem->low, sbf->len);
dbf = create_buffer(0x100000);
/* crunch in reverse */
reverse_buffer(sbf);
ret = crunch_normal_int(sbf, dbf, BITMODE_PRESHIFT);
/* check safe distance (reuse sbf as target) */
decrunch_normal_int(dbf, sbf, BITMODE_PRESHIFT, &safe);
destroy_buffer(sbf);
/* reverse result */
reverse_buffer(dbf);
/* pop endm from the end of the stream */
tmp = dbf->buf[dbf->len-1];
endm = dbf->buf[dbf->len-2];
dbf->buf[dbf->len-2] = tmp;
dbf->len--;
msg(MSG_VERBOSE, "safe = %zu\n", safe);
/* generate output */
generate_sfx(dbf, dmem, smem, safe, endm, conf);
destroy_buffer(dbf);
return ret;
}
/* eof */

View file

@ -0,0 +1,25 @@
/**************************************************************************
*
* FILE crunch_normal.h
* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* create encoding structure
*
******/
#ifndef CRUNCH_NORMAL_H
#define CRUNCH_NORMAL_H
#include <stdint.h>
#include "buffer.h"
#include "memory.h"
int crunch_normal(Buffer *sbf, Buffer *dbf);
int decrunch_normal(Buffer *sbf, Buffer *dbf);
int crunch_normal_mem(Memory *smem, Memory *dmem, int num_opts, char **opts);
int crunch_normal_sfx(Memory *smem, Memory *dmem, int num_opts, char **opts, int jmp);
#endif /* CRUNCH_NORMAL_H */
/* eof */

View file

@ -0,0 +1,33 @@
/**************************************************************************
*
* FILE global.c
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* Global functions.
*
******/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "global.h"
/* global variables */
int verbose_g;
int debug_g;
void panic(const char *str, ...)
{
va_list args;
fprintf(stderr, "%s: ", program_g);
va_start(args, str);
vfprintf(stderr, str, args);
va_end(args);
fputc('\n', stderr);
exit(1);
}
/* eof */

View file

@ -0,0 +1,27 @@
/**************************************************************************
*
* FILE global.h
* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* Global functions.
*
******/
#ifndef GLOBAL_H
#define GLOBAL_H
#include <stdarg.h>
#define PACKAGE "subsizer"
#define VERSION "0.7pre1"
/* global variables */
extern const char program_g[];
extern int verbose_g;
extern int debug_g;
void panic(const char *str, ...);
#endif /* GLOBAL_H */
/* eof */

View file

@ -0,0 +1,181 @@
/**************************************************************************
*
* FILE histogram.c
* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* handling of histograms
*
******/
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "global.h"
#include "histogram.h"
#include "utils.h"
Hist *create_histogram(int range, int window)
{
Hist *h;
h = safe_malloc(sizeof(Hist), "Histogram");
h->range = range;
h->bin = safe_calloc(range, sizeof(size_t), "histogram bins");
h->cost = safe_calloc(range, sizeof(double), "histogram costs");
h->window = window;
h->wbuf = 0;
h->wcnt = 0;
if (h->window) {
h->wbuf = safe_calloc(h->window, sizeof(HistEntry), "histogram window");
}
h->he = 0;
return h;
}
void destroy_histogram(Hist *h)
{
free(h->he);
free(h->wbuf);
free(h->bin);
free(h->cost);
free(h);
}
void hist_reset(Hist *h)
{
int i;
for (i = 0; i < h->range; i++) {
h->bin[i] = 0;
h->cost[i] = 0;
}
}
void hist_add(Hist *h, int v, double cost)
{
h->bin[v]++;
h->cost[v] += cost;
if (h->wbuf) {
HistEntry *he = &h->wbuf[h->wcnt];
if (he->n) {
h->bin[he->val]--;
h->cost[he->val] -= he->cost;
}
he->n = 1;
he->val = v;
he->cost = cost;
h->wcnt = (h->wcnt + 1) % h->window;
}
}
HistEntry *get_histlist(Hist *h)
{
int i;
int n;
HistEntry *he = h->he;
if (he) {
return he;
}
n = 0;
for (i = 0; i < h->range; i++) {
if (h->bin[i]) {
n++;
}
}
he = safe_malloc(sizeof(HistEntry) * (n + 1), "histogram list");
h->he = he;
for (i = 0; i < h->range; i++) {
if (h->bin[i]) {
he->val = i;
he->n = h->bin[i];
he->cost = h->cost[i];
he++;
}
}
he->n = 0;
return h->he;
}
/**************************************************************************
*
* NAME get_histrange
*
* DESCRIPTION
* get the actual range used.
*
******/
int get_histrange(Hist *h)
{
int i;
for (i = h->range-1; i >= 0; --i) {
if (h->bin[i])
break;
}
return i;
}
/**************************************************************************
*
* NAME get_number
*
* DESCRIPTION
* get the number of values added.
*
******/
size_t get_number(Hist *h)
{
int i;
size_t n;
n = 0;
for (i = 0; i < h->range; i++) {
n += h->bin[i];
}
return n;
}
/**************************************************************************
*
* NAME get_entropy
*
* DESCRIPTION
* Calculate the Shannon entropy of a histogram
*
******/
double get_entropy(Hist *h)
{
int i;
double e;
size_t n = get_number(h);
e = 0.0;
for (i = 0; i < h->range; i++) {
if (h->bin[i]) {
double p = (double)h->bin[i] / n;
e += -(log(p)/log(2)) * p;
}
}
return e;
}
/* eof */

View file

@ -0,0 +1,45 @@
/**************************************************************************
*
* FILE histogram.h
* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* handling of histograms
*
******/
#ifndef HISTOGRAM_H
#define HISTOGRAM_H
#include <stdint.h>
#include <stdlib.h>
typedef struct {
int val;
size_t n;
double cost;
} HistEntry;
typedef struct {
int range;
size_t *bin;
double *cost;
int window;
HistEntry *wbuf;
int wcnt;
HistEntry *he;
} Hist;
Hist *create_histogram(int range, int window);
void destroy_histogram(Hist *h);
void hist_reset(Hist *h);
void hist_add(Hist *h, int v, double cost);
HistEntry *get_histlist(Hist *h);
int get_histrange(Hist *h);
size_t get_number(Hist *h);
double get_entropy(Hist *h);
#endif /* HISTOGRAM_H */
/* eof */

View file

@ -0,0 +1,220 @@
/**************************************************************************
*
* FILE match.c
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* create match structure
*
******/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "global.h"
#include "match.h"
#include "utils.h"
/**************************************************************************
*
* SECTION packer
*
******/
MatchTree *create_matchtree(void)
{
MatchTree *mt;
mt = safe_malloc(sizeof(MatchTree), "MatchTree");
mt->match = 0;
mt->match_buf = 0;
mt->min_offs = 1;
mt->max_offs = 0x10000;
/* shortest match is l=1 */
/* 1 + 1 + 1 + 5 bits is less than 1 * literal */
//mt->min_offs1 = 1;
mt->max_offs1 = 32;
#if 0
/* 1 + 2 + 1 + 13 bits is less than 2 * literal */
//mt->min_offs2 = 1;
mt->max_offs2 = 0x2000;
#else
/* shortest match is l=2 */
/* 1 + 1 + 1 + 14 bits is less than 2 * literal */
//mt->min_offs2 = 1;
mt->max_offs2 = 0x4000;
#endif
mt->min_len = 1;
mt->max_len = 0x100;
mt->min_rle = 2;
mt->max_rle = 0x100;
mt->rle_holdoff = 8;
//mt->rle_holdoff = 1000000;
/* maybe allocate entries for the actual buffer already here? */
//printf("sizeof(Match) = %d\n", sizeof(Match));
return mt;
}
void destroy_matchtree(MatchTree *mt)
{
if (mt) {
free(mt->match);
free(mt->match_buf);
free(mt);
}
}
int build_match(MatchTree *mt, uint8_t *buf, int len)
{
int cur;
int rcnt;
mt->buf = buf;
mt->len = len;
mt->match = safe_malloc(len * sizeof(Match *), "match table");
/* this should be replaced by some dynamic realloc! */
mt->match_buf = safe_malloc(200000000 * sizeof(Match), "matches");
Match *cur_m = mt->match_buf;
/* do the processing */
rcnt = 0;
cur = 0;
while (cur < len) {
int i;
int window;
int rlen;
Match **mp = &mt->match[cur];
uint8_t v = buf[cur];
Match *last_m = cur_m;
/* find matches */
*mp = 0;
/* check rle */
rlen = 1;
while ((cur + rlen < len) && buf[cur + rlen] == v) {
rlen++;
if (rlen == mt->max_rle) {
break;
}
}
if (rlen >= mt->min_rle) {
/* skip the first rle */
if (rcnt > 0) {
make_rle(cur_m, cur, rlen);
cur_m++;
}
rcnt++;
if (rcnt > mt->rle_holdoff) {
goto cont; /* gaah! Clean me up! */
}
} else {
rcnt = 0;
}
/* max search range from the current offset */
window = (cur < mt->max_offs) ? cur : mt->max_offs;
/* seek for matches within that window */
for (i = mt->min_offs; i <= window; i++) {
int moffs = i;
int mlen = 0;
/*
* optimized search, first check if at least one byte matches,
* then start scanning with end check and similar
*/
if (buf[cur-i] == v) {
mlen++;
while ((cur+mlen < len) && buf[cur-i+mlen] == buf[cur+mlen]) {
mlen++;
if (mlen == mt->max_len) {
break;
}
}
}
/* this should probably consider the min_len. */
if ( (mlen >= mt->min_len) && (
(mlen >= 1 && moffs <= mt->max_offs1) ||
(mlen >= 2 && moffs <= mt->max_offs2) ||
(mlen >= 3) )
) {
make_match(cur_m, moffs, mlen);
cur_m++;
}
}
cont:
if (last_m != cur_m) {
make_end(cur_m);
cur_m++;
*mp = last_m;
}
cur++;
}
if (debug_g) {
int n_tot = 0, n_rle_tot = 0;
for (cur = 0; cur < len; cur++) {
printf("%d: $%02x, ", cur, mt->buf[cur]);
if (mt->match[cur] == 0) {
printf("literal\n");
} else {
int n = 0, n_rle = 0;
int max_match = 0, max_rle = 0;
Match *m = mt->match[cur];
while (m && !is_end(m)) {
if (is_rle(m)) {
int l = get_rle_len(m);
if (l > max_rle) {
max_rle = l;
}
n_rle++;
} else if (is_match(m)) {
int l = get_match_len(m);
if (l > max_match) {
max_match = l;
}
n++;
}
m++;
}
n_tot += n;
n_rle_tot += n_rle;
if (n_rle) {
printf("%d rle (l=%d)", n_rle, max_rle);
}
if (n) {
if (n_rle) {
printf(", ");
}
printf("%d matches (l=%d)", n, max_match);
}
printf("\n");
}
}
printf("%d total rle\n", n_rle_tot);
printf("%d total matches\n", n_tot);
}
return 0;
}
/* eof */

View file

@ -0,0 +1,134 @@
/**************************************************************************
*
* FILE match.h
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* create match structure
*
******/
#ifndef MATCH_H
#define MATCH_H
#include <stdint.h>
typedef struct {
/*
* MTYPE_END: ?
* MTYPE_LITERAL: ?
* MTYPE_MATCH: ?
* MTYPE_RLE: ?
*/
#define MTYPE_END 0
#define MTYPE_LITERAL 1
#define MTYPE_MATCH 2
#define MTYPE_RLE 3
uint32_t type:8;
uint32_t len:24;
uint32_t offs;
} Match;
static inline void make_end(Match *m)
{
m->type = MTYPE_END;
}
static inline int is_end(Match *m)
{
return m->type == MTYPE_END;
}
static inline void make_rle(Match *m, uint32_t src, int len)
{
m->type = MTYPE_RLE;
m->offs = src;
m->len = len;
}
static inline int is_rle(Match *m)
{
return m->type == MTYPE_RLE;
}
static inline int get_rle_len(Match *m)
{
return m->len;
}
static inline int get_rle_src(Match *m)
{
return m->offs;
}
static inline void make_match(Match *m, uint32_t offs, int len)
{
m->type = MTYPE_MATCH;
m->len = len;
m->offs = offs;
}
static inline int is_match(Match *m)
{
return m->type == MTYPE_MATCH;
}
static inline int get_match_len(Match *m)
{
return m->len;
}
static inline int get_match_offs(Match *m)
{
return m->offs;
}
static inline void make_literal(Match *m, uint32_t src, int len)
{
m->type = MTYPE_LITERAL;
m->offs = src;
m->len = len;
}
static inline int is_literal(Match *m)
{
return m->type == MTYPE_LITERAL;
}
static inline int get_literal_len(Match *m)
{
return m->len;
}
static inline int get_literal_src(Match *m)
{
return m->offs;
}
typedef struct {
int min_offs;
int max_offs;
int max_offs1;
int max_offs2;
int min_len;
int max_len;
int min_rle;
int max_rle;
int rle_holdoff;
uint8_t *buf;
int len;
Match **match;
Match *match_buf;
} MatchTree;
MatchTree *create_matchtree(void);
void destroy_matchtree(MatchTree *mt);
int build_match(MatchTree *mt, uint8_t *srcbuf, int srclen);
#endif /* MATCH_H */
/* eof */

View file

@ -0,0 +1,253 @@
/**************************************************************************
*
* FILE memory.c
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* handling of memory layouts
*
******/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "global.h"
#include "memory.h"
#include "message.h"
#include "utils.h"
/**************************************************************************
*
* NAME create_memory, destroy_memory
*
* DESCRIPTION
* Create/destroy memory.
*
******/
Memory *create_memory(size_t size)
{
Memory *mem;
mem = safe_malloc(sizeof(Memory), "Memory");
mem->buf = safe_calloc(size, 1, "Memory buf");
mem->size = size;
mem->low = -1;
mem->high = -1;
return mem;
}
void destroy_memory(Memory *mem)
{
free(mem->buf);
free(mem);
}
/**************************************************************************
*
* NAME get_byte, set_byte, get_word, set_word
*
* DESCRIPTION
* Memory accessors.
*
******/
uint8_t get_byte(Memory *mem, mem_ptr_t ad)
{
return mem->buf[ad & (mem->size-1)];
}
void set_byte(Memory *mem, mem_ptr_t ad, uint8_t val)
{
mem->buf[ad & (mem->size-1)] = val;
}
uint16_t get_word(Memory *mem, mem_ptr_t ad)
{
return get_byte(mem, ad) | (get_byte(mem, ad+1) << 8);
}
void set_word(Memory *mem, mem_ptr_t ad, uint16_t val)
{
set_byte(mem, ad, val & 0xff);
set_byte(mem, ad+1, val >> 8);
}
size_t insert_mem(Memory *dmem, mem_ptr_t da, uint8_t *src, size_t len)
{
memcpy(dmem->buf + da, src, len);
return len;
}
/**************************************************************************
*
* NAME load_mem
*
* DESCRIPTION
* load a file to memory.
*
******/
void load_mem(Memory *mem, file_t *f, mem_ptr_t *aptr, size_t *lptr)
{
FILE *fp;
size_t lrd;
mem_ptr_t ad;
mem_ptr_t la;
int c;
fp = fopen(f->name, "rb");
if (!fp) {
panic("couldn't open source file");
}
la = f->la;
switch (f->mode) {
case MODE_NORMAL:
/* get load address */
la = fgetc(fp) + (fgetc(fp) << 8);
break;
case MODE_NEWADDR:
/* skip load address */
fgetc(fp);
fgetc(fp);
break;
case MODE_RAW:
/* no load address */
break;
default:
break;
}
/* skip offset if any */
if (f->offs > 0) {
fseek(fp, f->offs, SEEK_CUR);
}
/* load file body */
ad = la;
lrd = 0;
while ( c = fgetc(fp), c != EOF ) {
set_byte(mem, ad, c);
ad++;
lrd++;
/* if a max len is specified, then terminate when it has been
reached. */
if (f->len > 0 && lrd >= f->len)
break;
}
fclose(fp);
msg(MSG_VERBOSE, "read '%s' $%04X-$%04X.\n", f->name, la, ad);
if (mem->low < 0 || mem->low > la) {
mem->low = la;
}
if (mem->high < 0 || mem->high < ad) {
mem->high = ad;
}
if (aptr) {
*aptr = la;
}
if (lptr) {
*lptr = lrd;
}
}
/**************************************************************************
*
* NAME load_file_to_memory
*
* DESCRIPTION
* load a file to memory.
*
******/
void load_file_to_memory(Memory *mem, const char *name)
{
size_t len;
FILE *fp;
size_t la;
size_t sa;
size_t ea;
fp = fopen(name, "rb");
if (!fp) {
panic("couldn't open file for reading");
}
fseek(fp, 0, SEEK_END);
len = ftell(fp);
fseek(fp, 0, SEEK_SET);
la = fgetc(fp) | (fgetc(fp) << 8);
len -= 2;
sa = la;
ea = sa + len;
uint8_t *b = mem->buf + sa;
int l = len;
while (l > 0) {
size_t n;
n = fread(b, 1, l, fp);
b += n;
l -= n;
}
fclose(fp);
if (mem->low < 0 || mem->low > sa) {
mem->low = sa;
}
if (mem->high < 0 || mem->high < ea) {
mem->high = ea;
}
}
/**************************************************************************
*
* NAME save_file_from_memory
*
* DESCRIPTION
* Write memory contents to file.
*
******/
void save_file_from_memory(Memory *mem, const char *name)
{
FILE *fp;
size_t la;
size_t sa;
size_t ea;
sa = mem->low;
ea = mem->high;
la = sa;
fp = fopen(name, "wb");
if (!fp) {
panic("couldn't open file for writing");
}
fputc(la & 0xff, fp);
fputc(la >> 8, fp);
fwrite(mem->buf + sa, 1, ea - sa, fp);
fclose(fp);
}
/* eof */

View file

@ -0,0 +1,61 @@
/**************************************************************************
*
* FILE memory.h
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* handling of memory layouts
*
******/
#ifndef MEMORY_H
#define MEMORY_H
#include <stdint.h>
#include <stdlib.h>
typedef uint32_t mem_ptr_t;
typedef struct {
uint8_t *buf;
size_t size;
int low;
int high;
} Memory;
Memory *create_memory(size_t size);
void destroy_memory(Memory *mem);
uint8_t get_byte(Memory *mem, mem_ptr_t ad);
void set_byte(Memory *mem, mem_ptr_t ad, uint8_t val);
uint16_t get_word(Memory *mem, mem_ptr_t ad);
void set_word(Memory *mem, mem_ptr_t ad, uint16_t val);
size_t insert_mem(Memory *dmem, mem_ptr_t da, uint8_t *src, size_t len);
enum mode_t {
MODE_NORMAL = 0,
MODE_NEWADDR,
MODE_RAW
};
typedef struct {
char *name;
enum mode_t mode;
mem_ptr_t la;
int offs;
int len;
} file_t;
void load_mem(Memory *mem, file_t *f, mem_ptr_t *aptr, size_t *lptr);
void load_file_to_memory(Memory *mem, const char *name);
void save_file_from_memory(Memory *mem, const char *name);
#endif /* MEMORY_H */
/* eof */

View file

@ -0,0 +1,50 @@
/**************************************************************************
*
* FILE message.c
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* Message handling
*
******/
#include <stdio.h>
#include <stdarg.h>
#include "global.h"
#include "message.h"
void vmsg(msg_level_t level, const char *s, va_list ap)
{
switch (level) {
case MSG_DEBUG:
if (!debug_g) {
return;
}
break;
case MSG_VERBOSE:
if (!verbose_g) {
return;
}
break;
default:
break;
}
vprintf(s, ap);
}
void msg(msg_level_t level, const char *s, ...)
{
va_list ap;
va_start(ap, s);
vmsg(level, s, ap);
va_end(ap);
}
/* eof */

View file

@ -0,0 +1,22 @@
/**************************************************************************
*
* FILE message.h
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* Message handling.
*
******/
#ifndef MESSAGE_H
#define MESSAGE_H
#include <stdarg.h>
typedef enum { MSG_DEBUG, MSG_VERBOSE } msg_level_t;
void msg(msg_level_t level, const char *s, ...);
void vmsg(msg_level_t level, const char *s, va_list ap);
#endif /* MESSAGE_H */
/* eof */

View file

@ -0,0 +1,155 @@
/**************************************************************************
*
* FILE params.c
* Copyright (c) 2012, 2015 Daniel Kahlin
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* parameter parsing
*
******/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "memory.h"
#include "global.h"
#include "params.h"
static unsigned long int my_strtoul(char *ptr, char **eptr)
{
int base;
unsigned long int v;
switch (*ptr) {
case '$':
base = 16;
ptr++;
break;
case '@':
base = 8;
ptr++;
break;
case '%':
base = 2;
ptr++;
break;
default:
base = 0;
break;
}
v = strtoul(ptr, eptr, base);
return v;
}
int parse_value(char *str)
{
char *eptr;
unsigned long int v;
v = my_strtoul(str, &eptr);
if ( str == eptr || *eptr != 0 )
panic("couldn't parse value (%s)", str);
return v;
}
void parse_range(char *str, mem_ptr_t *low, mem_ptr_t *high)
{
char *p;
if ( !(str && strlen(str)) ) {
return;
}
p = strchr(str, '-');
if (p) {
*p++ = 0;
if (*p != 0) {
*high = parse_value(p);
}
}
if (*str != 0) {
*low = parse_value(str);
}
}
file_t parse_filename(char *name)
{
char *p, *name_end;
char c;
char *eptr;
unsigned long int v;
file_t file;
file.name = name;
file.mode = MODE_NORMAL;
file.la = 0;
file.offs = -1;
file.len = -1;
p = name;
/* parse */
p = strpbrk(p, ",@");
if (p) {
name_end = p;
c = *p++;
switch (c) {
case ',':
v = my_strtoul(p, &eptr);
if (p == eptr && *p != ',') {
panic("missing start address");
}
if (p != eptr) {
file.la = v;
file.mode = MODE_NEWADDR;
}
p = eptr;
break;
case '@':
file.la = my_strtoul(p, &eptr);
if (p == eptr) {
panic("missing start address");
}
file.mode = MODE_RAW;
p = eptr;
break;
default:
break;
}
p = strchr(p, ',');
if (p) {
p++;
v = my_strtoul(p, &eptr);
if (p != eptr) {
file.offs = v;
}
p = strchr(p, ',');
if (p) {
p++;
file.len = parse_value(p);
}
}
/* mark the end of the file name */
*name_end = 0;
}
if (debug_g) {
printf("name='%s', ad=$%04X, offs=%d, len=%d, mode=%d\n", file.name, file.la, file.offs, file.len, file.mode);
}
return file;
}
/* eof */

View file

@ -0,0 +1,21 @@
/**************************************************************************
*
* FILE params.h
* Copyright (c) 2012, 2015 Daniel Kahlin
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* parameter parsing
*
******/
#ifndef PARAMS_H
#define PARAMS_H
#include "memory.h"
int parse_value(char *str);
void parse_range(char *str, mem_ptr_t *low, mem_ptr_t *high);
file_t parse_filename(char *str);
#endif /* PARAMS_H */
/* eof */

View file

@ -0,0 +1,327 @@
/**************************************************************************
*
* FILE pathfinder.c
* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* find optimized path by considering encoding cost
*
******/
#include <limits.h>
#include <stdio.h>
#include <stdint.h>
#include "bitfunc.h"
#include "pathfinder.h"
#include "global.h"
#include "match.h"
#include "message.h"
#include "utils.h"
#define DEBUG_COMPLEXITY 0
PrimaryPath *create_primarypath(int n, uint8_t *buf)
{
PrimaryPath *pp;
pp = safe_malloc(sizeof(PrimaryPath), "primarypath");
/* allocate n+1 entries to allow for the end marker */
pp->path = safe_malloc( (n + 1) * sizeof(Match), "matches");
pp->n = n;
pp->buf = buf;
/* insert end marker */
make_end(&(pp->path[n]));
return pp;
}
void destroy_primarypath(PrimaryPath *pp)
{
if (pp) {
free(pp->path);
free(pp);
}
}
/**************************************************************************
*
* SECTION fast cost functions
*
******/
double litcost[0x10000];
double lencost[0x10000];
double offscost1[0x10000];
double offscost2[0x10000];
double offscost3[0x10000];
double offscost[0x10000];
static void prepare_fast(CostFuncSet *cfs, EncodingSet *es)
{
int i;
/* prepare tables for fast cost calculations */
for (i = 0; i < 0x10000; i++) {
litcost[i] = cfs->cost_lit(es, i);
lencost[i] = cfs->cost_mlen(es, i);
offscost1[i] = cfs->cost_moffs(es, i, 1);
offscost2[i] = cfs->cost_moffs(es, i, 2);
offscost3[i] = cfs->cost_moffs(es, i, 3);
offscost[i] = cfs->cost_moffs(es, i, 4);
}
}
static inline double fast_lit(int l)
{
return litcost[l];
}
static inline double fast_mlen(int l)
{
return lencost[l];
}
static inline double fast_moffs(int of, int l)
{
switch (l) {
case 1:
return offscost1[of];
case 2:
return offscost2[of];
case 3:
return offscost3[of];
default:
return offscost[of];
}
}
/**************************************************************************
*
* NAME find_cheapest_path
*
* DESCRIPTION
* Consider encoding cost to find the cheapest path through all matches
*
******/
PrimaryPath *find_cheapest_path(MatchTree *mt, CostFuncSet *cfs, EncodingSet *es, unsigned int flags)
{
int i, j;
int cur;
int len = mt->len;
double *dist;
double *prev;
Match *path;
/* configuration */
int min_match = 1;
int enforce_exclusion = (flags & FCP_ENFORCE_EXCLUSION);
int literal_sequences = (flags & FCP_LITERAL_SEQUENCES);
int initial_literal = (flags & FCP_INITIAL_LITERAL);
/* construct lookup tables */
prepare_fast(cfs, es);
/* create path tables */
dist = safe_malloc(sizeof(double) * (len + 1), "dist table");
prev = safe_malloc(sizeof(double) * (len + 1), "prev table");
path = safe_malloc(sizeof(Match) * (len + 1), "match table");
/* initialize costs */
for (i = 0; i < len+1; i++) {
/* make sure we have headroom for a few additions. */
dist[i] = INT_MAX - 0x10000;
prev[i] = -1;
//path[i] = 0;
}
dist[0] = 0;
#if DEBUG_COMPLEXITY
int n_match, n_match_taken, n_rle, n_rle_taken, n_lt, n_lt_taken;
n_match = 0;
n_match_taken = 0;
n_rle = 0;
n_rle_taken = 0;
n_lt = 0;
n_lt_taken = 0;
#endif
/*
* calculate costs
*/
cur = 0;
while (cur < len) {
int v;
double w;
Match *m = mt->match[cur];
while (m && !is_end(m)) {
if (is_match(m) && get_match_len(m) >= min_match ) {
/* match */
int l = get_match_len(m);
int of = get_match_offs(m);
/*
* - does not consider when the escape bit isn't needed.
*/
/*
* scan through all possible shorter lengths and see if
* any are cheaper.
* TODO: which choices are actually interesting here?
*/
int c = 4;
while (c && l >= min_match) {
w = 1 + fast_mlen(l) + fast_moffs(of, l);
v = cur + l;
#if DEBUG_COMPLEXITY
n_match++;
#endif
if (dist[v] > dist[cur] + w) {
dist[v] = dist[cur] + w;
prev[v] = cur;
make_match(&path[v], of, l);
#if DEBUG_COMPLEXITY
n_match_taken++;
#endif
}
l--;
c--;
}
} else if (is_rle(m) && get_rle_len(m) >= min_match) {
/* match */
int l = get_rle_len(m);
int of = 1;
/*
* - does not consider when the escape bit isn't needed.
*/
/*
* scan through all possible shorter lengths and see if
* any are cheaper.
* TODO: which choices are actually interesting here?
*/
int c = 4;
while (c && l >= min_match) {
w = 1 + fast_mlen(l) + fast_moffs(of, l);
v = cur + l;
#if DEBUG_COMPLEXITY
n_rle++;
#endif
if (dist[v] > dist[cur] + w) {
dist[v] = dist[cur] + w;
prev[v] = cur;
make_match(&path[v], of, l);
#if DEBUG_COMPLEXITY
n_rle_taken++;
#endif
}
l--;
c--;
}
}
m++;
}
/* literal */
/*
* - should be expanded to handle all possible literal
* sequences
*/
if ( enforce_exclusion &&
(cur > 0) && (mt->buf[cur-1] == mt->buf[cur]) ) {
/* force unbearable cost for bytes breaking the
exclusion property */
w = 30000;
} else {
w = fast_lit(1);
}
v = cur + 1;
#if DEBUG_COMPLEXITY
n_lt++;
#endif
if (dist[v] > dist[cur] + w) {
dist[v] = dist[cur] + w;
prev[v] = cur;
make_literal(&path[v], cur, 1);
#if DEBUG_COMPLEXITY
n_lt_taken++;
#endif
}
if ( !( initial_literal && (cur == 0) ) && literal_sequences ) {
int l = (cur + 256 < len) ? 256 : len - cur;
while (l > 1) {
w = fast_lit(l);
v = cur + l;
#if DEBUG_COMPLEXITY
n_lt++;
#endif
if (dist[v] > dist[cur] + w) {
dist[v] = dist[cur] + w;
prev[v] = cur;
make_literal(&path[v], cur, l);
#if DEBUG_COMPLEXITY
n_lt_taken++;
#endif
}
l--;
}
}
cur++;
}
msg(MSG_DEBUG, "cost=%f bits (%f bytes)\n", dist[len], (dist[len]+7)/8);
#if DEBUG_COMPLEXITY
printf("n_match=%d (%d), n_rle=%d (%d), n_lit=%d (%d)\n", n_match, n_match_taken, n_rle, n_rle_taken, n_lt, n_lt_taken);
#endif
/*
* Backtrack the cheapest path to find the number of primary units
* to allocate.
*/
i = len;
j = 0;
while (i > 0) {
j++;
i = prev[i];
}
PrimaryPath *pp;
pp = create_primarypath(j, mt->buf);
pp->len = len;
pp->cost = dist[len];
/*
* Backtrack the cheapest path and create primary units accordingly.
*/
i = len;
j = pp->n-1;
while (i > 0) {
pp->path[j] = path[i];
j--;
i = prev[i];
}
/* free up path tables */
free(dist);
free(prev);
free(path);
return pp;
}
/* eof */

View file

@ -0,0 +1,47 @@
/**************************************************************************
*
* FILE pathfinder.h
* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* create encoding structure
*
******/
#ifndef PATHFINDER_H
#define PATHFINDER_H
#include <stdint.h>
#include <stdlib.h>
#include "match.h"
typedef struct {
int n;
double cost;
Match *path;
uint8_t *buf;
size_t len;
} PrimaryPath;
PrimaryPath *create_primarypath(int n, uint8_t *buf);
void destroy_primarypath(PrimaryPath *pp);
typedef struct EncodingSet EncodingSet;
typedef struct {
double (*cost_lit)(EncodingSet *es, int l);
double (*cost_mlen)(EncodingSet *es, int l);
double (*cost_moffs)(EncodingSet *es, int of, int l);
} CostFuncSet;
#define FCP_ENFORCE_EXCLUSION (1 << 0)
#define FCP_LITERAL_SEQUENCES (1 << 1)
#define FCP_INITIAL_LITERAL (1 << 2)
PrimaryPath *find_cheapest_path(MatchTree *mt, CostFuncSet *cfs, EncodingSet *es, unsigned int flags);
#endif /* PATHFINDER_H */
/* eof */

View file

@ -0,0 +1,22 @@
# Makefile
# top level targets
all: sfx.o
# source files
SRC = detect_start.c generate_sfx.c fold.c
# targets
sfx.o: $(SRC:%.c=%.o)
$(LD) -r $^ -o $@
# clean
clean:
rm -f *~ \#*\#
rm -f *.o
rm -f *.d
rm -f a.out
# handle dependencies
-include $(SRC:%.c=%.d)
# eof

View file

@ -0,0 +1,450 @@
;**************************************************************************
;*
;* FILE decrunch_normal.asm
;* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
;* Written by Daniel Kahlin <daniel@kahlin.net>
;*
;* DESCRIPTION
;* subsizer 0.7pre1 executable decruncher
;*
;******
processor 6502
seg code
mac NEW_PART
org $0000
dc.b {1},0
endm
;**************************************************************************
;*
;* Configuration options
;*
;******
ifnconst MAKE_EXE
HAVE_CLI equ 1
endif
HAVE_LONG_PARTS equ 1
if HAVE_LONG_PARTS
PART_MASK equ %00001111
N_PARTS equ 16
else
PART_MASK equ %00000111
N_PARTS equ 8
endif
NEW_PART "header"
subroutine header
org $0801
begin_header:
;**************************************************************************
;*
;* Basic line!
;*
;******
if 0
start_of_line:
dc.w end_line
dc.w 0
dc.b $9e,"2069 /T.L.R/",0
end_line:
dc.w 0
else
start_of_line:
dc.w end_line
dc.w 0
dc.b $9e,"2063 SUBSIZER!",0
end_line:
dc.b $a0,$00
endif
;**************************************************************************
;*
;* NAME start
;*
;******
start:
ifnconst MAKE_EXE
sei
lda #$34
sta $01
jmp tail
endif
end_header:
NEW_PART "tail"
subroutine tail
org $1000
begin_tail:
;**************************************************************************
;*
;* NAME tail
;*
;******
tail:
ifnconst MAKE_EXE
wrap equ $07e8
wrap_st equ $3000
WRAP_LEN equ $10
ldy #DECRUNCHER_LEN
tl_lp1:
lda.w decruncher_st-1,y
sta.w decruncher-1,y
cpy #WRAP_LEN+1
bcs tl_skp1
lda.w wrap_st-1,y
sta.w wrap-1,y
tl_skp1:
dey
bne tl_lp1
; Y = 0
endif
; ldy #0
dc_lp01:
ldx #4
jsr dc_get_bits
sta bits,y
tya
and #PART_MASK
bne dc_skp01
; Acc = 0
sta base_l,y
sta base_h,y
beq dc_skp02 ; always taken
dc_skp01:
lda #0
sta.z tmp_zp
ldx bits-1,y
sec
dc_lp02:
rol
rol.z tmp_zp
dex
bpl dc_lp02
; C = 0
; clc
adc base_l-1,y
sta base_l,y
lda.z tmp_zp
adc base_h-1,y
sta base_h,y
dc_skp02:
iny
cpy #N_PARTS*4+4
bne dc_lp01
; perform decrunch
ldy #0
jmp decrunch_entry
end_tail:
NEW_PART "decruncher"
org zp_end
subroutine decruncher
begin_decruncher:
;**************************************************************************
;*
;* NAME decruncher
;*
;* DESCRIPTION
;* decruncher
;*
;******
seg.u zp
org $f9
tmp_zp:
len_zp:
ds.b 1
copy_zp:
ds.w 1
hibits_zp:
ds.b 1
zp_end:
seg code
decruncher_st:
rorg zp_end
decruncher: ;+0ftDecruncher
dest_zp:
dc.w $0000 ;+0ftDestEnd
buf_zp:
dc.b $80 ;+0ftBufZp
if HAVE_LONG_PARTS
tabo:
dc.b 48,0,16,32
tabb:
dc.b 2,4,4,4
else
tabo:
dc.b 24,0,8,16
tabb:
dc.b 2,3,3,3
endif
decrunch_entry:
dc_literal:
lda dest_zp
bne dc_skp5
dec dest_zp+1
dc_skp5:
dec dest_zp
jsr dc_get_byte
; ldy #0
sta (dest_zp),y
; bcs dc_lp1 ; always taken
decrunch_main:
; Y = 0
;------
; perform actual decrunch
dc_lp1:
jsr dc_get_bit
bcs dc_literal
; Y = 0
; get length as bits/base.
; ldy #$ff+1
dc_lp2:
iny
cpy #N_PARTS
beq dc_skp0
jsr dc_get_bit
bcc dc_lp2
dc_skp0:
ldx bits_len-1,y
jsr dc_get_bits
; C = 0
adc base_len-1,y
sta len_zp
; C = 0
;******
;* IN: len = $01..$100 (Acc = $00..$ff)
;* OUT: dest_zp = dest_zp - len, Y = len-1
;*
tay
; clc
eor #$ff
adc dest_zp
sta dest_zp
bcs dc_skp22
dec dest_zp+1
dc_skp22:
; check end marker here to avoid thrashing carry earlier
cpy #$00 ;+1ftEndMarkerMinusOne
beq done
;******
;* Get selector bits depending on length.
;*
;* IN: len = $01..$100 (Y = $00..$ff)
;* OUT:
;*
cpy #4
bcc dc_skp2
ldy #3
dc_skp2:
; get offset as bits/base.
ldx tabb,y
jsr dc_get_bits
; C = 0
adc tabo,y
tay
ldx bits_offs,y
jsr dc_get_bits
; C = 0
adc base_offs_l,y
tax
lda hibits_zp
adc base_offs_h,y
tay
; X/Y = offset - 1
sec
txa
adc dest_zp
sta copy_zp
tya
adc dest_zp+1
sta copy_zp+1
;******
;* Reverse fast copy
;*
;* IN: len = $01..$100 (len_zp = $00..$ff), C = 0
;*
ldy len_zp
beq dc_skp4
dc_lp4:
lda (copy_zp),y
sta (dest_zp),y
dey
bne dc_lp4
dc_skp4:
lda (copy_zp),y
sta (dest_zp),y
bcc dc_lp1 ; always taken
;**************************************************************************
;*
;* NAME dc_get_bits
;*
;* DESCRIPTION
;* Get bits from the packed stream.
;*
;* IN:
;* X = number of bits to get
;*
;* OUT:
;* Acc = bit 7-0
;* hibits_zp = bit 15-8
;* C = bit 16
;* Y = preserved
;* X = 0
;* Z = 1
;*
;******
dc_get_bits:
lda #0
sta hibits_zp
cpx #1
bcc dcg_ex1
dcg_lp1:
asl buf_zp
bne dcg_skp1
; C=1 (because the marker bit was just shifted out)
pha
jsr dc_get_byte
rol
sta buf_zp
pla
dcg_skp1:
rol
rol hibits_zp
dex
bne dcg_lp1 ; C=0 for all X!=0
dcg_ex1:
rts
;**************************************************************************
;*
;* NAME dc_get_bit
;*
;* DESCRIPTION
;* Get one bit from the packed stream into carry
;*
;* IN:
;* -
;*
;* OUT:
;* Acc = ?
;* C = bit 0
;* Y = preserved
;* X = preserved
;* Z = 0
;*
;******
dc_get_bit:
asl buf_zp
bne dcgb_ex1
; C=1 (because the marker bit was just shifted out)
jsr dc_get_byte
rol
sta buf_zp
dcgb_ex1:
rts
;**************************************************************************
;*
;* NAME dc_get_byte
;*
;* DESCRIPTION
;* Get byte from the packed stream.
;*
;******
dc_get_byte:
lda dc_ptr
bne dcgb_skp1
dec dc_ptr+1
dcgb_skp1:
dec dc_ptr
dc_ptr equ . + 1
lda.w $0000 ;+1ftSrcEnd
rts
;******
;* exit out
done:
ifnconst MAKE_EXE
lda #$37
sta $01
if HAVE_CLI
cli
endif
jmp $0830
endif
rend
DECRUNCHER_LEN equ . - decruncher_st
end_decruncher:
seg.u tables
org $0334
begin_tables:
;**************************************************************************
;*
;* NAME base_l, base_h, bits
;*
;* DESCRIPTION
;* Data for bits/base decoding.
;*
;******
base_l:
base_len:
ds.b N_PARTS,0
base_offs_l:
ds.b N_PARTS*3+4,0
base_h equ . - N_PARTS
; ds.b N_PARTS,0
base_offs_h:
ds.b N_PARTS*3+4,0
bits:
bits_len:
ds.b N_PARTS,0
bits_offs:
ds.b N_PARTS*3+4,0
end_tables:
ifnconst MAKE_EXE
echo "header", begin_header, end_header, end_header-begin_header
echo "tail", begin_tail, end_tail, end_tail-begin_tail
echo "decruncher", begin_decruncher, end_decruncher, end_decruncher-begin_decruncher
echo "[run] decruncher", decruncher, decruncher+DECRUNCHER_LEN, DECRUNCHER_LEN
echo "[run] tables", begin_tables, end_tables, end_tables-begin_tables
endif
; eof

View file

@ -0,0 +1,474 @@
;**************************************************************************
;*
;* FILE decrunch_normal_dirty.asm
;* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
;* Written by Daniel Kahlin <daniel@kahlin.net>
;*
;* DESCRIPTION
;* subsizer 0.7pre1 executable decruncher - dirty version
;*
;******
processor 6502
seg code
mac NEW_PART
org $0000
dc.b {1},0
endm
;**************************************************************************
;*
;* Configuration options
;*
;******
ifnconst MAKE_EXE
HAVE_CLI equ 1
endif
HAVE_LONG_PARTS equ 1
if HAVE_LONG_PARTS
PART_MASK equ %00001111
N_PARTS equ 16
else
PART_MASK equ %00000111
N_PARTS equ 8
endif
NEW_PART "header"
subroutine header
org $0801
begin_header:
;**************************************************************************
;*
;* Basic line!
;*
;******
if 0
start_of_line:
dc.w end_line
dc.w 0
dc.b $9e,"2069 /T.L.R/",0
end_line:
dc.w 0
else
start_of_line:
dc.w end_line
dc.w 0
dc.b $9e,"2063 SUBSIZER!",0
end_line:
dc.b $a0,$00
endif
;**************************************************************************
;*
;* NAME start
;*
;******
start:
ifnconst MAKE_EXE
sei
lda #$34
sta $01
jmp tail
endif
end_header:
NEW_PART "tail"
subroutine tail
org $1000
begin_tail:
;**************************************************************************
;*
;* NAME tail
;*
;******
tail:
ifnconst MAKE_EXE
wrap equ $07e8
wrap_st equ $3000
WRAP_LEN equ $10
ldx #DECRUNCHER_LEN
tl_lp1:
lda.w decruncher_st-1,x
sta.w decruncher-1,x
cpx #WRAP_LEN+1
bcs tl_skp1
lda.w wrap_st-1,x
sta.w wrap-1,x
tl_skp1:
dex
bne tl_lp1
; X = 0
endif
; ldx #0
dc_lp01:
;******
;* get 4 bits
; could be optimized by storing the bits in zp from the beginning and
; then shifting out 4 bits at a time, increasing the ptr.
lda #%11100000
dcg_lp1:
asl.z buf_zp
bne dcg_skp1
; C=1 (because the marker bit was just shifted out)
tay
jsr dc_get_byte
rol
sta.z buf_zp
tya
dcg_skp1:
rol
bcs dcg_lp1
; Acc = 4 bits.
sta.z bits,x
txa
and #PART_MASK
tay
beq dc_skp01
lda #0
sta.z hibits_zp
ldy.z bits-1,x
sec
dc_lp02:
rol
rol.z hibits_zp
dey
bpl dc_lp02
; C = 0
; clc
adc.z base_l-1,x
tay
lda.z hibits_zp
adc.z base_h-1,x
dc_skp01:
sta.z base_h,x
sty.z base_l,x
inx
cpx #N_PARTS*4+4
bne dc_lp01
; perform decrunch
ldy #0
jmp decrunch_entry
end_tail:
NEW_PART "decruncher"
org zp_end
subroutine decruncher
begin_decruncher:
;**************************************************************************
;*
;* NAME decruncher
;*
;* DESCRIPTION
;* decruncher
;*
;******
seg.u zp
org $be
hibits_zp:
ds.b 1
zp_end:
seg code
decruncher_st:
rorg zp_end
decruncher: ;+0ftDecruncher
buf_zp:
dc.b $80 ;+0ftBufZp
if HAVE_LONG_PARTS
tabb:
dc.b %10000000 | [48 >> 2] ; 2 bits
dc.b %11100000 | [0 >> 4] ; 4 bits
dc.b %11100000 | [16 >> 4] ; 4 bits
dc.b %11100000 | [32 >> 4] ; 4 bits
else
tabb:
dc.b %10000000 | [24 >> 2] ; 2 bits
dc.b %11000000 | [0 >> 3] ; 3 bits
dc.b %11000000 | [8 >> 3] ; 3 bits
dc.b %11000000 | [16 >> 3] ; 3 bits
endif
;******
;* get bit macro
mac get_bit
asl.z buf_zp
bne .gb_skp1
; C=1 (because the marker bit was just shifted out)
jsr dc_get_byte
rol
sta.z buf_zp
.gb_skp1:
endm
;******
;* get bits max8 macro
mac get_bits_max8
.gb_lp1:
asl.z buf_zp
bne .gb_skp1
; C=1 (because the marker bit was just shifted out)
pha
jsr dc_get_byte
rol
sta.z buf_zp
pla
.gb_skp1:
rol
dey
bne .gb_lp1
endm
;******
;* get bits max8 masked macro
mac get_bits_max8_masked
.gb_lp1:
asl.z buf_zp
bne .gb_skp1
; C=1 (because the marker bit was just shifted out)
tay
jsr dc_get_byte
rol
sta.z buf_zp
tya
.gb_skp1:
rol
bcs .gb_lp1
endm
;******
;* get bits max16 macro
mac get_bits_max16
.gb_lp1:
asl.z buf_zp
bne .gb_skp1
; C=1 (because the marker bit was just shifted out)
pha
jsr dc_get_byte
rol
sta.z buf_zp
pla
.gb_skp1:
rol
rol.z hibits_zp
dey
bne .gb_lp1 ; C=0 for all Y!=0
endm
;**************************************************************************
;*
;* NAME dc_get_byte
;*
;* DESCRIPTION
;* Get byte from the packed stream.
;*
;******
dc_get_byte:
lda.z dc_ptr
bne dcgb_skp1
dec.z dc_ptr+1
dcgb_skp1:
dec.z dc_ptr
dc_ptr equ . + 1
lda.w $0000 ;+1ftSrcEnd
rts
;******
;* Reverse fast copy
;*
;* IN: len = $01..$100 (len_zp = $00..$ff), C = 0
;*
copy:
len_zp equ . + 1
ldy #0
beq dc_skp4
dc_lp4:
copy_zp equ .+1
lda.w $0000,y
dest_zp equ .+1
sta.w $0000,y ;+1ftDestEnd
dey
bne dc_lp4
dc_skp4:
lda (copy_zp),y
; sta (dest_zp),y
bcc dc_common ; always taken
decrunch_entry:
dc_literal:
lda dest_zp
bne dc_skp5
dec dest_zp+1
dc_skp5:
dec dest_zp
jsr dc_get_byte
; ldy #0
dc_common:
sta (dest_zp),y
; fall through
decrunch_main:
;------
; perform actual decrunch
dc_lp1:
get_bit
bcs dc_literal
; get length as bits/base.
ldx #$100-N_PARTS
dc_lp2:
inx
beq dc_skp0
get_bit
bcc dc_lp2
clc
dc_skp0:
; C = 0, Y = 0
; lda #0
tya
ldy.z [bits_len+N_PARTS-1]&$ff,x
beq dcb1_skp2
get_bits_max8
dcb1_skp2:
; C = 0
adc.z [base_len+N_PARTS-1]&$ff,x
sta len_zp
; C = 0
;******
;* IN: len = $01..$100 (Acc = $00..$ff)
;* OUT: dest_zp = dest_zp - len, X = len-1
;*
tax
; clc
eor #$ff
adc dest_zp
sta dest_zp
bcs dc_skp22
dec dest_zp+1
dc_skp22:
; check end marker here to avoid thrashing carry earlier
cpx #$00 ;+1ftEndMarkerMinusOne
beq done
;******
;* Get selector bits depending on length.
;*
;* IN: len = $01..$100 (X = $00..$ff)
;* OUT:
;*
cpx #4
bcc dc_skp2
ldx #3
dc_skp2:
; get offset as bits/base.
lda tabb,x
get_bits_max8_masked
tax
; C = 0
lda #0
sta hibits_zp
ldy.z bits_offs,x
beq dcb3_skp2
get_bits_max16
dcb3_skp2:
; C = 0, Acc/hibits_zp + base_offs,x = offset - 1
; perform: copy_zp = Acc/hibits_zp + base_offs,x + 1 + dest_zp
; result: copy_zp = dest_zp + offset
adc.z base_offs_l,x
bcc dcb3_skp3
inc hibits_zp
dcb3_skp3:
sec
adc dest_zp
sta copy_zp
lda hibits_zp
adc.z base_offs_h,x
; C = 0
adc dest_zp+1
sta copy_zp+1
jmp copy
done:
ifnconst MAKE_EXE
lda #$37
sta $01
if HAVE_CLI
cli
endif
jmp $0830
endif
rend
DECRUNCHER_LEN equ . - decruncher_st
end_decruncher:
seg.u tables
org $0002
begin_tables:
;**************************************************************************
;*
;* NAME base_l, base_h, bits
;*
;* DESCRIPTION
;* Data for bits/base decoding.
;*
;******
base_l:
base_len:
ds.b N_PARTS,0
base_offs_l:
ds.b N_PARTS*3+4,0
base_h equ . - N_PARTS
; ds.b N_PARTS,0
base_offs_h:
ds.b N_PARTS*3+4,0
bits:
bits_len:
ds.b N_PARTS,0
bits_offs:
ds.b N_PARTS*3+4,0
end_tables:
ifnconst MAKE_EXE
echo "header", begin_header, end_header, end_header-begin_header
echo "tail", begin_tail, end_tail, end_tail-begin_tail
echo "decruncher", begin_decruncher, end_decruncher, end_decruncher-begin_decruncher
echo "[run] decruncher", decruncher, decruncher+DECRUNCHER_LEN, DECRUNCHER_LEN
echo "[run] tables", begin_tables, end_tables, end_tables-begin_tables
endif
; eof

View file

@ -0,0 +1,37 @@
/* autogenerated by make_exe.pl, do not edit */
#ifndef DECRUNCHERS_H
#define DECRUNCHERS_H
#include <stdint.h>
enum fixtype_t {
ftBufZp = 1,
ftDecruncher,
ftDestEnd,
ftEndMarkerMinusOne,
ftSrcEnd,
ftEnd = -1
};
typedef struct {
enum fixtype_t type;
uint16_t addr;
} FixEntry;
#define FLAG_DIRTY (1<<0)
#define FLAG_NOCLI (1<<1)
#define FLAG_MASK (FLAG_DIRTY | FLAG_NOCLI)
#define FLAG_XBASE (1<<2)
typedef struct {
uint16_t addr;
uint8_t *data;
int len;
FixEntry *fix_entry;
int flags;
} FixStruct;
#endif /* DECRUNCHERS_H */
/* eof */

View file

@ -0,0 +1,259 @@
/* autogenerated by make_exe.pl, do not edit */
#include <stdint.h>
#include "decrunchers.h"
#define XOR_MAGIC 0xa7
static uint8_t default_header[] = {
0xb2,0xaf,0xa7,0xa7,0x39,0x95,0x97,0x91,
0x94,0x87,0xf4,0xf2,0xe5,0xf4,0xee,0xfd,
0xe2,0xf5,0x86,0xa7,0x07,0xa7
};
static uint8_t default_tail[] = {
0x05,0xa3,0x87,0xdd,0xa6,0x3e,0x0b,0xa4,
0x3f,0x8e,0xa8,0x77,0xaf,0x3e,0x93,0xa4,
0x3e,0xcf,0xa4,0x57,0xbb,0x0e,0xa7,0x22,
0x5e,0x19,0x0c,0xa4,0x9f,0x8d,0x81,0x5e,
0x6d,0xb7,0x5d,0xde,0x94,0xa4,0x3e,0x93,
0xa4,0x02,0x5e,0xde,0xc0,0xa4,0x3e,0xcf,
0xa4,0x6f,0x67,0xe3,0x77,0x6d,0x07,0xa7,
0xeb,0xaf,0xa6
};
static uint8_t default_decruncher[] = {
0xa7,0xa7,0x27,0x97,0xa7,0xb7,0x87,0xa5,
0xa3,0xa3,0xa3,0x02,0x5a,0x77,0xa5,0x61,
0x59,0x61,0x5a,0x87,0x07,0xa6,0x36,0x5a,
0x87,0x32,0xa6,0x17,0x49,0x6f,0x67,0xb7,
0x57,0xa2,0x87,0x32,0xa6,0x37,0x51,0x19,
0x0c,0xa4,0x87,0xdd,0xa6,0xde,0x94,0xa4,
0x22,0x5e,0x0f,0xee,0x58,0xc2,0x5a,0x22,
0x5a,0x17,0xa5,0x61,0x59,0x67,0xa7,0x57,
0xd6,0x67,0xa3,0x37,0xa5,0x07,0xa4,0x19,
0xa3,0xa6,0x87,0xdd,0xa6,0xde,0xa7,0xa6,
0x0f,0x19,0x1b,0xa4,0x87,0xdd,0xa6,0xde,
0xe3,0xa4,0x0d,0x02,0x5b,0xde,0xdf,0xa4,
0x0f,0x9f,0x2d,0xc2,0x5a,0x22,0x5d,0x3f,
0xc2,0x59,0x22,0x5c,0x03,0x5e,0x57,0xa0,
0x16,0x5d,0x36,0x5a,0x2f,0x77,0x5e,0x16,
0x5d,0x36,0x5a,0x37,0x3c,0x0e,0xa7,0x22,
0x5b,0x47,0xa6,0x37,0xb5,0xa1,0x58,0x77,
0xaf,0xef,0x87,0x07,0xa6,0x8d,0x22,0x58,
0xcf,0x8d,0x81,0x5b,0x6d,0x77,0x49,0xc7,
0xa1,0x58,0x77,0xa1,0x87,0x07,0xa6,0x8d,
0x22,0x58,0xc7,0x0a,0x0b,0xa6,0x77,0xa4,
0x69,0x0a,0xa6,0x69,0x0b,0xa6,0x0a,0xa7,
0xa7,0xc7
};
static FixEntry ft_default_header[] = {
{ftEnd, 0}
};
static FixEntry ft_default_tail[] = {
{ftEnd, 0}
};
static FixEntry ft_default_decruncher[] = {
{ftDecruncher, 0x00fd},
{ftDestEnd, 0x00fd},
{ftBufZp, 0x00ff},
{ftEndMarkerMinusOne, 0x013b},
{ftSrcEnd, 0x01ac},
{ftEnd, 0}
};
static uint8_t nocli_header[] = {
0xb2,0xaf,0xa7,0xa7,0x39,0x95,0x97,0x91,
0x94,0x87,0xf4,0xf2,0xe5,0xf4,0xee,0xfd,
0xe2,0xf5,0x86,0xa7,0x07,0xa7
};
static uint8_t nocli_tail[] = {
0x05,0xa3,0x87,0xdd,0xa6,0x3e,0x0b,0xa4,
0x3f,0x8e,0xa8,0x77,0xaf,0x3e,0x93,0xa4,
0x3e,0xcf,0xa4,0x57,0xbb,0x0e,0xa7,0x22,
0x5e,0x19,0x0c,0xa4,0x9f,0x8d,0x81,0x5e,
0x6d,0xb7,0x5d,0xde,0x94,0xa4,0x3e,0x93,
0xa4,0x02,0x5e,0xde,0xc0,0xa4,0x3e,0xcf,
0xa4,0x6f,0x67,0xe3,0x77,0x6d,0x07,0xa7,
0xeb,0xaf,0xa6
};
static uint8_t nocli_decruncher[] = {
0xa7,0xa7,0x27,0x97,0xa7,0xb7,0x87,0xa5,
0xa3,0xa3,0xa3,0x02,0x5a,0x77,0xa5,0x61,
0x59,0x61,0x5a,0x87,0x07,0xa6,0x36,0x5a,
0x87,0x32,0xa6,0x17,0x49,0x6f,0x67,0xb7,
0x57,0xa2,0x87,0x32,0xa6,0x37,0x51,0x19,
0x0c,0xa4,0x87,0xdd,0xa6,0xde,0x94,0xa4,
0x22,0x5e,0x0f,0xee,0x58,0xc2,0x5a,0x22,
0x5a,0x17,0xa5,0x61,0x59,0x67,0xa7,0x57,
0xd6,0x67,0xa3,0x37,0xa5,0x07,0xa4,0x19,
0xa3,0xa6,0x87,0xdd,0xa6,0xde,0xa7,0xa6,
0x0f,0x19,0x1b,0xa4,0x87,0xdd,0xa6,0xde,
0xe3,0xa4,0x0d,0x02,0x5b,0xde,0xdf,0xa4,
0x0f,0x9f,0x2d,0xc2,0x5a,0x22,0x5d,0x3f,
0xc2,0x59,0x22,0x5c,0x03,0x5e,0x57,0xa0,
0x16,0x5d,0x36,0x5a,0x2f,0x77,0x5e,0x16,
0x5d,0x36,0x5a,0x37,0x3c,0x0e,0xa7,0x22,
0x5b,0x47,0xa6,0x37,0xb5,0xa1,0x58,0x77,
0xaf,0xef,0x87,0x07,0xa6,0x8d,0x22,0x58,
0xcf,0x8d,0x81,0x5b,0x6d,0x77,0x49,0xc7,
0xa1,0x58,0x77,0xa1,0x87,0x07,0xa6,0x8d,
0x22,0x58,0xc7,0x0a,0x0b,0xa6,0x77,0xa4,
0x69,0x0a,0xa6,0x69,0x0b,0xa6,0x0a,0xa7,
0xa7,0xc7
};
static FixEntry ft_nocli_header[] = {
{ftEnd, 0}
};
static FixEntry ft_nocli_tail[] = {
{ftEnd, 0}
};
static FixEntry ft_nocli_decruncher[] = {
{ftDecruncher, 0x00fd},
{ftDestEnd, 0x00fd},
{ftBufZp, 0x00ff},
{ftEndMarkerMinusOne, 0x013b},
{ftSrcEnd, 0x01ac},
{ftEnd, 0}
};
static uint8_t dirty_default_header[] = {
0xb2,0xaf,0xa7,0xa7,0x39,0x95,0x97,0x91,
0x94,0x87,0xf4,0xf2,0xe5,0xf4,0xee,0xfd,
0xe2,0xf5,0x86,0xa7,0x07,0xa7
};
static uint8_t dirty_default_tail[] = {
0x0e,0x47,0xa1,0x18,0x77,0xaf,0x0f,0x87,
0x63,0xa7,0x8d,0x22,0x18,0x3f,0x8d,0x17,
0x56,0x32,0xdd,0x2d,0x8e,0xa8,0x0f,0x57,
0xb3,0x0e,0xa7,0x22,0x19,0x13,0xde,0x9f,
0x8d,0x81,0x19,0x2f,0xb7,0x5d,0xd2,0xa6,
0x0f,0x02,0x19,0xd2,0x92,0x32,0x91,0x33,
0xa5,0x4f,0x47,0xe3,0x77,0x6d,0x07,0xa7,
0xeb,0x46,0xa7
};
static uint8_t dirty_default_decruncher[] = {
0x27,0x2b,0x47,0x46,0x45,0x02,0x6a,0x77,
0xa5,0x61,0x69,0x61,0x6a,0x0a,0xa7,0xa7,
0xc7,0x07,0xa7,0x57,0xae,0x1e,0xa7,0xa7,
0x3e,0xa7,0xa7,0x2f,0x77,0x50,0x16,0x72,
0x37,0xac,0x02,0x7f,0x77,0xa5,0x61,0x7e,
0x61,0x7f,0x87,0x63,0xa7,0x36,0x7f,0xa1,
0x18,0x77,0xa1,0x87,0x63,0xa7,0x8d,0x22,
0x18,0x17,0x40,0x05,0x57,0x4f,0x57,0xaa,
0xa1,0x18,0x77,0xa1,0x87,0x63,0xa7,0x8d,
0x22,0x18,0x37,0x56,0xbf,0x3f,0x13,0x2e,
0x57,0xb7,0xa1,0x18,0x77,0xaf,0xef,0x87,
0x63,0xa7,0x8d,0x22,0x18,0xcf,0x8d,0x2f,
0x77,0x57,0xd2,0xb6,0x22,0x76,0x0d,0xee,
0x58,0xc2,0x7f,0x22,0x7f,0x17,0xa5,0x61,
0x7e,0x47,0xa7,0x57,0xef,0x47,0xa3,0x37,
0xa5,0x05,0xa4,0x12,0x67,0xa1,0x18,0x77,
0xaf,0x0f,0x87,0x63,0xa7,0x8d,0x22,0x18,
0x3f,0x8d,0x17,0x56,0x0d,0x0e,0xa7,0x22,
0x19,0x13,0x2d,0x57,0xb5,0xa1,0x18,0x77,
0xaf,0xef,0x87,0x63,0xa7,0x8d,0x22,0x18,
0xcf,0x8d,0x81,0x19,0x2f,0x77,0x49,0xd2,
0xb5,0x37,0xa5,0x41,0x19,0x9f,0xc2,0x7f,
0x22,0x72,0x02,0x19,0xd2,0xe1,0xc2,0x7e,
0x22,0x71,0xeb,0x77,0xa7
};
static FixEntry ft_dirty_default_header[] = {
{ftEnd, 0}
};
static FixEntry ft_dirty_default_tail[] = {
{ftEnd, 0}
};
static FixEntry ft_dirty_default_decruncher[] = {
{ftDecruncher, 0x00bf},
{ftBufZp, 0x00bf},
{ftSrcEnd, 0x00cd},
{ftDestEnd, 0x00d8},
{ftEndMarkerMinusOne, 0x0131},
{ftEnd, 0}
};
static uint8_t dirty_nocli_header[] = {
0xb2,0xaf,0xa7,0xa7,0x39,0x95,0x97,0x91,
0x94,0x87,0xf4,0xf2,0xe5,0xf4,0xee,0xfd,
0xe2,0xf5,0x86,0xa7,0x07,0xa7
};
static uint8_t dirty_nocli_tail[] = {
0x0e,0x47,0xa1,0x18,0x77,0xaf,0x0f,0x87,
0x63,0xa7,0x8d,0x22,0x18,0x3f,0x8d,0x17,
0x56,0x32,0xdd,0x2d,0x8e,0xa8,0x0f,0x57,
0xb3,0x0e,0xa7,0x22,0x19,0x13,0xde,0x9f,
0x8d,0x81,0x19,0x2f,0xb7,0x5d,0xd2,0xa6,
0x0f,0x02,0x19,0xd2,0x92,0x32,0x91,0x33,
0xa5,0x4f,0x47,0xe3,0x77,0x6d,0x07,0xa7,
0xeb,0x46,0xa7
};
static uint8_t dirty_nocli_decruncher[] = {
0x27,0x2b,0x47,0x46,0x45,0x02,0x6a,0x77,
0xa5,0x61,0x69,0x61,0x6a,0x0a,0xa7,0xa7,
0xc7,0x07,0xa7,0x57,0xae,0x1e,0xa7,0xa7,
0x3e,0xa7,0xa7,0x2f,0x77,0x50,0x16,0x72,
0x37,0xac,0x02,0x7f,0x77,0xa5,0x61,0x7e,
0x61,0x7f,0x87,0x63,0xa7,0x36,0x7f,0xa1,
0x18,0x77,0xa1,0x87,0x63,0xa7,0x8d,0x22,
0x18,0x17,0x40,0x05,0x57,0x4f,0x57,0xaa,
0xa1,0x18,0x77,0xa1,0x87,0x63,0xa7,0x8d,
0x22,0x18,0x37,0x56,0xbf,0x3f,0x13,0x2e,
0x57,0xb7,0xa1,0x18,0x77,0xaf,0xef,0x87,
0x63,0xa7,0x8d,0x22,0x18,0xcf,0x8d,0x2f,
0x77,0x57,0xd2,0xb6,0x22,0x76,0x0d,0xee,
0x58,0xc2,0x7f,0x22,0x7f,0x17,0xa5,0x61,
0x7e,0x47,0xa7,0x57,0xef,0x47,0xa3,0x37,
0xa5,0x05,0xa4,0x12,0x67,0xa1,0x18,0x77,
0xaf,0x0f,0x87,0x63,0xa7,0x8d,0x22,0x18,
0x3f,0x8d,0x17,0x56,0x0d,0x0e,0xa7,0x22,
0x19,0x13,0x2d,0x57,0xb5,0xa1,0x18,0x77,
0xaf,0xef,0x87,0x63,0xa7,0x8d,0x22,0x18,
0xcf,0x8d,0x81,0x19,0x2f,0x77,0x49,0xd2,
0xb5,0x37,0xa5,0x41,0x19,0x9f,0xc2,0x7f,
0x22,0x72,0x02,0x19,0xd2,0xe1,0xc2,0x7e,
0x22,0x71,0xeb,0x77,0xa7
};
static FixEntry ft_dirty_nocli_header[] = {
{ftEnd, 0}
};
static FixEntry ft_dirty_nocli_tail[] = {
{ftEnd, 0}
};
static FixEntry ft_dirty_nocli_decruncher[] = {
{ftDecruncher, 0x00bf},
{ftBufZp, 0x00bf},
{ftSrcEnd, 0x00cd},
{ftDestEnd, 0x00d8},
{ftEndMarkerMinusOne, 0x0131},
{ftEnd, 0}
};
static FixStruct fs_list_decruncher[] = {
{0x00fd, default_decruncher, sizeof(default_decruncher), ft_default_decruncher, },
{0x00fd, nocli_decruncher, sizeof(nocli_decruncher), ft_nocli_decruncher, FLAG_NOCLI},
{0x00bf, dirty_default_decruncher, sizeof(dirty_default_decruncher), ft_dirty_default_decruncher, FLAG_DIRTY},
{0x00bf, dirty_nocli_decruncher, sizeof(dirty_nocli_decruncher), ft_dirty_nocli_decruncher, FLAG_NOCLI|FLAG_DIRTY},
{0, NULL, 0, NULL, 0}
};
static FixStruct fs_list_header[] = {
{0x0801, default_header, sizeof(default_header), ft_default_header, },
{0x0801, nocli_header, sizeof(nocli_header), ft_nocli_header, FLAG_NOCLI},
{0x0801, dirty_default_header, sizeof(dirty_default_header), ft_dirty_default_header, FLAG_DIRTY},
{0x0801, dirty_nocli_header, sizeof(dirty_nocli_header), ft_dirty_nocli_header, FLAG_NOCLI|FLAG_DIRTY},
{0, NULL, 0, NULL, 0}
};
static FixStruct fs_list_tail[] = {
{0x1000, default_tail, sizeof(default_tail), ft_default_tail, },
{0x1000, nocli_tail, sizeof(nocli_tail), ft_nocli_tail, FLAG_NOCLI},
{0x1000, dirty_default_tail, sizeof(dirty_default_tail), ft_dirty_default_tail, FLAG_DIRTY},
{0x1000, dirty_nocli_tail, sizeof(dirty_nocli_tail), ft_dirty_nocli_tail, FLAG_NOCLI|FLAG_DIRTY},
{0, NULL, 0, NULL, 0}
};
/* eof */

View file

@ -0,0 +1,44 @@
/**************************************************************************
*
* FILE detect_start.c
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* Try to detect start address for a program file.
*
******/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include "../global.h"
#include "../memory.h"
#include "detect_start.h"
/**************************************************************************
*
* SECTION memory functions
*
******/
int detect_start(Memory *mem)
{
int jmp = -1;
if (mem->low == 0x0801) {
/* try to find jmp by looking at the sys */
if (get_byte(mem, 0x0805) == 0x9e) {
int ad;
for (ad = 0x0806; ad < 0x900; ad++) {
if (isdigit(get_byte(mem, ad))) {
break;
}
}
jmp = atoi((char *)&mem->buf[ad]);
}
}
return jmp;
}
/* eof */

View file

@ -0,0 +1,19 @@
/**************************************************************************
*
* FILE detect_start.h
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* Try to detect start address for a program file.
*
******/
#ifndef DETECT_START_H
#define DETECT_START_H
#include "../memory.h"
int detect_start(Memory *mem);
#endif /* DETECT_START_H */
/* eof */

View file

@ -0,0 +1,362 @@
/**************************************************************************
*
* FILE fold.c
* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
*
******/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "decrunchers.h"
#include "fold.h"
#include "generate_sfx.h"
#include "ops6502.h"
#include "../global.h"
#include "../memory.h"
/**************************************************************************
*
* NAME is_basic(), is_io(), is_kernal()
*
* DESCRIPTION
* Identify if address is within special section.
*
******/
static int is_basic(mem_ptr_t ad)
{
if (ad >= 0xa000 && ad <= 0xbfff) {
return 1;
}
return 0;
}
static int is_io(mem_ptr_t ad)
{
if (ad >= 0xd000 && ad <= 0xdfff) {
return 1;
}
return 0;
}
static int is_kernal(mem_ptr_t ad)
{
if (ad >= 0xe000 && ad <= 0xffff) {
return 1;
}
return 0;
}
/**************************************************************************
*
* SECTION rle parser
*
******/
typedef struct {
mem_ptr_t sa;
size_t n;
uint8_t val;
} RleEntry;
static int cmp_rle(const void *a, const void *b)
{
const RleEntry *ar = a;
const RleEntry *br = b;
/* sort on I/O area first */
if (is_io(ar->sa) && !is_io(br->sa)) {
return 1;
}
if (!is_io(ar->sa) && is_io(br->sa)) {
return -1;
}
/* sort on kernal area second */
if (is_kernal(ar->sa) && !is_kernal(br->sa)) {
return 1;
}
if (!is_kernal(ar->sa) && is_kernal(br->sa)) {
return -1;
}
/* sort on basic area third */
if (is_basic(ar->sa) && !is_basic(br->sa)) {
return 1;
}
if (!is_basic(ar->sa) && is_basic(br->sa)) {
return -1;
}
/* lastly sort on size */
if (ar->n < br->n) {
return 1;
}
if (ar->n > br->n) {
return -1;
}
return 0;
}
static size_t n_entries;
static RleEntry rle[256];
static void scan_rle(Memory *mem, mem_ptr_t sa, mem_ptr_t ea)
{
mem_ptr_t ad = sa;
int n_rle;
int last_byte;
int min_rle = 50;
n_entries = 0;
n_rle = 0;
last_byte = -1;
while (ad < ea) {
uint8_t c = get_byte(mem, ad);
if (c != last_byte) {
if (n_rle >= min_rle) {
rle[n_entries].sa = ad - n_rle - 1;
rle[n_entries].n = n_rle;
rle[n_entries].val = last_byte;
n_entries++;
}
n_rle = 0;
last_byte = c;
} else {
n_rle++;
}
ad++;
}
/* here we should flush the last entry */
qsort(rle, n_entries, sizeof(RleEntry), cmp_rle);
}
#if 0
static void dump_rle_entries(void)
{
int i;
for (i = 0; i < n_entries; i++) {
size_t n = rle[i].n;
mem_ptr_t sa = rle[i].sa;
mem_ptr_t ea = sa + n;
uint8_t val = rle[i].val;
if (n == 0) {
continue;
}
printf("slot: $%04X-$%04X = $%02X (%zu bytes)\n", sa, ea - 1, val, n);
}
}
#endif
/**************************************************************************
*
* SECTION folding logic
*
******/
static int generate_copy(uint8_t *buf, mem_ptr_t sa, mem_ptr_t da, size_t n)
{
uint8_t *p = buf;
/* generate copy */
MNE_LDXI(p, n);
int lp1 = p - buf;
MNE_LDAX(p, sa - 1);
MNE_STAX(p, da - 1);
MNE_DEX(p);
int br1 = p - buf;
MNE_BNE(p, lp1 - br1);
return p - buf;
}
static int generate_fill(uint8_t *buf, mem_ptr_t ta, size_t n, uint8_t val)
{
uint8_t *p = buf;
MNE_LDXI(p, n);
MNE_LDAI(p, val);
int lp1 = p - buf;
MNE_STAX(p, ta - 1);
MNE_DEX(p);
int br1 = p - buf;
MNE_BNE(p, lp1 - br1);
return p - buf;
}
static int generate_trampoline(uint8_t *buf, mem_ptr_t ca, mem_ptr_t ta, size_t n, uint8_t val, mem_ptr_t sa, SfxConfig *conf)
{
uint8_t *p = buf;
int tl;
mem_ptr_t tra = 0xd000;
/* length of trampoline */
tl = (conf->flags & FLAG_NOCLI) ? 9 : 10;
/* if trampoline fits below the decrunched data, put it there */
if ( (0x0002 + tl) <= sa ) {
tra = sa - tl;
}
/* generate copy */
MNE_LDAI(p, conf->mem);
MNE_STAZ(p, 0x01);
MNE_LDXI(p, tl);
int lp1 = p - buf;
MNE_LDAX(p, ca + 22 - 1);
MNE_STAX(p, tra - 1);
MNE_DEX(p);
int br1 = p - buf;
MNE_BNE(p, lp1 - br1);
MNE_LDXI(p, n);
MNE_LDAI(p, val);
MNE_JMP(p, tra);
int lp2 = p - buf;
MNE_STAX(p, ta - 1);
MNE_DEX(p);
int br2 = p - buf;
MNE_BNE(p, lp2 - br2);
if ( !(conf->flags & FLAG_NOCLI) ) {
MNE_CLI(p);
}
MNE_JMP(p, conf->jump);
return p - buf;
}
static void do_fold_a(Memory *smem, mem_ptr_t low_limit, SfxConfig *conf)
{
mem_ptr_t sa = smem->low;
mem_ptr_t ad = low_limit;
mem_ptr_t last_ca = 0;
int last_val = -1;
int last_n = -1;
uint8_t *last_jmpaddr = 0;
int i;
int count = 0;
int areas = 0;
for (i = 0; i < n_entries; i++) {
mem_ptr_t ca = rle[i].sa;
int n = rle[i].n;
int val = rle[i].val;
uint8_t *buf = smem->buf + ca;
uint8_t *p = buf;
int left = ad - sa;
if (last_jmpaddr) {
OP_ADR(last_jmpaddr, ca);
last_jmpaddr = 0;
}
if (left <= 0) {
p += generate_fill(p, last_ca, last_n, last_val);
generate_trampoline(p, ca+10, ca, n, val, sa, conf);
/* the counters should be possible to unify */
areas++;
count += p - buf;
break;
}
if (last_n < 0) {
int nmax = n - 14;
if ( left < nmax ) {
nmax = left;
n = nmax + 14;
}
ad -= nmax;
p += generate_copy(p, ca+14, ad, nmax);
last_jmpaddr = p + 1;
MNE_JMP(p, 0x0000);
memcpy(p, smem->buf + ad, nmax);
p += nmax;
} else {
int nmax = n - 24;
if ( left < nmax ) {
nmax = left;
n = nmax + 24;
}
ad -= nmax;
p += generate_fill(p, last_ca, last_n, last_val);
p += generate_copy(p, ca+24, ad, nmax);
last_jmpaddr = p + 1;
MNE_JMP(p, 0x0000);
memcpy(p, smem->buf + ad, nmax);
p += nmax;
}
areas++;
count += p - buf;
last_ca = ca;
last_n = n;
last_val = val;
}
conf->mem = 0x34;
conf->flags |= FLAG_NOCLI;
conf->jump = rle[0].sa;
smem->low = low_limit;
printf("folded $%04x-$%04x into %d chunks (%d bytes overhead)\n", sa, low_limit, areas, count - (low_limit-sa));
}
/**************************************************************************
*
* NAME fold()
*
* DESCRIPTION
* Fold low part in rle segments.
*
******/
SfxConfig *fold(Memory *smem, Memory *dmem, mem_ptr_t low_limit, mem_ptr_t high_limit, SfxConfig *conf)
{
mem_ptr_t sa = smem->low;
mem_ptr_t ea = smem->high;
if (sa >= low_limit) {
/* no fold required */
return conf;
}
#if 0
size_t n = low_limit - sa;
if (ea <= high_limit - n) {
/* simple copy possible (should consider the size of the copy routine) */
printf("simple fold possible\n");
return conf;
}
#endif
/* perform full fold */
/* find rle slots above the desired low limit */
scan_rle(smem, low_limit, ea);
do_fold_a(smem, low_limit, conf);
//dump_rle_entries();
return conf;
}
/* eof */

View file

@ -0,0 +1,20 @@
/**************************************************************************
*
* FILE fold.h
* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
*
******/
#ifndef FOLD_H
#define FOLD_H
#include "generate_sfx.h"
#include "../memory.h"
SfxConfig *fold(Memory *smem, Memory *dmem, mem_ptr_t low_limit, mem_ptr_t high_limit, SfxConfig *conf);
#endif /* FOLD_H */
/* eof */

View file

@ -0,0 +1,441 @@
/**************************************************************************
*
* FILE generate_sfx.c
* Copyright (c) 2015, 2016, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
*
******/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "decrunchers_data.h"
#include "generate_sfx.h"
#include "ops6502.h"
#include "../buffer.h"
#include "../global.h"
#include "../memory.h"
static size_t wrap_len;
static mem_ptr_t tail_addr;
static mem_ptr_t src_end_addr;
static mem_ptr_t wrap_src_addr;
static mem_ptr_t decr_store_addr;
static mem_ptr_t decr_addr;
static size_t decr_len;
static uint8_t first_bitbuf;
static int endm_v;
static mem_ptr_t dest_end_addr;
uint8_t header_buf[256];
int header_l;
uint8_t prologue_buf[256];
int prologue_l;
uint8_t epilogue_buf[16];
int epilogue_l;
/**************************************************************************
*
* SECTION common stage processing
*
******/
static SfxConfig config;
#define FLAG_SYSLESS (FLAG_XBASE << 0)
static void get_options(char *opts)
{
char *next;
char *curr;
if (*opts == 0) {
return;
}
curr = opts;
do {
char *val;
next = strchr(curr, ',');
if (next) {
*next++ = 0;
}
val = strchr(curr, '=');
if (val) {
*val++ = 0;
}
if (debug_g) {
printf("flag: %s, val: %s\n", curr, val);
}
if (strcmp("clean", curr) == 0) {
config.flags &= ~FLAG_DIRTY;
config.fold = 0x400;
continue;
}
if (strcmp("dirty", curr) == 0) {
config.flags |= FLAG_DIRTY;
config.fold = 0x200;
continue;
}
if (strcmp("fold", curr) == 0) {
if (val) {
uint16_t v;
v = strtoul(val, 0, 0);
config.fold = v;
} else {
config.fold = 0x200;
}
continue;
}
if (strcmp("nofold", curr) == 0) {
config.fold = 0;
continue;
}
if (strcmp("sei", curr) == 0) {
config.flags |= FLAG_NOCLI;
continue;
}
if (strcmp("mem", curr) == 0) {
uint8_t v;
v = strtoul(val, 0, 0);
config.mem = v;
continue;
}
if (strcmp("jmp", curr) == 0) {
uint16_t v;
v = strtoul(val, 0, 0);
config.jump = v;
continue;
}
if (strcmp("sysless", curr) == 0) {
config.flags |= FLAG_SYSLESS;
continue;
}
if (strcmp("load", curr) == 0) {
uint16_t v;
v = strtoul(val, 0, 0);
config.loadback = v;
config.flags |= FLAG_SYSLESS;
continue;
}
panic("unknown option: %s", curr);
} while ( (curr = next) );
}
SfxConfig *prepare_sfx(int num_opts, char **opts, int jmp)
{
config.loadback = 0x0801;
config.mem = 0x37;
config.mem_during = 0x34;
config.jump = jmp;
config.fold = 0x0400;
config.flags = 0;
while (num_opts) {
get_options(*opts++);
num_opts--;
}
return &config;
}
/**************************************************************************
*
* SECTION variants
*
******/
static FixStruct *find_variant(FixStruct *fs, int flags)
{
while (fs->addr) {
if (fs->flags == (flags & FLAG_MASK)) {
return fs;
}
fs++;
}
panic("couldn't find variant");
return 0;
}
#if 0
/**************************************************************************
*
* SECTION common stage processing
*
******/
static void scan_ft(FixStruct *fs)
{
FixEntry *fe = fs->fix_entry;
for (; fe->type != ftEnd; fe++) {
int offs = fe->addr - fs->addr;
switch (fe->type) {
default:
break;
}
}
}
#endif
static int process_ft(Memory *mem, mem_ptr_t sa, FixStruct *fs)
{
int i;
FixEntry *fe = fs->fix_entry;
int len = fs->len;
for (i = 0; i < len; i++) {
set_byte(mem, sa + i, fs->data[i] ^ XOR_MAGIC);
}
for (; fe->type != ftEnd; fe++) {
mem_ptr_t ad = sa + fe->addr - fs->addr;
switch (fe->type) {
case ftBufZp:
set_byte(mem, ad, first_bitbuf);
break;
case ftDestEnd:
set_word(mem, ad, dest_end_addr);
break;
case ftEndMarkerMinusOne:
set_byte(mem, ad, endm_v - 1);
break;
case ftSrcEnd:
set_word(mem, ad, src_end_addr);
break;
default:
break;
}
}
return len;
}
static int generate_header(uint8_t *buf)
{
uint8_t *p = buf;
/* generate memory setup */
MNE_SEI(p);
MNE_LDAI(p, config.mem_during);
MNE_STAZ(p, 0x01);
MNE_JMP(p, tail_addr);
return p - buf;
}
static int generate_prologue(uint8_t *buf, mem_ptr_t ca, mem_ptr_t la, int xy)
{
uint8_t *p = buf;
#if 0
/* generate memory setup */
MNE_SEI(p);
MNE_LDAI(p, 0x34);
MNE_STAZ(p, 0x01);
#endif
mem_ptr_t src1, dest1;
size_t len1;
mem_ptr_t src2, dest2;
size_t len2;
src1 = decr_store_addr;
dest1 = decr_addr;
len1 = decr_len;
src2 = wrap_src_addr;
dest2 = la;
len2 = wrap_len;
/* generate copy */
MNE_LDXYI(p, len1, xy);
if ( xy & (dest2 <= 0x1f6) ) {
/*
* this is a bit kludgy: sets stack to ~$CA on the dirty decruncher
* if required for safe uncrunch. FIXME: should check if overlap!
*/
MNE_TXS(p);
}
int lp1 = p - buf;
MNE_LDAXY(p, src1 - 1, xy);
MNE_STAXY(p, dest1 - 1, xy);
if (len2 && len1 > len2) {
MNE_CPXYI(p, len2 + 1, xy);
MNE_BCS(p, 8);
MNE_LDAXY(p, src2 - 1, xy);
MNE_STAXY(p, dest2 - 1, xy);
}
MNE_DEXY(p, xy);
int br1 = p - buf;
MNE_BNE(p, lp1 - br1);
if (len2 > len1) {
int offs = (0x100-len2) & 0xff;
int pages = (len2 >> 8) + 1; /* fix me */
MNE_LDXYI(p, pages, !xy);
MNE_LDXYI(p, offs, xy);
int lp2 = p - buf;
MNE_LDAXY(p, src2 - offs, xy);
MNE_STAXY(p, dest2 - offs, xy);
MNE_INXY(p, xy);
int br2 = p - buf;
MNE_BNE(p, lp2 - br2);
MNE_INC(p, ca + lp2 + 2);
MNE_INC(p, ca + lp2 + 5);
MNE_DEXY(p, !xy);
int br3 = p - buf;
MNE_BNE(p, lp2 - br3);
}
return p - buf;
}
static int generate_epilogue(uint8_t *buf)
{
uint8_t *p = buf;
/* generate memory setup */
if ( config.mem != config.mem_during ) {
MNE_LDAI(p, config.mem);
MNE_STAZ(p, 0x01);
}
/* generate CLI */
if ( !(config.flags & FLAG_NOCLI) ) {
MNE_CLI(p);
}
/* generate jump */
MNE_JMP(p, config.jump);
return p - buf;
}
int generate_sfx(Buffer *sbf, Memory *dmem, Memory *smem, int safe, int endm, SfxConfig *conf)
{
/* should actually use conf instead of config directly */
mem_ptr_t dest_end = smem->high;
mem_ptr_t sa = config.loadback;
mem_ptr_t la = smem->low - safe;
mem_ptr_t ca = sa;
mem_ptr_t ea = sa;
int xy = 0;
if (config.flags & FLAG_DIRTY) {
xy = 1;
}
FixStruct *fs_header = find_variant(fs_list_header, config.flags);
FixStruct *fs_tail = find_variant(fs_list_tail, config.flags);
FixStruct *fs_decruncher = find_variant(fs_list_decruncher, config.flags);
endm_v = endm;
dest_end_addr = dest_end;
/* pop first bitbuf from the end of the stream */
first_bitbuf = sbf->buf[sbf->len-1];
sbf->len--;
/******
* generate header (= sys + memory init + jump)
*
*/
mem_ptr_t header_end;
header_l = generate_header(header_buf);
header_end = sa + header_l;
if ( !(config.flags & FLAG_SYSLESS) ) {
header_end += fs_header->len;
}
/*
* determine if the header overlaps the highest possible start
* of the packed data
*/
if (header_end > la) {
/* yes, we need an overlap */
wrap_len = header_end - la;
} else {
/* no, force the start of the packed data to be where it already is */
wrap_len = 0;
la = header_end;
}
src_end_addr = la + sbf->len;
wrap_src_addr = src_end_addr;
tail_addr = src_end_addr + wrap_len;
generate_header(header_buf);
/* emit header */
if ( !(config.flags & FLAG_SYSLESS) ) {
ca += process_ft(dmem, ca, fs_header);
}
ca += insert_mem(dmem, ca, header_buf, header_l);
/* emit packed data (wrap_len may be 0) */
ca += insert_mem(dmem, ca, sbf->buf + wrap_len, sbf->len - wrap_len);
ca += insert_mem(dmem, ca, sbf->buf, wrap_len);
/******
* generate decruncher
*
*/
epilogue_l = generate_epilogue(epilogue_buf);
decr_addr = fs_decruncher->addr;
decr_len = fs_decruncher->len + epilogue_l;
/******
* generate prologue + tail
*
*/
prologue_l = generate_prologue(prologue_buf, ca, la, xy);
decr_store_addr = ca + prologue_l + fs_tail->len;
generate_prologue(prologue_buf, ca, la, xy);
/* emit prologue + tail */
ca += insert_mem(dmem, ca, prologue_buf, prologue_l);
ca += process_ft(dmem, ca, fs_tail);
/* emit decruncher */
ca += process_ft(dmem, ca, fs_decruncher);
ca += insert_mem(dmem, ca, epilogue_buf, epilogue_l);
/* clean up */
ea = ca;
dmem->low = sa;
dmem->high = ea;
return 0;
}
/* eof */

View file

@ -0,0 +1,32 @@
/**************************************************************************
*
* FILE generate_sfx.h
* Copyright (c) 2015, 2016 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
*
******/
#ifndef GENERATE_SFX_H
#define GENERATE_SFX_H
#include <stdint.h>
#include "../buffer.h"
#include "../memory.h"
typedef struct {
mem_ptr_t loadback;
uint8_t mem;
uint8_t mem_during;
mem_ptr_t jump;
mem_ptr_t fold;
int flags;
} SfxConfig;
SfxConfig *prepare_sfx(int num_opts, char **opts, int jmp);
int generate_sfx(Buffer *sbf, Memory *dmem, Memory *smem, int safe, int endm, SfxConfig *conf);
#endif /* GENERATE_SFX_H */
/* eof */

View file

@ -0,0 +1,48 @@
/**************************************************************************
*
* FILE mach_c64.c
* Copyright (c) 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
*
******/
#include "../memory.h"
/**************************************************************************
*
* NAME is_basic(), is_io(), is_kernal()
*
* DESCRIPTION
* Identify if address is within special section.
*
******/
int is_basic(mem_ptr_t ad)
{
if (ad >= 0xa000 && ad <= 0xbfff) {
return 1;
}
return 0;
}
int is_io(mem_ptr_t ad)
{
if (ad >= 0xd000 && ad <= 0xdfff) {
return 1;
}
return 0;
}
int is_kernal(mem_ptr_t ad)
{
if (ad >= 0xe000 && ad <= 0xffff) {
return 1;
}
return 0;
}
/* eof */

View file

@ -0,0 +1,20 @@
/**************************************************************************
*
* FILE mach_c64.h
* Copyright (c) 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
*
******/
#ifndef MACH_C64_H
#define MACH_C64_H
#include "../memory.h"
int is_basic(mem_ptr_t ad);
int is_io(mem_ptr_t ad);
int is_kernal(mem_ptr_t ad);
#endif /* MACH_C64_H */
/* eof */

View file

@ -0,0 +1,63 @@
/**************************************************************************
*
* FILE ops6502.h
* Copyright (c) 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
*
******/
#ifndef OPS6502_H
#define OPS6502_H
#define OP_ADR(p, ad) { *p++ = (ad) & 0xff; *p++ = ((ad) >> 8) & 0xff; }
#define MNE_CLD(p) { *p++ = 0xd8; }
#define MNE_CLI(p) { *p++ = 0x58; }
#define MNE_SED(p) { *p++ = 0xf8; }
#define MNE_SEI(p) { *p++ = 0x78; }
#define MNE_LDAI(p, imm) { *p++ = 0xa9; *p++ = imm; }
#define MNE_STAZ(p, zp) { *p++ = 0x85; *p++ = zp; }
#define MNE_JMP(p, ad) { *p++ = 0x4c; OP_ADR(p, ad); }
#define MNE_TAX(p) { *p++ = 0xaa; }
#define MNE_TSX(p) { *p++ = 0xba; }
#define MNE_TAY(p) { *p++ = 0xa8; }
#define MNE_TXA(p) { *p++ = 0x8a; }
#define MNE_TXS(p) { *p++ = 0x9a; }
#define MNE_TYA(p) { *p++ = 0x98; }
#define MNE_BCS(p, offs) { *p++ = 0xb0; *p++ = offs - 2; }
#define MNE_BNE(p, offs) { *p++ = 0xd0; *p++ = offs - 2; }
#define MNE_LDAI(p, imm) { *p++ = 0xa9; *p++ = imm; }
#define MNE_LDXI(p, imm) { *p++ = 0xa2; *p++ = imm; }
#define MNE_LDYI(p, imm) { *p++ = 0xa0; *p++ = imm; }
#define MNE_LDAX(p, ad) { *p++ = 0xbd; OP_ADR(p, ad); }
#define MNE_LDAY(p, ad) { *p++ = 0xb9; OP_ADR(p, ad); }
#define MNE_STAX(p, ad) { *p++ = 0x9d; OP_ADR(p, ad); }
#define MNE_STAZX(p, zp) { *p++ = 0x95; *p++ = zp; }
#define MNE_STAY(p, ad) { *p++ = 0x99; OP_ADR(p, ad); }
#define MNE_DEX(p) { *p++ = 0xca; }
#define MNE_DEY(p) { *p++ = 0x88; }
#define MNE_INX(p) { *p++ = 0xe8; }
#define MNE_INY(p) { *p++ = 0xc8; }
#define MNE_CPXI(p, imm) { *p++ = 0xe0; *p++ = imm; }
#define MNE_CPYI(p, imm) { *p++ = 0xc0; *p++ = imm; }
#define MNE_INC(p, ad) { *p++ = 0xee; OP_ADR(p, ad); }
#define MNE_DEC(p, ad) { *p++ = 0xce; OP_ADR(p, ad); }
/* special X/Y optional */
#define MNE_LDXYI(p, imm, xy) if (xy) MNE_LDXI(p, imm) else MNE_LDYI(p, imm)
#define MNE_LDAXY(p, ad, xy) if (xy) MNE_LDAX(p, ad) else MNE_LDAY(p, ad)
#define MNE_STAXY(p, ad, xy) if (xy) MNE_STAX(p, ad) else MNE_STAY(p, ad)
#define MNE_DEXY(p, xy) if (xy) MNE_DEX(p) else MNE_DEY(p)
#define MNE_INXY(p, xy) if (xy) MNE_INX(p) else MNE_INY(p)
#define MNE_CPXYI(p, imm, xy) if (xy) MNE_CPXI(p, imm) else MNE_CPYI(p, imm)
#endif /* OPS6502_H */
/* eof */

View file

@ -0,0 +1,460 @@
/**************************************************************************
*
* FILE subsizer.c
* Copyright (c) 2012, 2015, 2016, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* CBM packer/cruncher
*
******/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include "bitfunc.h"
#include "buffer.h"
#include "crunch_normal.h"
#include "pathfinder.h"
#include "global.h"
#include "match.h"
#include "memory.h"
#include "message.h"
#include "params.h"
#include "sfx/detect_start.h"
#include "utils.h"
#define MAX_DECRUNCH_SIZE 0x100000
#define HAVE_CRUNCH_LEVEL 0
#define PROGRAM "subsizer"
const char program_g[] = PROGRAM;
typedef struct {
char *name;
int (*pack_func)(Buffer *, Buffer *);
int (*depack_func)(Buffer *, Buffer *);
int (*mem_func)(Memory *, Memory *, int num_opts, char **opts);
int (*sfx_func)(Memory *, Memory *, int num_opts, char **opts, int jmp);
} Type;
Type types[] = {
{ /* crunch_normal */
"normal",
crunch_normal, decrunch_normal,
crunch_normal_mem,
crunch_normal_sfx
},
{ 0, 0, 0, 0, 0 }
};
static int crunch_raw(Type *p, int num_files, char **srcnames, char *destname, int backwards_mode);
static int decrunch_raw(Type *p, int num_files, char **srcnames, char *destname, int backwards_mode);
static int crunch_mem(Type *p, int num_files, char **srcnames, char *destname, int forwards_mode);
#if HAVE_CRUNCH_LEVEL
static int crunch_level(Type *p, int num_files, char **srcnames, char *destname);
#endif
static int crunch_sfx(Type *p, int num_files, char **srcnames, char *destname, int num_sfx_opts, char **sfx_opts);
int main(int argc, char *argv[])
{
int c;
char **srcnames = 0;
char *destname = "a.out";
enum { MODE_RAW, MODE_SFX, MODE_MEM, MODE_LEVEL } mode;
int num_sfx_opts = 0;
char *sfx_opts[256];
int backwards_mode;
int forwards_mode;
int depack_mode;
char *pack_type;
/* defaults */
verbose_g = 1;
debug_g = 0;
mode = MODE_RAW; /* always raw mode for now */
backwards_mode = 0;
forwards_mode = 0;
depack_mode = 0;
pack_type = "normal";
/*
* scan for valid options
*/
while (EOF != (c = getopt(argc, argv, "o:t:rmlxX:bfdqvDVh"))) {
switch (c) {
/* a missing parameter */
case ':':
/* an illegal option */
case '?':
exit(1);
/* set quiet mode */
case 'q':
verbose_g = 0;
break;
/* set verbose mode */
case 'v':
verbose_g++;
break;
/* set debug mode */
case 'D':
debug_g = 1;
break;
/* print version */
case 'V':
fprintf (stdout, PROGRAM " " VERSION "\n");
exit(0);
/* print help */
case 'h':
fprintf(stderr,
PROGRAM " " VERSION ": CBM packer/cruncher\n"
"Copyright (c) 2012, 2015, 2016, 2017 Daniel Kahlin <daniel@kahlin.net>\n"
"Written by Daniel Kahlin <daniel@kahlin.net>\n"
"\n"
"usage: " PROGRAM " [OPTION] FILE...\n"
"\n"
"Valid options:\n"
" -t<type> select algorithm type (default: normal)\n"
" -r raw file\n"
" -m memory file\n"
#if HAVE_CRUNCH_LEVEL
" -l level file\n"
#endif
" -x executable file\n"
" -X<opts> executable options (implies '-x')\n"
" -b backwards mode\n"
" -f forwards mode\n"
" -d decompress\n"
" -o<name> output file\n"
" -q be quiet\n"
" -v be verbose (can be multiple)\n"
" -D display debug information\n"
" -h displays this help text\n"
" -V output program version\n"
);
exit(0);
/* set destination name */
case 'o':
destname = optarg;
break;
/* set algorithm type */
case 't':
pack_type = optarg;
break;
/* run in raw mode */
case 'r':
mode = MODE_RAW;
break;
/* run in memory mode */
case 'm':
mode = MODE_MEM;
break;
/* run in level mode */
case 'l':
mode = MODE_LEVEL;
break;
/* set executable mode */
case 'x':
mode = MODE_SFX;
break;
/* set executable options (implying -x) */
case 'X':
mode = MODE_SFX;
sfx_opts[num_sfx_opts] = optarg;
num_sfx_opts++;
break;
/* run in backwards mode */
case 'b':
backwards_mode = 1;
break;
/* run in forwards mode */
case 'f':
forwards_mode = 1;
break;
/* run in depack mode */
case 'd':
depack_mode = 1;
break;
/* default behavior */
default:
break;
}
}
/*
* optind now points at the first non option argument
* expect one or more arguments
*/
int num_files = argc - optind;
if (num_files < 1) {
panic("too few arguments");
}
srcnames = &argv[optind];
Type *p = types;
while (p->name) {
if (strcmp(p->name, pack_type) == 0) {
break;
}
p++;
}
if (!p->name) {
panic("unknown type '%s'", pack_type);
}
if (!depack_mode) {
/* handle crunching */
switch (mode) {
case MODE_RAW:
crunch_raw(p, num_files, srcnames, destname, backwards_mode);
break;
case MODE_MEM:
crunch_mem(p, num_files, srcnames, destname, forwards_mode);
break;
#if HAVE_CRUNCH_LEVEL
case MODE_LEVEL:
crunch_level(p, num_files, srcnames, destname);
break;
#endif
case MODE_SFX:
if (!p->sfx_func) {
panic("sfx not available for '%s'", p->name);
}
crunch_sfx(p, num_files, srcnames, destname, num_sfx_opts, sfx_opts);
break;
default:
panic("internal: invalid mode (%d)", mode);
break;
}
} else {
/* handle decrunching */
switch (mode) {
case MODE_RAW:
decrunch_raw(p, num_files, srcnames, destname, backwards_mode);
break;
case MODE_SFX:
panic("cannot depack sfx");
break;
default:
panic("internal: invalid mode (%d)", mode);
break;
}
}
exit(0);
}
static int crunch_raw(Type *p, int num_files, char **srcnames, char *destname, int backwards_mode)
{
int ret;
Buffer *sbf;
Buffer *dbf;
/* this should handle more than one file */
sbf = create_buffer_from_file(srcnames[0]);
dbf = create_buffer(MAX_DECRUNCH_SIZE);
if (backwards_mode) {
reverse_buffer(sbf);
}
ret = p->pack_func(sbf, dbf);
if (ret) {
printf("warning: packing failed. packer returned non-zero!\n");
}
msg(MSG_VERBOSE, "packed %zu bytes into %zu bytes\n", sbf->len, dbf->len);
#if 1
/* verify */
Buffer *vbf;
vbf = create_buffer(MAX_DECRUNCH_SIZE);
ret = p->depack_func(dbf, vbf);
if (ret) {
printf("warning: verify failed. depacker returned non-zero!\n");
} else if (compare_buffer(sbf, vbf) != 0) {
printf("warning: verify failed. depacked data does not match source data!\n");
} else {
msg(MSG_VERBOSE, "verifed %zu bytes...ok\n", sbf->len);
}
destroy_buffer(vbf);
#endif
if (backwards_mode) {
reverse_buffer(dbf);
}
write_buffer_to_file(dbf, destname);
destroy_buffer(dbf);
destroy_buffer(sbf);
return 0;
}
static int decrunch_raw(Type *p, int num_files, char **srcnames, char *destname, int backwards_mode)
{
int ret;
Buffer *sbf;
Buffer *dbf;
/* this should handle more than one file */
sbf = create_buffer_from_file(srcnames[0]);
dbf = create_buffer(MAX_DECRUNCH_SIZE);
if (backwards_mode) {
reverse_buffer(sbf);
}
ret = p->depack_func(sbf, dbf);
if (ret) {
printf("warning: depacking failed. depacker returned non-zero!\n");
}
msg(MSG_VERBOSE, "depacked %zu bytes into %zu bytes\n", sbf->len, dbf->len);
if (backwards_mode) {
reverse_buffer(dbf);
}
write_buffer_to_file(dbf, destname);
destroy_buffer(dbf);
destroy_buffer(sbf);
return 0;
}
static int crunch_mem(Type *p, int num_files, char **srcnames, char *destname, int forwards_mode)
{
int i;
int ret;
Memory *smem;
Memory *dmem;
int srclen, destlen;
smem = create_memory(0x10000);
dmem = create_memory(0x10000);
for (i = 0; i < num_files; i++) {
char *name = srcnames[i];
file_t f;
f = parse_filename(name);
load_mem(smem, &f, 0, 0);
}
srclen = smem->high - smem->low;
// ret = p->mem_func(smem, dmem, 0, 0);
ret = p->mem_func(smem, dmem, forwards_mode, 0); // FIXME: kludge!!!!
if (ret) {
printf("warning: packing failed. packer returned non-zero!\n");
}
destlen = dmem->high - dmem->low;
msg(MSG_VERBOSE, "packed %d bytes (%d blocks) into %d bytes (%d blocks)\n", srclen, (srclen+2+253)/254, destlen, (destlen+2+253)/254 );
save_file_from_memory(dmem, destname);
destroy_memory(dmem);
destroy_memory(smem);
return 0;
}
static int crunch_sfx(Type *p, int num_files, char **srcnames, char *destname, int num_sfx_opts, char **sfx_opts)
{
int i;
int ret;
Memory *smem;
Memory *dmem;
int srclen, destlen;
int jmp = -1;
smem = create_memory(0x10000);
dmem = create_memory(0x10000);
for (i = 0; i < num_files; i++) {
char *name = srcnames[i];
file_t f;
f = parse_filename(name);
load_mem(smem, &f, 0, 0);
}
srclen = smem->high - smem->low;
if (jmp < 0) {
int r;
r = detect_start(smem);
#if 0
if (r < 0) {
panic("couldn't detect start address!");
}
#endif
jmp = r;
msg(MSG_VERBOSE, "detected sys: $%04X\n", jmp);
}
/* if presumable basic start, make sure $0800 ends up $00 */
if (smem->low == 0x0801) {
set_byte(smem, 0x0800, 0x00);
smem->low = 0x0800;
}
ret = p->sfx_func(smem, dmem, num_sfx_opts, sfx_opts, jmp);
if (ret) {
printf("warning: packing failed. packer returned non-zero!\n");
}
destlen = dmem->high - dmem->low;
msg(MSG_VERBOSE, "packed %d bytes (%d blocks) into %d bytes (%d blocks)\n", srclen, (srclen+2+253)/254, destlen, (destlen+2+253)/254 );
save_file_from_memory(dmem, destname);
destroy_memory(dmem);
destroy_memory(smem);
return 0;
}
/* eof */

View file

@ -0,0 +1,108 @@
/**************************************************************************
*
* FILE universal.c
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* universal encodings
*
******/
#include <stdio.h>
#include "bitfunc.h"
#include "universal.h"
#include "global.h"
/**************************************************************************
*
* NAME cost_unary, write_unary, read_unary
*
******/
int cost_unary(int v, int lim)
{
if (v == lim-1) {
return v;
}
return v+1;
}
void write_unary(BitWriteState *bws, int v, int lim, int pol)
{
uint32_t mask = pol ? 0xfffffff : 0;
int endbit = pol ? 0 : 1;
bitwr_write(bws, mask, v);
if (v < lim-1) {
bitwr_write(bws, endbit, 1);
}
}
int read_unary(BitReadState *brs, int lim, int pol)
{
int n = 0;
while (bitrd_read(brs, 1) == pol) {
n++;
if (n == lim-1) {
break;
}
}
return n;
}
/**************************************************************************
*
* NAME cost_eliasgamma, write_eliasgamma, read_eliasgamma
*
******/
int cost_eliasgamma(int v)
{
int n;
/* find first 1 */
for (n = 31; n >= 0; n--) {
if ( (v>>n) & 0x01)
break;
}
return n + n+1;
}
void write_eliasgamma(BitWriteState *bws, int v)
{
int n;
/* find first 1 */
for (n = 31; n >= 0; n--) {
if ( (v>>n) & 0x01)
break;
}
bitwr_write(bws, 0, n);
bitwr_write(bws, v, n+1);
}
int read_eliasgamma(BitReadState *brs)
{
int v;
int n = 0;
while (bitrd_read(brs, 1) == 0) {
n++;
}
if (n == 16) {
/* short cut to end marker */
return 0x10000;
}
v = bitrd_read(brs, n);
v |= 1<<n;
return v;
}
/* eof */

View file

@ -0,0 +1,25 @@
/**************************************************************************
*
* FILE universal.h
* Copyright (c) 2015 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* universal encodings
*
******/
#ifndef UNIVERSAL_H
#define UNIVERSAL_H
#include "bitfunc.h"
int cost_unary(int v, int lim);
void write_unary(BitWriteState *bws, int v, int lim, int pol);
int read_unary(BitReadState *brs, int lim, int pol);
int cost_eliasgamma(int v);
void write_eliasgamma(BitWriteState *bws, int v);
int read_eliasgamma(BitReadState *brs);
#endif /* UNIVERSAL_H */
/* eof */

View file

@ -0,0 +1,93 @@
/**************************************************************************
*
* FILE utils.c
* Copyright (c) 2012, 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* utility functions.
*
******/
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "global.h"
#include "utils.h"
/**************************************************************************
*
* SECTION memory functions
*
******/
void *safe_malloc(size_t size, const char *msg)
{
void *ptr = malloc(size);
if (!ptr) {
panic("couldn't malloc: %s", msg);
}
return ptr;
}
void *safe_calloc(size_t nmemb, size_t size, const char *msg)
{
void *ptr = calloc(nmemb, size);
if (!ptr) {
panic("couldn't calloc: %s", msg);
}
return ptr;
}
void *safe_realloc(void *ptr, size_t size, const char *msg)
{
ptr = realloc(ptr, size);
if (!ptr) {
panic("couldn't realloc: %s", msg);
}
return ptr;
}
char *safe_strdup(const char *str, const char *msg)
{
char *dup = strdup(str);
if (!dup) {
panic("couldn't strdup: %s", msg);
}
return dup;
}
#if 0
/* apparently not present in mingw32 */
char *safe_strndup(const char *str, int n, const char *msg)
{
char *dup = strndup(str, n);
if (!dup) {
panic("couldn't strndup: %s", msg);
}
return dup;
}
#endif
/**************************************************************************
*
* SECTION time functions
*
******/
double get_time(void)
{
struct timeval tv;
gettimeofday(&tv, 0);
return tv.tv_sec + 0.000001 * tv.tv_usec;
}
/* eof */

View file

@ -0,0 +1,31 @@
/**************************************************************************
*
* FILE utils.h
* Copyright (c) 2012, 2015, 2017 Daniel Kahlin <daniel@kahlin.net>
* Written by Daniel Kahlin <daniel@kahlin.net>
*
* DESCRIPTION
* utility functions.
*
******/
#ifndef UTILS_H
#define UTILS_H
#include <stdlib.h>
/* memory functions */
void *safe_malloc(size_t size, const char *msg);
void *safe_calloc(size_t nmemb, size_t size, const char *msg);
void *safe_realloc(void *ptr, size_t size, const char *msg);
char *safe_strdup(const char *str, const char *msg);
#if 0
/* apparently not present in mingw32 */
char *safe_strndup(const char *str, int n, const char *msg);
#endif
/* time functions */
double get_time(void);
#endif /* UTILS_H */
/* eof */