diff options
-rw-r--r-- | rand.s | 104 | ||||
-rw-r--r-- | taipan.c | 468 | ||||
-rw-r--r-- | timed_getch.s | 5 |
3 files changed, 355 insertions, 222 deletions
@@ -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 @@ -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 |