neilobremski
Experienced Member
The puzzles of Magenta's Maze are generated using the barely sufficient rand() function supplied by MSC 5.1. There are some issues with this, most notably that the highest bit of the 16-bit word output is always zero. The way we C programmers were taught to use this function was to modulus the result by the maximum amount desired thus providing 0 to N - 1 (where N is the maximum). This is unfortunately how Magenta's Maze uses it to place objects within the map and rotate the magic circles.
However, it also uses a function called "coinflip" which returns 1 or 0 randomly. This function calls rand() and stores the result in a static variable:
There are two problems with this with the first being the aforementioned zero high bit, meaning that every 16th coin flip will always be FALSE! Argh! The second and more subtle problem is the use of a static function variable for the random bits which do not get reset when the player finishes (or quits) a map and starts a new one without restarting the game.
I decided for the assembler version to keep the first bug so that the generated levels are the same as the first version of a freshly started Magenta's Maze. However, I fixed the second bug which means that subsequent maps of the still-loaded game will be different. This was a hard decision to make, honestly, because I wanted direct compatibility ... but I cannot ignore that that bug really is a bug!
Finally, here is the code for rand(), srand(), and coinflip() in assembler.
However, it also uses a function called "coinflip" which returns 1 or 0 randomly. This function calls rand() and stores the result in a static variable:
Code:
char coinflip(void)
{
static int bitsleft = 0;
static int data = 0;
if (!bitsleft) {
data = rand();
bitsleft = 15;
return (char)(data & 1);
}
bitsleft--;
data >>= 1;
return (char)(data & 1);
}
There are two problems with this with the first being the aforementioned zero high bit, meaning that every 16th coin flip will always be FALSE! Argh! The second and more subtle problem is the use of a static function variable for the random bits which do not get reset when the player finishes (or quits) a map and starts a new one without restarting the game.
I decided for the assembler version to keep the first bug so that the generated levels are the same as the first version of a freshly started Magenta's Maze. However, I fixed the second bug which means that subsequent maps of the still-loaded game will be different. This was a hard decision to make, honestly, because I wanted direct compatibility ... but I cannot ignore that that bug really is a bug!
Finally, here is the code for rand(), srand(), and coinflip() in assembler.
Code:
a 300
; ----------------------------------------------------------------------------
; rand () :RAND
;
; Psuedo-random number generator that returns a (positive) 16-bit value in AX.
; NOTE: this is intended to return identical values to the rand() in MSC 5.1.
;
; CALLER MUST make DS = CS.
;
MOV AX, 43FD; 300
MOV DX, 0003; 303
PUSH DX ; 306
PUSH AX ; 307
PUSH WORD [035C]; 308
PUSH WORD [035A]; 30C
CALL 0326 ; 310
ADD AX, 9EC3; 313
ADC DX, 26 ; 316
MOV [035A], AX ; 319
MOV [035C], DX ; 31C
MOV AX, DX ; 320
AND AH, 7F ; 322
RET ; 325
;
PUSH BP ; 326
MOV BP, SP ; 327
MOV AX, [BP+06] ; 329
MOV BX, [BP+0A] ; 32C
OR BX, AX ; 32F
MOV BX, [BP+08] ; 331
JNZ 0341 ; 334
;
MOV AX, [BP+04] ; 336
MUL BX ; 339
MOV SP, BP ; 33B
POP BP ; 33D
RET 0008 ; 33E
;
MUL BX ; 341
MOV CX, AX ; 343
MOV AX, [BP+04] ; 345
MUL WORD [BP+0A]; 348
ADD CX, AX ; 34B
MOV AX, [BP+04] ; 34D
MUL BX ; 350
ADD DX, CX ; 352
MOV SP, BP ; 354
POP BP ; 356
RET 0008 ; 357
; random number seed plus overflow/remainder
e 035A 01 00 00 00
a 360
; ----------------------------------------------------------------------------
; CoinFlip () :COINFLIP
;
; Uses RAND for a psuedo-random bit comparison (use like TEST/JE|JNE).
;
; CALLER MUST make DS = CS.
;
PUSH SI ; 360
MOV SI, 037C; 361
DEC BYTE[SI+2] ; 364
JS 0371 ; 367 >COINFLIP_RAND
SHR WORD[SI], 1; 369
TEST WORD[SI], 1; 36B :COINFLIP_TEST
POP SI ; 36F
RET ; 370
CALL 0300 ; 371 >RAND :COINFLIP_RAND
MOV [SI], AX; 374
MOV BYTE[SI+2],F; 376
JMP 036B ; 37A >COINFLIP_TEST
; coinflip data
e 037C 00 00 00 00
a 380
; ----------------------------------------------------------------------------
; srand () :SRAND
;
; Seed random number generator with AX and reset coin flip data.
;
MOV [035A], AX ; 380
XOR AX, AX ; 383
MOV [035C], AX ; 385
MOV [037C], AX ; 388
MOV [037E], AX ; 38B
RET ; 38E