Ze_ro Posted April 24, 2004 Share Posted April 24, 2004 Before I try taking on a huge program that will likely be more than I can handle, I've decided to start off in the shallow end and write a simple 2600 version of the classic Simon game. I already have it to the point where it draws pretty much everything on the screen, and will actually light up the buttons when you wiggle the joystick... but I need some way to generate random numbers in order for this to actually act anything like the original Simon. I noticed this bit of code that Eric Ball posted in another thread: ; simple 8 bit LFSR (RANDOM is a byte in Zero Page RAM) RAND LDA RANDOM BEQ XSEED LSR BCC SRAND XSEED EOR #$A9 SRAND STA RANDOM RTS ... which is a nice piece of code... but I think I'm going to need something a little more random than that. Otherwise, every time you start the game, you'll get the exact same sequence of button presses, which wouldn't be very fun at all. So what's the best way to go about this? Would it be a decent idea to start a timer when the VCS turns on, and then when the reset button is pressed, read the timer and use it to seed this LFSR? Or what if I read one of the RAM addresses before they're initialized, and use that to seed the LFSR? --Zero Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted April 24, 2004 Share Posted April 24, 2004 Yeah...using the frame counter to seed the generator might work well. When the switch is moved, it could be any 1 of 256 combinations. BTW I hope that you just use 4-way joystick movement as opposed to diagonals. The player can just hold the stick diagonally Look forward to more info on this...I used to play that thing 'till the lights burned out Quote Link to comment Share on other sites More sharing options...
DEBRO Posted April 24, 2004 Share Posted April 24, 2004 Before I try taking on a huge program that will likely be more than I can handle, I've decided to start off in the shallow end and write a simple 2600 version of the classic Simon game. Video Simon but I need some way to generate random numbers in order for this to actually act anything like the original Simon. I noticed this bit of code that Eric Ball posted in another thread: ; simple 8 bit LFSR (RANDOM is a byte in Zero Page RAM) RAND LDA RANDOM BEQ XSEED LSR BCC SRAND XSEED EOR #$A9 SRAND STA RANDOM RTS You can also use a random number generater posted to [stella] by Thomas here http://www.biglist.com/lists/stella/archiv...1/msg00222.html. The idea is to seed the random seed with a non zero number. To get sort of random numbers I used a technique used in Berzerk. I stored the TIM64T value in the y-register at start up. After the initial cart zeroing routine I set the random seed from y. Also during the game I would call the random routine periodically to sort of give better results. Other games would use user input (joystick values) to re-seed the random number (i.e. Donkey Kong). Quote Link to comment Share on other sites More sharing options...
Ze_ro Posted April 25, 2004 Author Share Posted April 25, 2004 BTW I hope that you just use 4-way joystick movement as opposed to diagonals. The player can just hold the stick diagonally Damn straight. I didn't play all that Q*Bert for nothing! I'm still trying to think of a clever way to allow you to switch the joystick control on the fly (ie, switch between Up = Top right, or Up = Top left). Video Simon Yeah, I know... this isn't really a serious attempt at a marketable game or anything... it's mostly just to get my feet wet. I'd eventually like to try making a space trading game (think Elite, except with Space War type battle scenes instead of 3D wireframe graphics), but that's obviously far beyond my abilities at the moment. You can also use a random number generater posted to [stella] by Thomas herehttp://www.biglist.com/lists/stella/archives/200401/msg00222.html. Looks like the exact same thing, except that it doesn't check for zero... To get sort of random numbers I used a technique used in Berzerk. I stored the TIM64T value in the y-register at start up. After the initial cart zeroing routine I set the random seed from y. So loading a value from uninitialized memory will actually give you a usable value? I was worried that 90% of the time, it would just give zero, and that emulators might give you zero 100% of the time. I'll be throwing out all but the first two bits of the number anyways, since all I really need is a number between 0 and 3. --Zero Quote Link to comment Share on other sites More sharing options...
Ze_ro Posted April 25, 2004 Author Share Posted April 25, 2004 Alright, I tried initializing things with TIM64T, and although I DO get some nice, random sequences, they always end up starting with red. I guess I'll have to fiddle with things a bit. --Zero Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted April 25, 2004 Share Posted April 25, 2004 BTW I hope that you just use 4-way joystick movement as opposed to diagonals. The player can just hold the stick diagonally Damn straight. I didn't play all that Q*Bert for nothing! I'm still trying to think of a clever way to allow you to switch the joystick control on the fly (ie, switch between Up = Top right, or Up = Top left). Use a set of "masks" in a table. Grab the difficulty switch setting, game selection, whatever you use to do the option...and use that to set an offset value to the correct table of mask values. Then just match them up (there's probably a easier way, but that's what I just guessed at). So loading a value from uninitialized memory will actually give you a usable value? I was worried that 90% of the time, it would just give zero, and that emulators might give you zero 100% of the time. There is no guarantee that the value is going to be anything for certian...that's how come the routine to begin with AFAIK. You might be right about emus tho. In either case, it would always be usable (the ram location always exists...and it always contains something - even if it's just a zero). Quote Link to comment Share on other sites More sharing options...
Ze_ro Posted April 25, 2004 Author Share Posted April 25, 2004 I'm still trying to think of a clever way to allow you to switch the joystick control on the fly. Use a set of "masks" in a table. Grab the difficulty switch setting, game selection, whatever you use to do the option...and use that to set an offset value to the correct table of mask values. Then just match them up Yeah, I was thinking of doing it that way... but I was also trying to think of some way to transform the joystick values that would correspond with a 90 degree rotation... however, I can't seem to come up with any simple routine that can do that without taking up more memory, and taking more time. Oh well, there's still a lot of work to do before I start to worry about niceties like that anyways. By the way, anywhere know where I can find out what frequencies the original Simon toy used so that I can try to approximate them with the VCS? And while I'm talking about sound, is it just a matter of sticking the proper values into AUDCx, AUDFx and AUDVx, and then just waiting an appropriate length of time before muting AUDVx? Or is there something more to it than that? [Edit: Ah, I was right! Sound is refreshingly simple after dealing with all the asymmetrical playfield and sprite position nonsense] --Zero Quote Link to comment Share on other sites More sharing options...
Happy_Dude Posted April 25, 2004 Share Posted April 25, 2004 Heres the code I'm using in Master Mind deluxe. It was posted to [stella] It's called once per frame in the attract mode (title screen) until the fire button is pressed Seems random enough ; RANDOM NUMBER GENERATOR;Rand1, Rand2, Rand3, Rand4 are RAM locations, initialized to any nonzero;value at program start (I use #$6D);RandomBit generates one random bit.;RandomByte generates one random byte and returns it in the accumulator. RandomBit SUBROUTINE lda Rand4 asl asl asl eor Rand4;new bit is now in bit 6 of A asl asl ;new bit is now in carry rol Rand1;shift new bit into bit 0 of register; bit 7 goes into carry rol Rand2;shift old bit 7 into bit 8, etc. rol Rand3 rol Rand4 rts RandomByte SUBROUTINE ldx #8 RandomByte1 jsr RandomBit dex bne RandomByte1 lda Rand1 rts Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted April 25, 2004 Share Posted April 25, 2004 Which routine you use depends on how random you need the results. Just make sure that the initial value isn't 0. The simple code uses only one byte and produces a sequence repeating after 2^8-1 (=255) iterations which is usually random enough. This kind of random number generator is called LFSR (linear feedback shift register). Use Google for more informations and optimal values. With 32 bits you get 2^32-1 (=4294967295!) values before the sequence repeats. This is probably more than required in most cases, so 16 bits might be a good compromise there. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted April 25, 2004 Share Posted April 25, 2004 Alright, I tried initializing things with TIM64T, and although I DO get some nice, random sequences, they always end up starting with red. I guess I'll have to fiddle with things a bit. You have to use INTIM instead. BTW: Doesn't work when using a SC (or CC), since the register has been initialized by the according software. And e.g. Windows z26 doesn't support this trick (yet!), while the DOS version does. Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted April 25, 2004 Share Posted April 25, 2004 Yeah, I was thinking of doing it that way... but I was also trying to think of some way to transform the joystick values that would correspond with a 90 degree rotation... however, I can't seem to come up with any simple routine that can do that without taking up more memory, and taking more time. Well, how about a "temp" approach? Just save the left/right delta to a temp, up/down delta to another temp, and have the program add them to the proper ram location on the routine exit. I just woke up...sorry if this has some mistakes JoyStick: LDA #$00 ;2 clear the temps STA Temp1 ;3 STA Temp2 ;3 LDA SWCHA ;4 load stick LDX Player ;3 Check player # BEQ CheckStick ;2 branch if left joystick ASL ;2 otherwise, move the bits to the top ASL ;2 ASL ;2 ASL ;2 CheckStick: AND #$F0 ;2 CMP #$F0 ;2 BEQ End ;2 branch if no movement ASL ;2 check right BCS NoRight ;2 INC Temp1 ;5 NoRight: ASL ;2 check left BCS NoLeft ;2 DEC Temp1 ;5 NoLeft: ASL ;2 check down BCS NoDown ;2 INC Temp2 ;5 NoDown: ASL ;2 check up BCS NoUp ;2 DEC Temp2 ;5 NoUp: BIT SWCHB ;2 check difficulty BMI UpRight ;2 branch if selected CLC ;2 LDA PlayerX,X ;4 ADC Temp1 ;3 STA PlayerX,X ;4 CLC ;2 LDA PlayerY,X ;4 ADC Temp2 ;3 STA PlayerY,X ;4 JMP End ;3 UpRight: CLC ;2 LDA PlayerY,X ;4 ADC Temp1 ;3 STA PlayerY,X ;4 CLC ;2 LDA PlayerX,X ;4 ADC Temp2 ;3 STA PlayerX,X ;4 End: RTS ;6 Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted April 25, 2004 Share Posted April 25, 2004 Yeah...the above stores deltas more suited to actual movement. Since all you are interested in is a value from 0 to 3, you could probably do this... JoyStick: LDA #$FF ;2 load a value for non-movement LDY Player ;3 Load the player's number STA Selection,Y ;4 store the minus value LDA SWCHA ;4 load stick CPY #$00 ;3 Check player # BEQ CheckStick ;2 branch if left joystick ASL ;2 otherwise, move the bits to the top ASL ;2 ASL ;2 ASL ;2 CheckStick: AND #$F0 ;2 CMP #$F0 ;2 BEQ End ;2 branch if no movement ASL ;2 check right (Y remains at zero) ASL ;2 check left BCS CheckDown ;2 INY ;2 (Y=1) CheckDown: ASL ;2 check down BCS CheckUp ;2 LDY #$02 ;2 CheckUp: ASL ;2 check up BCS ChecksDone ;2 LDY #$03 ;2 ChecksDone: TYA ;2 ;I just used the left difficulty switch to hold the flag whether to flip ;the values...you could use anything...just branch off to skip the ;EOR afterward. You could even test the Player value using BIT to ;decide if you should be checking the right difficulty switch, for example. BIT SWCHB ;2 check difficulty BMI SaveStick ;2 branch if selected EOR #$02 (left/right becomes up/down and visa-versa) SaveStick: LDY Player ;3 Load the player's number STA Selection,Y ;4 store the direction End: RTS ;6 Selection and Selection+1 now contain the 0-3 value for each player (or $FF if the stick was not moved). Variable "Player" holds the current player you are checking. You could just use a high bit from another ram location to do that rather than waste a full byte. 49 bytes used by the above routine. No temps used. Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted April 25, 2004 Share Posted April 25, 2004 The same routine using masks... JoyStick: LDA SWCHA ;4 load stick BIT Player ;3 Check player # BEQ CheckStick ;2 branch if left joystick ASL ;2 otherwise, move the bits to the top ASL ;2 ASL ;2 ASL ;2 CheckStick: AND #$F0 ;2 CMP #$F0 ;2 BEQ SaveStick ;2 branch if no movement LDY #$03 ;2 initialize a counter MaskLoop: CMP MaskValues-1,Y ;4 check if the mask matches BEQ ChecksDone ;2 Yes...keep current Y DEY ;2 otherwise reduce and try again BNE MaskLoop ;2 (skips Y=0...default) ChecksDone: TYA ;2 ;I just used the left difficulty switch to hold the flag whether to flip ;the values...you could use anything...just branch off to skip the ;EOR afterward. You could even test the Player value using BIT to ;decide if you should be checking the right difficulty switch, for example. BIT SWCHB ;2 check difficulty BMI SaveStick ;2 branch if selected EOR #$02 (left/right becomes up/down and visa-versa) SaveStick: LDY Player ;3 Load the player's number STA Selection,Y ;4 store the direction RTS ;6 MaskValues: .byte %11010000,%10110000,%01110000 Selection and Selection+1 now contain the 0-3 value for each player (or minus value $F0 if the stick was not moved). Variable "Player" holds the current player you are checking. You could just use a high bit from another ram location to do that rather than waste a full byte. 43 bytes used by the above routine. No temps used. Quote Link to comment Share on other sites More sharing options...
DEBRO Posted April 25, 2004 Share Posted April 25, 2004 Alright, I tried initializing things with TIM64T, and although I DO get some nice, random sequences, they always end up starting with red. I guess I'll have to fiddle with things a bit. You have to use INTIM instead. Doh! Thanks for correcting me. Sorry about that Ze_ro. BTW, there is also a post on [stella] about the values of RIOT at start up. Read the RIOT RAM test thread. Quote Link to comment Share on other sites More sharing options...
Ze_ro Posted April 27, 2004 Author Share Posted April 27, 2004 Thanks for the help guys! The game now generates it's own sequences and plays them back perfectly. My main problem now is latching the switches... as it is right now, I have it so that the select switch will rotate through three different moves: a demo sequence that just plays back a hardcoded sequence as an "attract mode" of sorts, an actual sequence generation where it continuously plays back a sequence over and over, adding a button each time it finishes, and a third mode that just lights up whatever button the user points at. The problem with this is that it reads the select switch every single frame, so it switches game modes 60 times a second! The same goes for my joystick handling, although it hasn't been a problem until now, since I have to come up with some way to compare the user's button presses against the queue. What's the easiest way to go about this? Should I just keep track of SWCHA and SWCHB, and only act if there is a change? Or is there a better way of doing this that doesn't require me to give up two bytes of RAM? --Zero Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted April 27, 2004 Share Posted April 27, 2004 Nope...I'm pretty sure you need to store the values (so the program can tell if there is any change). Fortunately for you, a game of this type should have plenty of ram left over Plus it has the added bonus of making those checks use less space later on, since you'll be reading from the ram locations instead of the actual SWCHA/B addresses. Quote Link to comment Share on other sites More sharing options...
EricBall Posted April 28, 2004 Share Posted April 28, 2004 This is the code from SpaceWar! 7800 for handling the fire button, which you should be able to adapt to handle SWCHB. FIRE_A LDA INPT4,X TAY ; save result in unused register EOR FIRE_1,X; quick compare BPL FIRE_B ; same as last frame STY FIRE_1,X; save if different BNE FIRE_Z ; and exit FIRE_B TYA ; restore current value EOR FIRE_2,X; quick compare again BPL FIRE_Z ; no change STY FIRE_2,X; save if changed TYA ; set flags BMI FIRE_Z ; up (X is used because I use the same code for both players). For SWCHB you will need to mask off the desired bits and change the BPL/BMI to BEQ/BNE. And I appologise to those programmers that prefer lowercase, but uppercase ASM is a habit I've had since I started on the CoCo/6809 where we didn't have no stinking lowercase. Quote Link to comment Share on other sites More sharing options...
Ze_ro Posted April 28, 2004 Author Share Posted April 28, 2004 Nope...I'm pretty sure you need to store the values (so the program can tell if there is any change). Fortunately for you, a game of this type should have plenty of ram left over True enough... in fact, assuming I don't end up using any more ram than I'm already using, the sequences can get as long as 121 buttons before the program starts to overwrite important data (And in fact, using some better memory handling techniques, I could easily double or even quadruple that I think). I think that's long enough that bounds checking is probably unnecessary. However, I hate to write inefficient code regardless of the circumstances. This is actually going along faster than I expected. I might have this whole thing wrapped up by the end of the week --Zero Quote Link to comment Share on other sites More sharing options...
Ze_ro Posted April 29, 2004 Author Share Posted April 29, 2004 Awesome! I have the entire game completely working and playable now! Not too bad for only 5 days worth of work I've attached a screenshot... it doesn't look like much, but it was never really meant to look like much. All it needs are a few tweaks... some sprite changes... add difficulty modes... clean up some of the code... figure out why Z26 says I'm only display 254 scanlines... after that's done, I can move on to something else. Again, thanks for your help guys! --Zero Quote Link to comment Share on other sites More sharing options...
DEBRO Posted April 29, 2004 Share Posted April 29, 2004 Quote Link to comment Share on other sites More sharing options...
ErikM Posted June 24, 2004 Share Posted June 24, 2004 Heres the code I'm using in Master Mind deluxe. It was posted to [stella] Heh, that's my original code from way back when (1997), that I originally wrote for INV. Funny how things can come full circle. The advantage of the 32-bit version over a shorter one is that it's less prone to artifacts in the distribution of the output. For example, if you pull an 8-bit random number from an 8-bit LFSR, you can never get zero. One easy way to seed any shift-based RNG is to just simply call it once per frame whether or not you need a random number. Unless the user manages to press Game Reset on exactly the same frame after every bootup, you'll get a reasonably random initialization of the sequence. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.