Jump to content



1

Too many cycles


10 replies to this topic

#1 Propane13 ONLINE  

Propane13

    Stargunner

  • 1,386 posts
  • Location:Philly

Posted Fri Dec 16, 2011 7:07 AM

Hello!

So, I was working on a project, and then a situation hit me that I think could be a real tripping hazard for 7800 programmers.
If I remember right, if you have more cycles of calculations than the space between VBlank starting and MARIA's next screen drawing routine, then on real hardware, you'll have some issues. I think (correct me if I'm wrong) that MARIA will go ahead and start drawing the next frame, interrupting whatever calculation you're on, but when she returns, you may have some register corruption. I think I remember running into something like this a few years ago.

Since this seems to be a valid situation that emulation does not trap (maybe it indicates that an issue is present with some subtle graphics glitches), I was wondering:
1) How many cycles are there between when VBLank starts (let's assume a worst-case scenario, when MARIA's last DLL is full of data), and when MARIA starts drawing the next frame? Is that an easy number to derive or estimate?
2) Is there any way in emulation or with a tool to see how many cycles some code is using in order to make sure you end up under that threshold?
What would be perfect is if I could press a button in prosystem and have it give a count of the cycles between MSTAT reads/writes/BIT commands. If it was over the cycle threshold in 1, put the color in red, if under, put it in green.

I know the 7800 community is limited, but I wonder how other programmers deal with this issue.
Optimization is the solution, sure, but how do you know when you've surpassed the threshold, or how close you are?
I guess the other possibility is to disable MARIA drawing when calculations take longer than expected, but I'm thinking that's more of a workaround than a correct solution.

Thoughts?
-John

#2 GroovyBee OFFLINE  

GroovyBee

    7800 Developer

  • 5,782 posts
  • Busy bee!
  • Location:North, England

Posted Fri Dec 16, 2011 8:31 AM

I'd be interested in seeing an example where MARIA corrupts what you are doing on the real hardware.

#3 Rybags OFFLINE  

Rybags

    Quadrunner

  • 10,314 posts
  • Location:Australia

Posted Fri Dec 16, 2011 8:37 AM

Maria doesn't corrupt anything the CPU is doing, it just steals cycles which is transparent but can affect anything time-critical.

There's 114 CPU cycles per scanline, but that drops if Maria is doing DMA or you want to access TIA or RIOT.

There's a maximum of 242 "active" scanlines NTSC or 292 with PAL from a total of 262 or 312.

Generally the "vital stuff" that must be done before active display starts is things like setting pointers or changing colour registers. So the trick is to do that kind of thing early in VBlank processing.

In theory you could use a DLI near the bottom of your screen and start VBlank processing early to gain more cycles. Of course the trick there would be to not do anything that would cause screen glitches while the display's still active.

Another thing is to try and keep the VBlank processing tight - if something can execute in the mainline code then put it there. Sometimes the only reason for doing something at a particular time is because some variables might need to be tested before being updated, e.g. object positions. But an alternative method could be to make copies of certain data before updating it, then do that processing later.

#4 Propane13 ONLINE  

Propane13

    Stargunner

  • 1,386 posts
  • Location:Philly

Posted Fri Dec 16, 2011 9:33 AM

Whoa, wait a sec-- that is an interesting idea for a trick.
So, are you suggesting that when you start drawing the "blank lines" of the screen (i.e. overscan), you can do the following:
- disable MARIA output
- adjust the stack, since you ain't coming back from this DLI
- start your calculations

And then, when you're complete, wait for vblank, and then re-enable MARIA?

That's a slick idea. But, would it work? I wonder if anyone's tried it.

-John

#5 PacManPlus OFFLINE  

PacManPlus

    River Patroller

  • 3,320 posts
  • Location:Naples, Florida

Posted Fri Dec 16, 2011 9:58 AM

Taking an example from the Ms. Pac-Man source code, I do something very similar in all of my games.
I mark the last zone (the blank lines) in a display list with a DLI, and start doing sound processing, sprite positioning, and sometimes other things during VBLANK.

Using the example from Meteor Shower:
;  THIS IS THE DISPLAY LIST LIST.  THIS WILL BE DROPPED INTO RAM.
DLLIST
;PAL
;PAL	 .byte    $0F,>(NULDLST),<(NULDLST)			    ;25 ADDITIONAL BLANK LINES FOR PAL
;PAL	 .byte    $08,>(NULDLST),<(NULDLST)
;PAL
		 .byte    $0F,>(NULDLST),<(NULDLST)			    ;16 BLANK LINES
		 .byte    $04,>(NULDLST),<(NULDLST)			    ;5 BLANK LINES

		 .byte    $C7,>(SCDLIST),<(SCDLIST)			    ;SCORE ZONE -  8 LINES - WITH INTERRUPT
		 .byte    $4F,>(DLIST00),<(DLIST00)			    ;PF ZONE 00 - 16 LINES
		 .byte    $4F,>(DLIST01),<(DLIST01)			    ;PF ZONE 01 - 16 LINES
		 .byte    $4F,>(DLIST02),<(DLIST02)			    ;PF ZONE 02 - 16 LINES
		 .byte    $4F,>(DLIST03),<(DLIST03)			    ;PF ZONE 03 - 16 LINES
		 .byte    $4F,>(DLIST04),<(DLIST04)			    ;PF ZONE 04 - 16 LINES
		 .byte    $4F,>(DLIST05),<(DLIST05)			    ;PF ZONE 05 - 16 LINES
		 .byte    $4F,>(DLIST06),<(DLIST06)			    ;PF ZONE 06 - 16 LINES
		 .byte    $4F,>(DLIST07),<(DLIST07)			    ;PF ZONE 07 - 16 LINES
		 .byte    $4F,>(DLIST08),<(DLIST08)			    ;PF ZONE 08 - 16 LINES
		 .byte    $4F,>(DLIST09),<(DLIST09)			    ;PF ZONE 09 - 16 LINES
		 .byte    $4F,>(DLIST10),<(DLIST10)			    ;PF ZONE 10 - 16 LINES
		 .byte    $4F,>(DLIST11),<(DLIST11)			    ;PF ZONE 11 - 16 LINES
		 .byte    $8F,>(NULDLST),<(NULDLST)			    ;16 BLANK LINES - THIS IS THE INTERRUPT THAT STARTS VBLANK PROCESSING
		 .byte    $05,>(NULDLST),<(NULDLST)			    ;6 BLANK LINES
;PAL
;PAL	 .byte    $0F,>(NULDLST),<(NULDLST)			    ;25 ADDITIONAL BLANK LINES FOR PAL
;PAL	 .byte    $08,>(NULDLST),<(NULDLST)
;PAL
		 .byte    $00,$00,$00
DLLEND
DLSTLNG   EQU	 DLISTEND-DLIST
DLLLNG    EQU	 DLLEND-DLLIST

Then, in the DLI code:
;  KERNAL - MAINTAIN THE ON-SCREEN DISPLAY
DLI	  
		  PHA						    ;STACK REGISTERS
		  TXA
		  PHA
		  TYA
		  PHA
		  CLD
		  LDA	 DLIFLG			    ;MARKS WHICH DLI WE ARE AT ($00 = TOP, $01 = BOTTOM
		  BEQ	 DLITOP
		  JMP	 DLIBOTTOM
DLITOP
		  LDA	 #$01				    ;FOR NOW, WE DO NOTHING AT THE TOP DLI BUT UPDATE THE FLAG
		  STA	 DLIFLG
		  JMP	 DLIOUT			 
;  THIS ROUTINE TAKES CARE OF THE LAST DLI ON THE SCREEN, THE 'VERTICAL BLANK
;  ROUTINE'.
DLIBOTTOM
		  LDA	 #$00
		  STA	 DLIFLG				 ;RESET FOR TOP DLI
		  INC	 RTLOCAL+1			  ;INCREMENT CLOCK
		  BNE	 DLICONT
		  INC	 RTLOCAL
DLICONT
		  JSR	 LOADER				 ;LOAD UP NEXT FRAME (UPDATE SPRITE POSITIONS)
DLIWAIT
		  BIT	 MSTAT				  ;WAIT FOR VBLANK
		  BPL	 DLIWAIT
DLIOTHER
		  JSR	 TUNER				  ;DO TUNES
		  JSR	 RAND				   ;UPDATE RANDOM NUMBER
DLIOUT
		  PLA						    ;UNSTACK AND LEAVE
		  TAY
		  PLA						    ;THIS IS WHERE MOST DLI'S LEAVE
		  TAX
		  PLA
NULLRTI
		  RTI


Does this help at all?
Bob

#6 Propane13 ONLINE  

Propane13

    Stargunner

  • 1,386 posts
  • Location:Philly

Posted Fri Dec 16, 2011 12:47 PM

Interesting.

So, what I'm wondering is... if your DLI doesn't complete before your subroutines, what happens?
Does MARIA come back and interrupt the interrupt, or do you potentially have a screen drawn with too many lines (waiting for the DLI to finish)?

#7 Rybags OFFLINE  

Rybags

    Quadrunner

  • 10,314 posts
  • Location:Australia

Posted Fri Dec 16, 2011 6:11 PM

Why would you have too many lines? You control what's onscreen and when the display terminates via the sets of DLLs.

Having an interrupt and not returning isn't really a good idea, in theory the whole program could be done that way but it could become a nightmare to debug. If the int ever happened before all the previous required processing was complete then you could get data corruption.

Bob's idea is another good one - you can just set a flag that means "OK, the display is about to finish" which the main program can check, then clear the flag once you begin VBlank processing.

If you're worried about an interrupt taking way too long, you can also use a flag such that if another interrupt occurs, it will know that it's interrupted the previous interrupt, ie test and set the flag on entry, clear it just before exit.

A good technique to see how long stuff is taking is to use colour bars. Just alternating the background between 2 similar luma values is usually enough.

#8 kenfused OFFLINE  

kenfused

    Stargunner

  • 1,192 posts
  • Location:Columbus, Ohio

Posted Fri Dec 16, 2011 7:19 PM

Everything I have done i have put a dli on the last line and essentially use it like a vbi on the 8-bit computers. the vbi builds/updates the dl's and dll's, plays sounds, etc. Game logic is done outside of interrupts. If anything needs syncd it can always be done with a timer updated in the vbi that you can wait for to increment. It obviously has to finish building the dlist structures before the Maria starts drawing the next frame. You can even start your vbi a little earlier if you don't need dli's at the bottom of the screen and Maria doesn't catch up to you but I have never had any problems completing building what I need to in the vbi.

There was one of the 7800 games (ms pacman maybe) that had two dlist buffers, and had a dli for each zone build the dli for the next zone and alternated between the two buffers. The calcs were done in the vbi i think, but the actual building was done in a dli. I often wonder if that was more a quick an dirty conversion from Maria1 to Maria2 although it probably saved a little RAM.

#9 EricBall OFFLINE  

EricBall

    Dragonstomper

  • 711 posts
  • Location:Markham, Ontario, Canada

Posted Mon Dec 19, 2011 11:06 AM

I know that in the past emulators didn't track how many cycles MARIA used, I don't know whether current emulators track CPU cycles versus MARIA cycles. The only way MARIA could corrupt registers is via a DLI which doesn't properly preserve them.

I remember in SpaceWar! 7800 I had a DLI in the first inactive zone which would just set a flag then RTI. The main code would loop on that flag instead of MSTAT. I guess you could do away with that and pop the SR & PC off the stack and make the ISR the start of the routine. Wouldn't be a bad idea to have the ISR check a flag to see if the previous frame had completed first.

https://sites.google.../atari7800wiki/ has some info on cycle counts and DLIs.

#10 Propane13 ONLINE  

Propane13

    Stargunner

  • 1,386 posts
  • Location:Philly

Posted Sun Jan 29, 2012 8:32 AM

Ok, just to close out this thread-- there was an issue with too many cycles, but that wasn't my main issue.
The main corruption issue seems to have stemmed from not initializing RAM on the 7800 properly.

I'm guessing that emulation sometimes defaults certain memory areas to $00, whereas in a real hardware situation, such RAM locations could have any random value on power-up.

#11 PacManPlus OFFLINE  

PacManPlus

    River Patroller

  • 3,320 posts
  • Location:Naples, Florida

Posted Sun Jan 29, 2012 9:01 AM

Yes, you are correct. :)




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users