aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <yalhcru@gmail.com>2021-05-27 17:12:46 -0400
committerB. Watson <yalhcru@gmail.com>2021-05-27 17:12:46 -0400
commitdb18f631485eea0b1e7d6c8cf853ff4a281c2aba (patch)
tree745ce83b3c24eb8253b7c7097d9a36937b627a49
parent5b6f0359338c922f809c5133eb4c7efc61b3f21c (diff)
downloadtaipan-db18f631485eea0b1e7d6c8cf853ff4a281c2aba.tar.gz
cash_dirty, change random seeding, now 9006 bytes
-rw-r--r--rand.s104
-rw-r--r--taipan.c468
-rw-r--r--timed_getch.s5
3 files changed, 355 insertions, 222 deletions
diff --git a/rand.s b/rand.s
index 6efcabd..f1b2c72 100644
--- a/rand.s
+++ b/rand.s
@@ -1,6 +1,5 @@
.export _randl, _rand1to3
- .import _rand
; .export _randb, _randi, _randl
; .export _rand1in5
; .export _randbit
@@ -37,6 +36,109 @@ _rand1to3:
ldx #0 ; now A is 1..3, but we have to force X to 0...
rts
+;;; This rand() function copied from cc65-2.19's libsrc/common/rand.s
+;;; and modified for my nefarious purposes.
+;;; srand() is not present (we don't use it).
+;
+; Random number generator
+;
+; Written and donated by Sidney Cadot - sidney@ch.twi.tudelft.nl
+; 2016-11-07, modified by Brad Smith
+; 2019-10-07, modified by Lewis "LRFLEW" Fox
+;
+; May be distributed with the cc65 runtime using the same license.
+;
+;
+; int rand (void);
+; void srand (unsigned seed);
+;
+; Uses 4-byte state.
+; Multiplier must be 1 (mod 4)
+; Added value must be 1 (mod 2)
+; This guarantees max. period (2**32)
+; The lowest bits have poor entropy and
+; exhibit easily detectable patterns, so
+; only the upper bits 16-22 and 24-31 of the
+; 4-byte state are returned.
+;
+; The best 8 bits, 24-31 are returned in the
+; low byte A to provide the best entropy in the
+; most commonly used part of the return value.
+;
+; Uses the following LCG values for ax + c (mod m)
+; a = $01010101
+; c = $B3B3B3B3
+; m = $100000000 (32-bit truncation)
+;
+; The multiplier was carefully chosen such that it can
+; be computed with 3 adc instructions, and the increment
+; was chosen to have the same value in each byte to allow
+; the addition to be performed in conjunction with the
+; multiplication, adding only 1 additional adc instruction.
+;
+
+ .export _rand, _randseed, _randseedl, _randseedh, _initrand, _addrandbits
+
+.bss
+
+; The seed. Not ANSI C compliant: we default to 0 rather than 1.
+_randseedl:
+_randseed: .res 4
+_randseedh = _randseed+2
+
+.code
+
+_rand: clc
+ lda _randseed+0
+ adc #$B3
+ sta _randseed+0
+ adc _randseed+1
+ sta _randseed+1
+ adc _randseed+2
+ sta _randseed+2
+ and #$7f ; Suppress sign bit (make it positive)
+ tax
+ lda _randseed+2
+ adc _randseed+3
+ sta _randseed+3
+ rts ; return bit (16-22,24-31) in (X,A)
+
+; Initially the seed comes from sequential reads of POKEY's random
+; register. It never returns 0 so we're guaranteed to have a usable
+; seed.
+_initrand:
+ ldx #3
+@l:
+ lda RANDOM
+ sta _randseed,x
+ dex
+ bpl @l
+ rts
+
+; Caller passes us a user keystroke as ATASCII, in A. We take bits 0
+; to 2, wait that many scanlines, get a random number from POKEY, and
+; EOR it into the _randseed byte pointed to by the low 2 bits of the
+; frame counter (RTCLOK+2).
+; Note that agetc() is still calling rand() on odd frames while all
+; this is going on.
+_addrandbits:
+ and #$07
+ tax
+@l1:
+ sta WSYNC
+ dex
+ bpl @l1
+ lda RTCLOK+2
+ and #$03
+ tax
+ lda RANDOM
+@e:
+ eor _randseed,x
+ beq @e ; if the result is 0, undo the eor.
+ sta _randseed,x
+ rts
+
+
;;; rest of file is commented out
; RANDOM is the POKEY LFSR read address. According to the POKEY data
diff --git a/taipan.c b/taipan.c
index 79f97d0..7b0396f 100644
--- a/taipan.c
+++ b/taipan.c
@@ -14,14 +14,14 @@
#include "bignum.h"
#endif
-/* define this to use POKEY's RANDOM register instead of cc65's
- rand(). Disabled for now, maybe forever (pending testing) */
-// #define POKEY_RANDOM
-
/**** These defines should be disabled for normal gameplay.
Don't leave any of them enabled for a release or a
normal test build. */
+/* define this to debug the random number seeding process in
+ init_game() */
+// #define RANDSEED_TEST
+
/* define this for testing sea_battle(). it causes a pirate
attack every time you leave port. */
// #define COMBAT_TEST
@@ -91,21 +91,9 @@ extern void explosion(void);
extern void __fastcall__ cblank(unsigned char count);
extern void __fastcall__ backspace(void);
-/* Atari-specific random number functions from rand.s.
- Non-Atari platforms can probably just:
-#define initrand() _randomize()
-#define randi() ((unsigned int)rand())
-#define randl() (unsigned long)((randi() << 16) | randi())
-*/
-
-#ifdef POKEY_RANDOM
-#define initrand() /* no-op on Atari */
-/* random positive int, 0 to 32767 */
-extern unsigned int __fastcall__ randi(void);
-#else
-#define initrand() _randomize()
+extern void __fastcall__ addrandbits(char);
+extern void __fastcall__ initrand(void);
#define randi() ((unsigned int)rand())
-#endif
extern unsigned char rand1to3(void);
@@ -120,9 +108,9 @@ extern const char *port_stat_screen;
/* boolean, whether or not port_stats() needs to redraw the
static parts of the port stats screen (by copying
port_stat_screen into screen RAM) */
-char port_stat_dirty = 1;
-
-char bank_dirty = 1;
+char port_stat_dirty = 1,
+ bank_dirty = 1,
+ cash_dirty = 1;
/* boolean, turbo fighting mode. cleared on entry to sea_battle(), set
when user enters turbo mode. has no effect outside of sea_battle() so
@@ -335,8 +323,6 @@ char wu_assassin;
// void splash_intro(void);
unsigned long get_num(void);
-void name_firm(void);
-void cash_or_guns(void);
void set_prices(void);
void port_stats(void);
int port_choices(void); /* making this an char actually wastes 1 byte! */
@@ -634,6 +620,23 @@ char get_ship_status(void) {
return 100 - ((damage * 100L) / capacity);
}
+/*
+#ifdef CART_TARGET
+# pragma code-name (push, "HIGHCODE")
+#endif
+*/
+void print_bar_line(void) {
+ cprint_pipe();
+ cspaces(38);
+ cprint_pipe();
+}
+/*
+#ifdef CART_TARGET
+# pragma code-name (pop)
+#endif
+*/
+
+
#ifdef BIGNUM
bignum(big1T) = BIG_1T;
bignum(big1B) = BIG_1B;
@@ -642,7 +645,28 @@ bignum(big1K) = BIG_1K;
bignum(big0) = BIG_0;
#endif
+#ifdef RANDSEED_TEST
+extern char randseed[4]; /* aka unsigned long randseed */
+void debug_randseed(void) {
+ char oldx = PEEK(85); /* COLCRS */
+ char oldy = PEEK(84); /* ROWCRS */
+ char i;
+ gotox0y22();
+ for(i = 0; i < 4; i++) {
+ cprintuchar(randseed[i]);
+ cspace();
+ }
+ clrtoeol();
+ gotoxy(oldx, oldy);
+}
+
+#endif
+
+/* moved cash_or_guns() and name_firm() to inline code here.
+ saves 10 bytes. */
void init_game(void) {
+ unsigned char input, firmlen = 0;
+
#ifdef BIGNUM
big_copy(bank, big0);
#else
@@ -662,8 +686,171 @@ void init_game(void) {
year = 1860;
ec = 20;
- name_firm();
- cash_or_guns();
+/* This used to be a separate name_firm() function.
+ TODO: rewrite in asm, or at least better C */
+ clr_screen();
+
+ /* old version, readable, but compiles to 78 byte more
+ than the new version below.
+ chlinexy(1, 7, 38);
+ chlinexy(1, 16, 38);
+ cvlinexy(0, 8, 8);
+ cvlinexy(39, 8, 8);
+ cputcxy(0, 7, 17); // upper left corner
+ cputcxy(0, 16, 26); // lower left corner
+ cputcxy(39, 7, 5); // upper right corner
+ cputcxy(39, 16, 3); // lower right corner
+ gotoxy(6, 9);
+ cprint_taipan_comma();
+ gotoxy(2, 11);
+ cputs("What will you name your");
+ gotoxy(6, 13);
+ cprint_firm_colon();
+ chlinexy(12, 14, 22);
+ */
+
+ gotoy(7);
+ cputc(17);
+ chline(38);
+ cputc(5);
+ print_bar_line();
+ cprint_pipe();
+ cspaces(4);
+ cprint_taipan_comma();
+ cspaces(26);
+ cprint_pipe();
+ print_bar_line();
+ cprint_pipe();
+ // cputs(" What will you name your");
+ print_msg(M_what_will_you_name_firm);
+ cspaces(14);
+ cprint_pipe();
+ print_bar_line();
+ cprint_pipe();
+ cspaces(4);
+ cprint_firm_colon();
+ cspaces(29);
+ cprint_pipe();
+ cprint_pipe();
+ cspaces(10);
+ chline(MAX_FIRM);
+ cspaces(4);
+ cprint_pipe();
+ print_bar_line();
+ cputc(26);
+ chline(38);
+ cputc(3);
+
+ gotoxy(11, 13);
+
+ initrand();
+
+ #ifdef RANDSEED_TEST
+ debug_randseed();
+ #endif
+
+ while(1) {
+ input = agetc();
+ addrandbits(input);
+ #ifdef RANDSEED_TEST
+ debug_randseed();
+ #endif
+ if(input == ENTER) {
+ if(firmlen)
+ break;
+ else
+ bad_joss_sound();
+ } else if(input == DEL) {
+ gotox(12);
+ cblank(22);
+ firmlen = 0;
+ } else if(input == BKSP) {
+ if(firmlen) {
+ backspace();
+ --firmlen;
+ }
+ } else if(firmlen < MAX_FIRM) {
+ cputc(firm[firmlen++] = input | 0x80);
+ }
+ }
+
+ firm[firmlen] = '\0';
+ firmpos = 12 - firmlen / 2;
+
+ /* end of name_firm() */
+
+ /* formerly a separate cash_or_guns() function. */
+ clr_screen();
+ cprint_Do_you_want();
+ // cputs("to start . . .\n\n");
+ print_msg(M_to_start);
+ // cputs(" 1) With cash (and a debt)\n\n");
+ cputs(" 1");
+ print_msg(M_with_cash);
+ cspaces(16);
+ cputs("-- or --\n\n 2");
+ // cputs(" 2) With five guns and no cash\n");
+ print_msg(M_with_5_guns);
+ cspaces(16);
+ // cputs("(But no debt!)");
+ print_msg(M_but_no_debt);
+ gotoxy(10, 10);
+ // cputc('?');
+ cprint_question_space();
+
+ do {
+ input = agetc();
+ } while ((input != '1') && (input != '2'));
+
+ capacity = 60;
+ damage = 0;
+ if(input == '1') {
+ cash = 400;
+ debt = 5000;
+ hold = 60;
+ guns = 0;
+ li = 0;
+ bp = 10;
+ } else {
+#ifdef TIMEWARP
+ year = 1869;
+ cash = 1000000000L;
+ // cash = 4294000000L;
+ // cash = 3500000L;
+# ifdef BIGNUM
+ big_copy(bank, big1M);
+ big_mul(bank, bank, big1M);
+# else
+ bank = 1000000000L;
+# endif
+ debt = 0;
+ capacity = 1000;
+ hold = 800;
+ guns = 20;
+ li = 1;
+ bp = 7;
+ ed = 9;
+ ec = 90;
+#else /* !TIMEWARP */
+ cash = 0;
+ debt = 0;
+ hold = 10;
+ guns = 5;
+ li = 1;
+ bp = 7;
+#endif /* TIMEWARP */
+#ifdef ALMOST_DEAD
+ damage = capacity - 1;
+#endif
+ }
+ /* end of cash_or_guns() */
+
+ for(input = PEEK(20); input > 0; --input)
+ randi();
+ #ifdef RANDSEED_TEST
+ debug_randseed();
+ #endif
+
set_prices();
}
@@ -841,6 +1028,7 @@ void new_ship(void) {
if(yngetc(0) == 'y') {
cash -= amount;
+ cash_dirty = 1;
hold += 50;
capacity += 50;
damage = 0;
@@ -875,6 +1063,7 @@ void new_gun(void) {
return;
}
cash -= amount;
+ cash_dirty = 1;
hold -= 10;
guns += 1;
}
@@ -1600,80 +1789,11 @@ unsigned long get_num(void) {
return strtonum(num_buf);
}
-/* TODO: rewrite in asm */
/*
#ifdef CART_TARGET
# pragma code-name (push, "HIGHCODE")
#endif
*/
-void cash_or_guns(void) {
- char choice;
-
- clr_screen();
- cprint_Do_you_want();
- // cputs("to start . . .\n\n");
- print_msg(M_to_start);
- // cputs(" 1) With cash (and a debt)\n\n");
- cputs(" 1");
- print_msg(M_with_cash);
- cspaces(16);
- cputs("-- or --\n\n 2");
- // cputs(" 2) With five guns and no cash\n");
- print_msg(M_with_5_guns);
- cspaces(16);
- // cputs("(But no debt!)");
- print_msg(M_but_no_debt);
- gotoxy(10, 10);
- // cputc('?');
- cprint_question_space();
-
- do {
- choice = agetc();
- } while ((choice != '1') && (choice != '2'));
-
- capacity = 60;
- damage = 0;
- if(choice == '1') {
- cash = 400;
- debt = 5000;
- hold = 60;
- guns = 0;
- li = 0;
- bp = 10;
- } else {
-#ifdef TIMEWARP
- year = 1869;
- cash = 1000000000L;
- // cash = 4294000000L;
- // cash = 3500000L;
-# ifdef BIGNUM
- big_copy(bank, big1M);
- big_mul(bank, bank, big1M);
-# else
- bank = 1000000000L;
-# endif
- debt = 0;
- capacity = 1000;
- hold = 800;
- guns = 20;
- li = 1;
- bp = 7;
- ed = 9;
- ec = 90;
-#else /* !TIMEWARP */
- cash = 0;
- debt = 0;
- hold = 10;
- guns = 5;
- li = 1;
- bp = 7;
-#endif /* TIMEWARP */
-#ifdef ALMOST_DEAD
- damage = capacity - 1;
-#endif
- }
-}
-
void set_prices(void) {
unsigned char i;
for(i = 0; i < 4; ++i)
@@ -1699,7 +1819,7 @@ void port_stats(void) {
if(port_stat_dirty) {
/* this stuff takes approx 1 jiffy */
- bank_dirty = 1;
+ bank_dirty = cash_dirty = 1;
/* all the static text that used to be in port_stats() has
been moved to mkportstats.c, which creates a .xex file which
@@ -1722,11 +1842,6 @@ void port_stats(void) {
}
/* dynamic stuff: */
-#ifdef PORT_STAT_TIMER
- startframe = PEEK(20);
- startline = PEEK(54283U);
-#endif
-
/* approx 73 VCOUNTs */
gotoxy(21, 4);
@@ -1777,19 +1892,35 @@ void port_stats(void) {
rvs_off();
clrtoeol();
+#ifdef PORT_STAT_TIMER
+ startframe = PEEK(20);
+ startline = PEEK(54283U);
+#endif
+
+ /* approx 1.5 frames */
gotoxy(28, 9);
cprintfancy_centered(debt);
clrtoeol();
+#ifdef PORT_STAT_TIMER
+ endframe = PEEK(20);
+ endline = PEEK(54283U);
+#endif
+
+ /* approx 1/4 frame */
gotoxy(29, 12);
clrtoeol();
print_status_desc(status);
cputc(':');
cprintuint(status);
- gotoxy(6, 14);
- cprintfancy(cash);
- cblankto(20);
+ if(cash_dirty) {
+ /* approx 1.5 frames */
+ gotoxy(6, 14);
+ cprintfancy(cash);
+ cblankto(20);
+ cash_dirty = 0;
+ }
if(bank_dirty) {
gotoxy(26, 14);
@@ -1803,11 +1934,6 @@ void port_stats(void) {
}
#ifdef PORT_STAT_TIMER
- endframe = PEEK(20);
- endline = PEEK(54283U);
-#endif
-
-#ifdef PORT_STAT_TIMER
gotoxy(0, 15);
cprintuint(startframe);
cprint_pipe();
@@ -1876,6 +2002,7 @@ void mchenry(void) {
}
if(amount <= cash) {
cash -= amount;
+ cash_dirty = 1;
// damage -= (int)((amount / br) + .5);
damage -= (int)(amount / br);
damage = (damage < 0) ? 0 : damage;
@@ -2412,6 +2539,7 @@ void quit(void) {
cprintfancy(booty);
cprint_bang();
cash += booty;
+ cash_dirty = 1;
good_joss_timed_getch();
} else if (result == 3) {
// cputs("We made it!");
@@ -2585,6 +2713,7 @@ void li_yuen_extortion(void) {
if(yngetc(0) == 'y') {
if(amount <= cash) {
cash -= amount;
+ cash_dirty = 1;
li = 1;
} else {
clear_msg_window();
@@ -2603,6 +2732,7 @@ void li_yuen_extortion(void) {
amount -= cash;
debt += amount;
cash = 0;
+ cash_dirty = 1;
li = 1;
cprint_elder_brother_wu();
@@ -2616,6 +2746,7 @@ void li_yuen_extortion(void) {
} else {
clear_msg_window();
cash = 0;
+ cash_dirty = 1;
// cputs("Very well. ");
print_msg(M_very_well);
cprint_elder_brother_wu();
@@ -2726,6 +2857,7 @@ void elder_brother_wu(void) {
final_stats();
} else {
cash += i;
+ cash_dirty = 1;
debt += j;
port_stats();
@@ -2753,6 +2885,7 @@ void elder_brother_wu(void) {
if(wu <= cash) {
if(wu > debt) wu = debt;
cash -= wu;
+ cash_dirty = 1;
debt -= wu;
/*
@@ -2790,6 +2923,7 @@ void elder_brother_wu(void) {
if((wu <= (cash * 2)) && !would_overflow(cash, wu))
{
cash += wu;
+ cash_dirty = 1;
debt += wu;
break;
} else {
@@ -2811,6 +2945,7 @@ void elder_brother_wu(void) {
unsigned char num = rand1to3();
cash = 0;
+ cash_dirty = 1;
port_stats();
compradores_report();
@@ -2961,114 +3096,6 @@ int port_choices(void) {
return choice;
}
-/*
-#ifdef CART_TARGET
-# pragma code-name (push, "HIGHCODE")
-#endif
-*/
-void print_bar_line(void) {
- cprint_pipe();
- cspaces(38);
- cprint_pipe();
-}
-/*
-#ifdef CART_TARGET
-# pragma code-name (pop)
-#endif
-*/
-
-/* TODO: rewrite in asm, or at least better C */
-void name_firm(void) {
- unsigned char input, firmlen = 0;
- unsigned int randseed;
-
- clr_screen();
-
- /* old version, readable, but compiles to 78 byte more
- than the new version below.
- chlinexy(1, 7, 38);
- chlinexy(1, 16, 38);
- cvlinexy(0, 8, 8);
- cvlinexy(39, 8, 8);
- cputcxy(0, 7, 17); // upper left corner
- cputcxy(0, 16, 26); // lower left corner
- cputcxy(39, 7, 5); // upper right corner
- cputcxy(39, 16, 3); // lower right corner
- gotoxy(6, 9);
- cprint_taipan_comma();
- gotoxy(2, 11);
- cputs("What will you name your");
- gotoxy(6, 13);
- cprint_firm_colon();
- chlinexy(12, 14, 22);
- */
-
- gotoy(7);
- cputc(17);
- chline(38);
- cputc(5);
- print_bar_line();
- cprint_pipe();
- cspaces(4);
- cprint_taipan_comma();
- cspaces(26);
- cprint_pipe();
- print_bar_line();
- cprint_pipe();
- // cputs(" What will you name your");
- print_msg(M_what_will_you_name_firm);
- cspaces(14);
- cprint_pipe();
- print_bar_line();
- cprint_pipe();
- cspaces(4);
- cprint_firm_colon();
- cspaces(29);
- cprint_pipe();
- cprint_pipe();
- cspaces(10);
- chline(MAX_FIRM);
- cspaces(4);
- cprint_pipe();
- print_bar_line();
- cputc(26);
- chline(38);
- cputc(3);
-
- gotoxy(11, 13);
-
- while(1) {
- if((input = agetc()) == ENTER) {
- if(firmlen)
- break;
- else
- bad_joss_sound();
- } else if(input == DEL) {
- gotox(12);
- cblank(22);
- firmlen = 0;
- } else if(input == BKSP) {
- if(firmlen) {
- backspace();
- --firmlen;
- }
- } else if(firmlen < MAX_FIRM) {
- cputc(firm[firmlen++] = input | 0x80);
- randseed <<= 1;
- randseed += (input - 32);
- }
- }
-
- firm[firmlen] = '\0';
- firmpos = 12 - firmlen / 2;
-
- randseed ^= (PEEKW(19) + PEEK(53770L));
- srand(randseed);
- for(input = 0; input < (firm[0] >> 2); ++input)
- randi();
-
- return;
-}
/*
#ifdef CART_TARGET
@@ -3147,6 +3174,7 @@ void buy(void) {
}
cash -= (amount * price[choice]);
+ cash_dirty = 1;
hold_[choice] += amount;
hold -= amount;
@@ -3189,6 +3217,7 @@ void sell(void) {
}
cash += (amount * price[choice]);
+ cash_dirty = 1;
hold += amount;
return;
@@ -3247,7 +3276,7 @@ void visit_bank(void) {
#else
bank += amount;
#endif
- if(amount) bank_dirty = 1;
+ if(amount) bank_dirty = cash_dirty = 1;
break;
} else {
you_only_have(0);
@@ -3263,7 +3292,6 @@ void visit_bank(void) {
print_msg(M_withdraw);
amount = get_num();
- if(amount) bank_dirty = 1;
#ifdef BIGNUM
if(amount == UINT32_MAX) {
big_copy(bigamt, bank);
@@ -3285,6 +3313,7 @@ void visit_bank(void) {
big_sub(bank, bank, bigamt);
big_add(bigcash, bigcash, bigamt);
big_to_ulong(bigcash, &cash);
+ if(amount) bank_dirty = cash_dirty = 1;
break;
}
#else
@@ -3296,6 +3325,7 @@ void visit_bank(void) {
{
cash += amount;
bank -= amount;
+ if(amount) bank_dirty = cash_dirty = 1;
break;
} else {
you_only_have(1);
@@ -3498,6 +3528,7 @@ int main(void) {
hold += hold_[0];
hold_[0] = 0;
cash -= fine;
+ cash_dirty = 1;
port_stats();
@@ -3564,6 +3595,7 @@ int main(void) {
unsigned long robbed = randclamp((cash >> 2) + (cash >> 1));
cash -= robbed;
+ cash_dirty = 1;
port_stats();
compradores_report();
diff --git a/timed_getch.s b/timed_getch.s
index fdb0dc0..a243c20 100644
--- a/timed_getch.s
+++ b/timed_getch.s
@@ -69,9 +69,7 @@ _timed_getch:
beq @wait4key
; user hit a key, handle it. but don't print a cursor.
-_agetc_no_cursor:
- jsr _cgetc
- jmp finish_agetc
+ bne _agetc_no_cursor
; _agetc removes the inverse-video bit, and if
; a control key is pressed, it turns it into the non-control version
@@ -83,6 +81,7 @@ _agetc:
lda #$80 ; inverse space (putchar uses screen codes)
jsr putchar
+_agetc_no_cursor:
jsr _cgetc ; get ATASCII code of keypress
pha