aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.txt58
-rw-r--r--comptitle.s.in1
-rw-r--r--taipan.c50
-rw-r--r--titlecompression.txt21
4 files changed, 120 insertions, 10 deletions
diff --git a/README.txt b/README.txt
index 9c8f1f7..bf03aab 100644
--- a/README.txt
+++ b/README.txt
@@ -215,6 +215,64 @@ I've made a few changes to the UI, compared to the Apple version:
Other things that need doing to the code:
+- Decide what to do about integer overflow. Possibilities:
+
+ - Use a "bigint" library (e.g. 64-bit ints). Would still be
+ possible to overflow, but it would take a really determined
+ player. Disadvantage: slow. Maxes out at:
+ 40-bit: 1,099,511,627,776 (1 trillion)
+ 48-bit: 281,474,976,710,656 (281 trillion)
+ 64-bit: 1.84467440737096e+19 (beaucoup!)
+
+ - Use the ROM floating point routines. Nobody's likely to ever
+ overflow them. But, would have to write wrapper code to call
+ them from C and convert longs to floats and back. And it'll
+ be slower than bigints even. Maxes out at 1.0e+97
+
+ - Use packed BCD (base 100) like the FP ROM, but as an integer (no
+ magnitude). Maxes out at:
+ 6 bytes: 1,000,000,000,000 (1 trillion)
+ 7 bytes: 100,000,000,000,000 (100 trillion)
+ 8 bytes: 10,000,000,000,000,000 (10 quadrillion)
+ Advantage: as above, but more so: "1.2 million" gets even easier
+ to calculate. Math would be faster than FP, slower than 64-bit ints,
+ but conversion to/from longs would be slower.
+
+ - Use a hybrid data format: one long for the bottom 5 decimal digits,
+ range 0 to 99,9999 (value % 100000) and the other for the high part
+ (value / 100000). Advantage: the game prints strings like "1.2
+ million", this would be faster/easier than a regular bigint.
+ Maxes out at 429,496,729,600,000 (429 trillion).
+
+ - Leave it as-is. Obviously the easiest option, but not very satisfying.
+ Maxes out at a measly 4.2 billion.
+
+ Whatever format we pick, we'll run into limitations. The "Cash" area
+ of the display is only so wide, we can only fill it with so many
+ characters. At some point, we need artificial limits:
+
+ - If your debt maxes out: "Taipan, you have been assassinated!" and
+ the game is over.
+
+ - If the bank maxes out, stop calculating interest. On deposit,
+ "Taipan, the bank's coffers are full!"
+
+ - If cash maxes out, forcibly retire the player. "Taipan, you are now
+ so rich that you own the entire continent of Asia!" or maybe "Taipan,
+ your ship has sunk under the weight of your massive fortune!"
+
+ Tricky part about these limits is checking for overflow without
+ actually overflowing. E.g. instead of "cash += amount" we have to
+ write "if(cash + amount < cash) overflow(); else cash += amount".
+ Also, backing out of the call tree will be a PITA. longjmp() anyone?
+ cc65's implementation seems to work OK. If I use ROM floats, I
+ won't worry about this at all.
+
+ For display, "million" can become "billion" if needed... then "trillion"
+ but without a space in front ("1.2trillion"). We have enough room for
+ 4 digits there, 9999trillion would be the max. Or, abbreviate "billion"
+ as "bil", allowing 4 more digits. "99999999 bil" would be 99 quadrillion.
+
- Size optimization. Right now, the executable is almost 27K of code. I'd
like it to at least fit on a 16K cartridge. A lot of the C code is
redundant, and some things can be rewritten in asm if need be. I've
diff --git a/comptitle.s.in b/comptitle.s.in
index b8ed688..824cded 100644
--- a/comptitle.s.in
+++ b/comptitle.s.in
@@ -39,6 +39,7 @@ table:
; decompression code starts here
init:
lda #0
+ sta SDMCTL ; turn off the screen. newtitle.s turns it back on.
;;; for benchmarking only, remove!
;sta RTCLOK
diff --git a/taipan.c b/taipan.c
index af755e4..f3f0c87 100644
--- a/taipan.c
+++ b/taipan.c
@@ -18,6 +18,10 @@
damage and capacity numbers directly */
// #define MCHENRY_TEST
+/* define this to start the game in the year 1869, with
+ 1000 capacity, 20 guns, and 1 billion cash and bank. */
+// #define TIMEWARP
+
/**** atari-specific stuff */
/* values returned by cgetc() for backspace & enter keys */
@@ -223,10 +227,10 @@ int hkw_[4],
hold_[4];
int hold = 0,
- capacity = 60,
+ // capacity = 60,
guns = 0,
bp = 0,
- damage = 0,
+ // damage = 0,
month = 1,
year = 1860,
li = 0,
@@ -234,13 +238,15 @@ int hold = 0,
wu_warn = 0,
wu_bailout = 0;
-int newdamage;
+// these need to be longs to avoid int overflow when
+// displaying ship status.
+long damage = 0, capacity = 60, newdamage;
// fancy_numbers() will get replaced sooner or later.
// void cprintfancy(unsigned long ul) {
// }
-/* print an int or long as a string, conio-style */
+/* print an int or long as a string, conio-style */
void cprintulong(unsigned long ul) {
cputs(ultoa(ul, fancy_num, 10));
}
@@ -514,7 +520,7 @@ int sea_battle(int id, int num_ships) {
fight_stats(num_ships, orders);
while(num_ships > 0) {
- status = 100 - ((damage * 100 / capacity));
+ status = 100L - ((damage * 100L / capacity));
if(status <= 0) {
return 4;
}
@@ -932,8 +938,8 @@ int sea_battle(int id, int num_ships) {
// if ((guns > 0) && ((randi()%100 < (((float) damage / capacity) * 100)) ||
// ((((float) damage / capacity) * 100) > 80)))
- if((guns > 0) && ((randi()%100 < ((damage * 100) / capacity)) ||
- (((damage * 100) / capacity)) > 80))
+ if((guns > 0) && ((randi()%100 < ((damage * 100L) / capacity)) ||
+ (((damage * 100L) / capacity)) > 80))
{
i = 1;
guns--;
@@ -966,8 +972,18 @@ int sea_battle(int id, int num_ships) {
damage += newdamage;
if(damage > capacity) damage = capacity; /* just in case */
+ /* the above is still somehow broken. When fighting lots of
+ ships, late in the game, we still get ship status over 100%
+ in the fight screen, and mchenry says we're 4 billion percent
+ damaged (and memory gets all kinds of corrupted after that).
+ I do NOT understand what's going on here. It looks like
+ damage is still somehow going negative, but that shouldn't
+ be possible. */
+ if(damage < 0) damage = capacity; /* band-aid! */
+
#ifdef DAMAGE_TEST
gotoxy(0, 23);
+ clrtoeol();
cprintulong(ed);
cputc(' ');
cprintulong(i);
@@ -1169,12 +1185,26 @@ void cash_or_guns(void)
li = 0;
bp = 10;
} else {
+#ifdef TIMEWARP
+ year = 1869;
+ cash = 1000000000L;
+ bank = 1000000000L;
+ debt = 0;
+ capacity = 1000;
+ hold = 800;
+ guns = 20;
+ li = 1;
+ bp = 7;
+ ed = 9;
+ ec = 90;
+#else
cash = 0;
debt = 0;
hold = 10;
guns = 5;
li = 1;
bp = 7;
+#endif
}
return;
@@ -1194,7 +1224,7 @@ void port_stats(void)
{
static int firmpos = 0;
int i, in_use,
- status = 100 - ((damage * 100) / capacity);
+ status = 100 - ((damage * 100L) / capacity);
/* all the static text that used to be in port_stats() has
been moved to mkportstats.c, which creates a .xex file which
@@ -1381,7 +1411,7 @@ void mchenry(void)
static int percent, time;
static long br, repair_price, amount;
// int percent = ((float) damage / capacity) * 100,
- percent = (damage * 100 / capacity);
+ percent = (damage * 100L / capacity);
time = ((year - 1860) * 12) + month;
/*
@@ -1836,7 +1866,7 @@ void quit(void)
// you have no chance of sinking. If you're 34%-66% damaged, you
// have a 1 in 3 chance. If you're over 66%, you have a 2 in
// 3 chance.
- damagepct = damage * 100 / capacity;
+ damagepct = damage * 100L / capacity;
if(damagepct < 34)
sunk = 0;
else if(damagepct < 67)
diff --git a/titlecompression.txt b/titlecompression.txt
index 5f6bf3f..9c30232 100644
--- a/titlecompression.txt
+++ b/titlecompression.txt
@@ -22,6 +22,27 @@ likely to have large areas of all 0 bytes, but e.g. it can't compress an
ASCII or ATASCII text file at all (because there are pretty much never
any null bytes in a human-readable text file).
+Apologia:
+---------
+
+I'm aware that cc65 has a zlib implementation, but it doesn't quite meet
+the requirements: it's slow, large (linking it adds 781 bytes to the
+executable size), and the best DEFLATE compression I could manage for
+my title screen was 2806 bytes (using 7z). Add to that the 781 bytes of
+zlib code and the other required libs for a C program, and I end up with
+something like a 65% compression ratio (since I'm counting the code size
+as part of the file size), and it takes 2/3 of a second to decompress. So,
+it meets one of my requirements, but not all of them. Which should not be
+taken as a criticism of cc65's zlib implementation: It's amazingly small,
+efficient, and easy to use. It's just that deflate is a general-purpose
+compression algorithm, and I thought I could do a better job with a
+purpose-built one... which I think I've done.
+
+I didn't know exomizer existed, before I started on my compression scheme.
+If I'd known, and if I'd tried it out, I probably would have just used
+it instead. It has a better compression ratio (55% for my title screen,
+including decomp code) and decompresses fast enough.
+
Theory of operation:
--------------------