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 | 
