| www.retrosoftware.co.uk http://www.retrosoftware.co.uk/forum/ |
|
| Odd behaviour with a delay loop. Can anyone help? http://www.retrosoftware.co.uk/forum/viewtopic.php?f=73&t=763 |
Page 1 of 1 |
| Author: | jbnbeeb [ Mon Apr 16, 2012 1:18 pm ] |
| Post subject: | Odd behaviour with a delay loop. Can anyone help? |
Hi, I've been (slowly) coding a Snake game as a fun way to learn assembler. See http://www.retrosoftware.co.uk/forum/viewtopic.php?f=19&t=745 and http://www.retrosoftware.co.uk/wiki/index.php/JSnakeDevDiary I've encountered a bug that I can't work out. I have a delay loop which I use to slow down the movement of the Snake to a playable speed. At the moment, the loops always decrement from same start value, but in time I would like to be able to vary said start value (to change Snake movement speed). Meanwhile, I've been changing the the start values and then compiling/running just to see any visible differences in speed. Two issues: 1: Main issue - when I only have the delay loop cycle round a few times (I can't determine the exact value as yet) .. the Snake speeds up when say Left key (Z) is held down, but all the other directions are unaffected. Sometimes two or three directions are affected. This seems to depend on the values entered.. but I haven't established an exact pattern. If I increase the start values above a certain threshold (that I haven't established exactly yet) .. the issue disappears. 2: Observed speed up of snake movement doesn't seem proportional to delay loop start values. i.e say I have outer loop start at 192 and inner loop at 128, I can't see a visible difference until inner loop starts at 64.. At the moment, I'm more bothered about first issue. Here is the Delay routine Code: .Delay ldx #48 .dxloop ldy #32 .dyloop dey bne dyloop dex bne dxloop lda #0 : ldx #0 : ldy #0 rts This is called from a main loop which jumps to these subroutines, moving the snake along the screen one unit at a time until a collision occurs , i.e: jsr Delay jsr GetKeypress jsr Move (animates the Snake!) jsr CollisionDetect I am wondering if it is something to do with the GetKeyPress routine being affected by preceding Delay routine - but if this is the case, why do I not encounter issue 1 regardless of values in the inner and outer loops? Here is an excerpt of the GetKeyPress routine: Code: ldx #Key_Z jsr inkey bne LeftPressed ldx #Key_X jsr inkey bne RightPressed ldx #Key_Colon jsr inkey bne UpPressed ldx #Key_ForwardSlash jsr inkey bne DownPressed jmp GKPEnd ; no key pressed .LeftPressed lda SnakeDirection cmp #DirRight beq IgnoreLeftKey lda #DirLeft sta SnakeDirection .IgnoreLeftKey rts . . . same idea for rest of directions . . And the inkey subroutine: Code: .inkey ; routine from Creative assembler book PHA ;Save A TYA ; Save Y PHA LDY #&FF ;Negative numbers LDA #&81 ; osbyte &81 is INKEY JSR osbyte ; Do it PLA ;Restore Y TAY PLA ; Restore A CPX #&00 ; Adjust zero flag RTS ; and return Any help or suggestions on troubleshooting this would be gratefully received. Thanks, jbnbeeb |
|
| Author: | PitfallJ [ Mon Apr 16, 2012 2:31 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
For a mainloop I'd do something like this: Code: MAINLOOP: JSR VSYNC JSR GETKEYS JSR MOVE JSR COLLISION JMP MAINLOOP where VSYNC waits for the next 50hz frame - that means your game can't loop super fast. (It could run slower of course) Code: VSYNC: LDA #&13 JSR _OSBYTE RTS If I wanted the character to run slower I could still run the game at 50hz but could use a fractional position like this: Code: LDA xpos_fraction CLC ADC #&80 STA xpos_fraction LDA xpos ADC #0 STA xpos So xpos would move 1 pixel every 2 frames or ADC #&40 to move 1 pixel every 4 frames etc. Your wait loop looks ok but the changing your X value has 32 times the effect of changing the Y value. This version that has a fixed Y loop amount would be proportional based on the input X, I put a couple of nops in the middle to make it even slower... Code: LDX #10 JSR WAIT ; this is twice as long as before LDX#20 JSR WAIT WAIT: .dxloop ldy #0 .dyloop nop nop dey bne dyloop dex bne dxloop rts - PJ |
|
| Author: | jbnbeeb [ Mon Apr 16, 2012 5:50 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Thanks for the advice PJ. I adapted your xpos fraction idea and placed this in my Move routine. I am using Vsync code from SteveO http://www.retrosoftware.co.uk/wiki/index.php/MovingSpritesMode2 I was using it from my Move routine but I'm now calling it from Main loop. I've dropped the delay loop in favour of the xpos fraction idea, but unfortunately I still have my main issue (see issue 1 above) in that the Snake moves faster when a particular direction key is pressed. I did some more testing and concluded that the GetKeyPress routine was the culprit. Whichever key I was trying to read first in the routine was the one that caused the Snake to move faster. I then had a read around, and am now using osbyte &79 to scan the keyboard from internal key 66 (X) upwards. I thought this would make things quicker as now I only have to call osbyte once to capture the internal key code. This hasn't cured the issue and has only made me more confused. Note the order I capture keys in code snippet below. My testing is showing that pressing "/" (Down) moves the Snake fastest, while pressing ":" (up) is moving the snake a bit faster. Otherwise, the snake speed remains constant whether no keys are pressed, or left or right is pressed. I'd like the speed of the snake to remain constant regardless of any keys are pressed or not, as this is like Acornsoft Snake or Hypervyper in that the snake continues to move in whichever direction was last pressed on keybd. When I was using the delay loop in my first post (with higher values).. I didn't get this problem at all, the snake moved at a constanst speed, and I was able to get it to go quite fast.. but there was a limit, at which point I encountered the same issue as I have now. Once again, any thoughts on what is happening and what can be done to resolve it would be appreciated. I'm stumped at present as my keybd routine doesn't look radically different to some of the source code for games on this site. Code: lda #&79
ldx #66 jsr osbyte cpx #Key_ForwardSlash beq DownPressed cpx #Key_Z beq LeftPressed cpx #Key_X beq RightPressed cpx #Key_Colon beq UpPressed rts ;jmp GKPEnd ; no key pressed .LeftPressed lda SnakeDirection cmp #DirRight beq IgnoreLeftKey lda #DirLeft sta SnakeDirection . . |
|
| Author: | TomW [ Mon Apr 16, 2012 8:15 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
You don't want to use OSBYTE &79 for this, I'd suggest going back to your older code. I can't see anything particularly wrong with the code in your first post, could you post the entire movement routine? |
|
| Author: | jbnbeeb [ Mon Apr 16, 2012 8:31 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Hi, Move routine as requested. Code: .Move ;jmp GetOldHead ;==wipe old tail 2x3 blob from screen ;retrieve x,y co-ords of old tail CLC LDA DelayFraction ADC #&20 STA DelayFraction LDA DelayCounter ADC #0 STA DelayCounter lda DelayCounter bne EofDelay rts .EofDelay lda #0 sta temp_val sta temp_val+1 COMP16 GrowSnakeCounter, temp_val beq WipeTail DE16 GrowSnakeCounter ;jsr UpdateBanner jmp GetOldHead .WipeTail ldx #0 lda (snaketail_ptr,X) sta XOrd jsr IncSnakeTailPtr lda (snaketail_ptr,X) sta YOrd ;eor retrieved x,y co-ords (actually eor the calculated screen address of the x,y co ords with the screen itself) ldy #0 ldx #0 lda #0 jsr ScreenStartAddress lda #&0C eor (XYScreenAddress,X) sta (XYScreenAddress,X) inc YOrd jsr ScreenStartAddress lda #&0C ldx #0 eor (XYScreenAddress,X) sta (XYScreenAddress,X) inc YOrd jsr ScreenStartAddress lda #&0C ldx #0 eor (XYScreenAddress,X) sta (XYScreenAddress,X) dec YOrd dec YOrd ;(increment tail pointer) jsr IncSnakeTailPtr ;=================================== .GetOldHead ldx #0 jsr DecSnakeHeadPtr lda (snakehead_ptr,X) sta XOrd jsr IncSnakeHeadPtr lda (snakehead_ptr,X) sta YOrd ; coords of old head lda #0 ldx #0 ldy #0 .PlotNewHead lda SnakeDirection : cmp #DirLeft : beq mvLeft cmp #DirRight : beq mvRight cmp #DirUp : beq mvUp cmp #DirDown :beq mvDown .mvLeft dec XOrd : jmp PlotBlob .mvRight inc XOrd ;x ord of new head jmp PlotBlob .mvUp sec :lda YOrd :sbc #3 sta YOrd jmp PlotBlob .mvDown lda #2 adc YOrd sta YOrd jmp PlotBlob .PlotBlob jsr IncSnakeHeadPtr ldx #0 lda XOrd sta (snakehead_ptr, X) jsr IncSnakeHeadPtr lda YOrd sta (snakehead_ptr, X) ldx #0 lda #0 ldy #0 jsr ScreenStartAddress lda #&0C eor (XYScreenAddress,X) : sta (XYScreenAddress,X) sta snakehead_scrn_addr ; store EORed result of plotted pixel - ie it's colour. If it is green (&0C), no collision, otherwise, we have a collision. inc YOrd jsr ScreenStartAddress lda #&0C ldx #0 eor (XYScreenAddress,X) :sta (XYScreenAddress,X) sta snakehead_scrn_addr+1 inc YOrd jsr ScreenStartAddress lda #&0C ldx #0 eor (XYScreenAddress,X) : sta (XYScreenAddress,X) sta snakehead_scrn_addr+2 dec YOrd dec YOrd rts ;End Move |
|
| Author: | MartinB [ Tue Apr 17, 2012 8:22 am ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Is your code interrupt driven in any way? In these circumstances where there's apparently nothing amiss, I usually expect to find that say X or Y are getting corrupted by some interrupt code and thus shortening (or lengthening) the loop. Just a thought.... |
|
| Author: | jbnbeeb [ Tue Apr 17, 2012 1:36 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Yes MartinB, this is a good point that I haven't thoroughly investigated yet. The vsync routine I'm using generates an interrupt. I've taken this from the sprite plotting example by Steve O'Leary in the Sample Code section of this site - snippet below. http://www.retrosoftware.co.uk/wiki/index.php/MovingSpritesMode2 Yesterday I was focussing on the keyboard routine. I ended up using yet another excellent bit of sample code, this time from Richard Talbot-Watkins: http://www.retrosoftware.co.uk/wiki/index.php/Reading_the_keyboard_by_direct_hardware_access I disable , then re-enable interrupts around each key check. This is definitely a much quicker read key routine. I had to adjust the "DelayFraction" as suggested by PJ above to a lower value in the Move routine to compensate. However, at certain speeds of snake movement, I get the same issue of the Snake speeding up when I hold down the Left or Right keys (left seems to move fastest.. or whichever key I check first in the read key routine). Left moves the snake quicker when I adjust the DelayFraction value higher .. &20 and above.. ie only moving snake every 8th frame. Sweetspot, with no speed up of snake when any direction key is held down seems to be &10. So at some speeds the snake speed is affected (increases) by the keyboard routine for so long as left or right keys are held down. I don't understand why it isn't affected regardless of DelayFraction setting. My aim is to a) not have the snake speed affected by holding down a key, b) to be able to control the speed of snake movement.. e.g. when player gets above a given score, increase snake speed. I reset the registers to 0 when calling many routines. How do I check for register corruption without affecting the registers Code: sei ; first things first turn off interupts when
; programming system via ; Set the code to execute when an interupt occurs (which will be the vsync interupt) LDA #LO(IRQHandler) ; set the interupt request to be ours STA InteruptRequestVectorLow ; we don't preserve anything previous LDA #HI(IRQHandler) ; note as interupts disabled it's safe to do this STA InteruptRequestVectorHi ; set CA1 to interupt when Vysync occurs LDA #%01111111 ; disable all 6522 interupts STA SysVIA_InteruptEnable ; we just want the ones we're interested in LDA #%10000010 ; enable CA1 interupt (VSync input from 6845) STA SysVIA_InteruptEnable LDA #%00000100 ; Set CA1 to interupts when it gets a positive ; edges, i.e. goes from 0 to 1 STA SysVIA_PeripheralControl ; following sets timer 1 to timed interupt each time Timer1 loaded,Shift register ; disabled,PB,PA Latches disabled LDA #0 STA SysVIA_AuxControl STA EndOfDisplayReached; cli ; finished setting up via, enable interupts rts ; .IRQHandler ; fires when the interupt occurs. LDA SysVIA_InteruptFlag ; look at the interupt flags of the system via AND #%00000010 ; mask out all but CA1 (which is linked to Vsync ; circuit), if set CA1 has had an active edge. BEQ NotVysncInterrupt ; not set check other interupts STA SysVIA_InteruptFlag ; is set so vsync occurred, storing back clears interupt flag LDA #%11000000 ; enable timer 1 interupt STA SysVIA_InteruptEnable LDA #$C8 ; set timer1, this value is critical to the smooth scrolling of the routine STA SysVIA_Timer1CounterLatchLow LDA #$46 STA SysVIA_Timer1CounterLatchHi lda # 0 sta EndOfDisplayReached ; Set to false LDA $FC ; on entrying interupt A is put in FC, so get back rti ; exit the interupt .NotVysncInterrupt LDA SysVIA_InteruptFlag AND #%01000000 ; checking if interupt was timer1 BNE Timer1Interrupt ; it was timer 1 LDA $FC ; on entrying interupt A is put in FC, so get back RTI .Timer1Interrupt ; timer 1 has counted down so the CRT has just finished rendering the visible part ; of the display STA SysVIA_InteruptFlag ; clear interupt flags STA SysVIA_InteruptEnable dec EndOfDisplayReached ; previous value was 0 so this makes it not zero LDA $FC ; on entrying interupt A is put in FC, so get back rti |
|
| Author: | MartinB [ Tue Apr 17, 2012 9:00 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
The responsibility for protecting registers really lies with any interrupt-called code and to be honest, /most/ of the ofiicial OS routines do just that. If you want to try a quick test, put an SEI at the start of your delay loop and a CLI at the end. This isn't ideal for your end product (unless that's what you want it achieve - i.e. an exclusive delay-only monopoly) but it might well give you a good clue In your own callable routines, the basic register protection is acieved through the usual PHA TYA PHA TXA PHA or similar followed by the reverse before the RTS or, for greater speed, store the three directly to a zp triple. Since you don't call anything from within your delay loop, it must be something external (interrupt code) that is rogering your count but you can't protect the delay code from that unless you again use a couple of private zp locations as your inner/outer counters. Again, this would also give you a clue as to where the problem might lie. |
|
| Author: | PitfallJ [ Tue Apr 17, 2012 9:23 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
As I understand it you can only use zero page from $00-&9F as the rest is used by the OS: sound, vdu calls and the like - are you using safe zero page addresses? If your game was running fast enough to begin with - I'd stick with the OSBYTE keyboard calls - I found using the hardware directly can be a total pain. (I only did it in my case because I needed the speed). - PJ |
|
| Author: | jbnbeeb [ Tue Apr 17, 2012 9:42 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Thanks for the feedback Martin and PJ. MartinB - for the time being I've now dispensed with the delay loop altogether and used the count ("DelayFraction") idea from PJ in second post above, though the snake moves more smoothly in my old code with the delay loop set at certain start values - and speed not affected by keypresses... otherwise, as per my first post, I get the same speed up issues regardless of what I've tried thus far. Noting MartinB's comments, I wonder if I should modify the vsync interrupt code listed above by saving /restoring registers in zp triple. At present, it seems the code doesn't save/restore contents of registers? jbnbeeb |
|
| Author: | MartinB [ Tue Apr 17, 2012 10:24 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
The vsync code above only uses A, not X or Y. A is saved in $FC by the initial OS code and restored just prior to the RTI so no, that particular code isn't responsible for any corruption of X and/or Y. I was initially referring to any other code you might be using in your game, maybe where you move the snake in response to an interrupt? |
|
| Author: | jbnbeeb [ Fri May 04, 2012 7:28 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Hi all, I'm still struggling to fix this issue. Essentially, the symptom is that when a direction key is held down in my snake game, the snake moves faster than if no key is pressed - but usually only in two directions. I wouldn't say I've drawn a definitive conclusion but here is what I *think* is going on: - When no keys are pressed, the game loop calls the keybd checking routine on each iteration - With no key pressed, the keybd checking routine will always take the same no. of cycles to execute, thus when the game loop calls the move snake routine, snake moves at a uniform rate - The first two directions checked are Left and Right. Holding these keys down results in snake moving faster. I think this is because the checks are earlier in the routine which I then rts out of once a given keypress is detected), thus on this occasion, routine has taken fewer cycles, and Move routine is then called sooner. - As up and down direction keys are checked later in the routine, the difference in no. of clock cycles to run the routine is smaller, and thus snake movement isn't noticeably faster The only interrupt code written in the program is the vsync routine as discussed above. I do sei / cli around the check for each key at the moment. I am using PJ's idea earlier in this thread so that the snake only moves every x no. of frames as a means of controlling rate of snake movement. Setting the "counter" at certain values results in a steady speed of snake. Other values result in the issue I'm discussing here. I also encountered this issue in my initial code, where I had an empty delay loop to control speed. Any further hints and thoughts would be much appreciated as I've been battling with this for some time. I could upload ssd and assembler (Beebasm) source if it would help? Thanks, jbnbeeb |
|
| Author: | PitfallJ [ Sat May 05, 2012 4:17 am ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Post the source. -PJ |
|
| Author: | jbnbeeb [ Sat May 05, 2012 3:04 pm ] | ||
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? | ||
Hi, source code attached as requested. There're several routines that could be rewritten now I'm a bit more proficient.. the routines I've been mentioning where I think the problem could be is in: .GetKeyPress and .Move PJ, Look out for first few lines in .Move where I've used your "draw every x times routine is called" idea Thanks, jbnbeeb
|
|||
| Author: | PitfallJ [ Sat May 05, 2012 4:00 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Crazy fast! I think this fixes it - look for ;PJ in the code for the changes. I added a new variable vsync_happened which I set to 1 as soon as vsync has occured. I replaced your VSYNC wait with VSYNC_NEW which waits for vsync_happened to be set and then clears it. I removed any wait at all in your move routine. It looks like your setting a timer in vblank and then waiting for the timer to time out whereas I'm just waiting for vblank. I general I'd not use your own IRQ Handler at all as you lose all the good things the OS gives you (like sound and keyboard) - unless you JMP(oldvec) at the end of the IRQ instead of RTI. Nice to see lots of comments - and I never thought about tabbing 6502 like 'c' - that's an idea! (so old school here) My only other comment is I like to setup my zero page with ORG &00 and GUARD &A0 with EQUBs and/or SKIPs - so that way you ensure you don't use a location twice and it's tidier, and it really easy to add or remove variables. -PJ Attachment:
|
|
| Author: | jbnbeeb [ Sat May 05, 2012 5:08 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Thanks for the fix PJ! Much appreciated! The code I was using for the vsync + timer was SteveO's code from the sample pages here (Mode 2 sprite plotting). I think the logic behind it was it bought extra time to do your updates between screen refreshes. I'm probably using it wrong. Waiting for vsync (only, bypassing the custom IRQ) certainly makes animation smooth.. is there any way to get the snake to move faster and still be tied to vsync? I'm guessing not, short of moving in greater increments of pixels which would look a bit jerkier? And I presume that if I don't tie to vsync (or if I tied the game loop to say half vsync value).. I'd get tearing. I want to be able to make the Snake progressively faster at certain score thresholds. |
|
| Author: | jbnbeeb [ Tue May 08, 2012 3:24 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Sorry if I'm wearing people out with this thread. If anyone has any patience left to read through this post I'd be most grateful. PJ kindly took a look at my code and simplified it such that it simply waits for vsync to occur before calling the getkeys and move snake routines. This makes the snake move smoothly but appears to me moving as fast as it can go in increments of 2 pixels. I'd like to make it move faster as a game feature when the player reaches a given score. So with current code.. I can't understand why the snake isn't moving faster. I say this with following (possibly false) assumptions: a) Vsync = 50 frames per second. b) Move routine is called once vsync occurs c) Move routine moves snake in x direction by two pixels d) so my move routine is being called 50 times a second? If so - why isn't the snake moving at a rate of 100 pixels per second? I've also used Richard Talbot-Watkins's "coloured band" idea to see if move routine is eating into the time when screen is being updated. Most of the time it isn't, with no more than a char row's worth of coloured band appearing across the top of the screen very occasionally. |
|
| Author: | PitfallJ [ Tue May 08, 2012 5:13 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Actually if you run at 50Hz you can move any amount and it will be 'smooth' - because the screen update is fast enough to fool your eyes. Just call you move routine multiple times now and it runs super fast - like this: Code: jsr VSYNC_NEW jsr GetKeyPress lda #0 : ldx #0 : ldy #0 jsr Move lda #0 : ldx #0 : ldy #0 jsr Move The only problem is in order to not be jerky you have to move if the same amount every frame so you speeds are limited to 1 wide pixel, or 2 wide pixels, or 3 wide pixels etc. If you try moving it 1 pixel and then 2 pixels on alternative frames to get an in between speed then it would be jerky. So- you really have to plot things at sub-pixels within the byte (maybe you do this already?) to get in-between speeds. -PJ |
|
| Author: | jbnbeeb [ Tue May 08, 2012 7:32 pm ] |
| Post subject: | Re: Odd behaviour with a delay loop. Can anyone help? |
Ah - thanks PJ. I had thought of moving in greater pixel increments but I thought this wouldn't be as smooth. I still wonder why the initial movement in increments of 2 pixels (aka 1 wide pixel) isn't resulting in a rate of 100 pixels a second if screen update is 50 times a second? I don't currently do the sub-pixel idea though I will look to implement for a more gradual increase in snake movement rate between "normal" and "super-fast" Then I'll try and improve the graphics a little bit and look to wrap up the Snake game for now. Being a glutton for punishment, I want to have a go at doing Snake in Java for Android platform (so I'll have to learn Java |
|
| Page 1 of 1 | All times are UTC [ DST ] |
| Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |
|