Jump to content
IGNORED

Re-visiting Defender...


PacManPlus

Recommended Posts

 

 

Hey Guys:

 

41 minutes ago, Synthpopalooza said:

Can someone point me to wav recordings of the arcade sound fx please?  I also noticed that all sounds seem to share one channel.  This is good, as we can stack POKEY filters in as many different ways as needed, even using all 4 channels for one sound if we so choose. :)

Hey!  RevEng posted the sounds below...  They are identical to the arcade, and all the 'Presets' tell what each sound is.

 

On 1/1/2022 at 10:09 PM, RevEng said:

Speaking of Defender sound, I'll just leave this here...

 

http://www.dl.unospace.net/defender_sound/

 

  • Thanks 1
Link to comment
Share on other sites

35 minutes ago, Synthpopalooza said:

The key to this:

 

Set up two 16-bit channels in POKEY ...

Please again, I'm begging you - get with Fred and be sure HOKEY can handle your files when this eventual masterpiece reaches the Store. At this point, it is increasingly untenable to use a ~40 year old vintage IC for each game we buy. 

Link to comment
Share on other sites

Morning all!

 

Just a quick update: I change the clearing of the scanner area to just removing the 'blip' immediately before placing the new 'blip' on the screen.  While it did save a LOT of cycles, I'm still suffering from slow-down (and missing blips in the radar) when there are a lot of objects on the screen.

 

I did the '7800 heat' utility again, and there doesn't seem to be any 'problem' or 'out of control' areas...

 

This is really bugging me...  I KNOW this should work...

  • Like 4
Link to comment
Share on other sites

It would be nice if there was some way, perhaps even hidden, to change the palette's purple color to red. Purple swarmers just look odd to me.

 

Also, any chance of getting the "firework" explosions and warp-in effects? Even the ColecoVision and NES versions managed to squeeze those in.

  • Like 1
Link to comment
Share on other sites

6 hours ago, PacManPlus said:

I did the '7800 heat' utility again, and there doesn't seem to be any 'problem' or 'out of control' areas...

7800 heat is good for identifying frequently called small bits of code, but is less useful if the problem is larger chunks of code that aren't as frequently called. (i.e. 10 cycles of instructions looped 10 times will look "hotter" than 100 cycles of instructions called just once, even though the actual cycle cost is the same for both)

 

Some alternative profiling advice... If you haven't already, I'd see what's eating most of your time by commenting out all BACKGRND register hits, and then before each routine you want to profile, change the BACKGRND color to something unique, and on exit from that routine set BACKGRND back to black. When you play the game, the screen will be a realtime-graph of what the cpu hogs are, relative to each other *and* relative to the overall frame time.

 

The one weakness of this technique is that it can't identify routines that run during non-visible/vblank time. But since we usually reserve non-visible time for DL updating code, that's not often a problem.

 

Generally I do the above technique at least once during development, to see how close I am to blowing past the frame timing. For Salvo, it was a useful technique to balance what was happening on even and odd frames. (expensive stuff like collision checks and enemy AI were set to run on different frames, to keep the rest of the action humming at 60Hz)

 

  • Like 7
Link to comment
Share on other sites

Hello!

 

3 hours ago, ZylonBane said:

It would be nice if there was some way, perhaps even hidden, to change the palette's purple color to red. Purple swarmers just look odd to me.

 

Also, any chance of getting the "firework" explosions and warp-in effects? Even the ColecoVision and NES versions managed to squeeze those in.

I may change the entire purple to red, but that will make the bombers red as well...  which might not be a bad thing.  The highlights on the player's ship will also be red.

 

Those are on the 'nice to have' list.  Which is the list I usually leave until the end to see if it can be done.  I'm pretty sure I'll be able to do that with the player's ship upon explosion (I can sneakily remove all other objects from the screen when the explosion happens to get the maximum number of dots for the explosion), but the individual enemy explosions... :ponder: let me get past these few issues and I can tell better...

 

2 hours ago, RevEng said:

7800 heat is good for identifying frequently called small bits of code, but is less useful if the problem is larger chunks of code that aren't as frequently called. (i.e. 10 cycles of instructions looped 10 times will look "hotter" than 100 cycles of instructions called just once, even though the actual cycle cost is the same for both)

 

Some alternative profiling advice... If you haven't already, I'd see what's eating most of your time by commenting out all BACKGRND register hits, and then before each routine you want to profile, change the BACKGRND color to something unique, and on exit from that routine set BACKGRND back to black. When you play the game, the screen will be a realtime-graph of what the cpu hogs are, relative to each other *and* relative to the overall frame time.

 

The one weakness of this technique is that it can't identify routines that run during non-visible/vblank time. But since we usually reserve non-visible time for DL updating code, that's not often a problem.

 

Generally I do the above technique at least once during development, to see how close I am to blowing past the frame timing. For Salvo, it was a useful technique to balance what was happening on even and odd frames. (expensive stuff like collision checks and enemy AI were set to run on different frames, to keep the rest of the action humming at 60Hz)

 

I love that idea.  Thank you - I'll give that a try.

  • Like 4
Link to comment
Share on other sites

7 hours ago, RevEng said:

Some alternative profiling advice... If you haven't already, I'd see what's eating most of your time by commenting out all BACKGRND register hits, and then before each routine you want to profile, change the BACKGRND color to something unique, and on exit from that routine set BACKGRND back to black. When you play the game, the screen will be a realtime-graph of what the cpu hogs are, relative to each other *and* relative to the overall frame time.

 

So I did the above :) 

 

The area of code I did that in...

;	HANDLE_ENEMIES - PROCESS THE VARIOUS STATES OF THE ENEMIES.
HANDLE_ENEMIES
	LDA #$00
	STA NUM_SCREEN_OBJECTS
	TAX
HEN_LOOP
	LDA OBJTYPE,X							;SEE IF THIS OBJECT IS POPULATED
	BMI HEN_NEXT
	LDA #RED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	STA BACKGND
	JSR HANDLE_ENEMY_MOVEMENT					;MOVE/ANIMATE THE ENEMY FOR EACH TYPE (INCLUDING DYING AND SCORE)
	LDA #BLUE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	STA BACKGND
	JSR HANDLE_ENEMIES_ON_SCANNER					;UPDATE THE SCANNER
	LDA #GREEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	STA BACKGND
	JSR HANDLE_ENEMY_SCREEN						;IF THE ENEMY IS ON THE SCREEN, DO SCREEN THINGS.
	LDA #BROWN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	STA BACKGND
	JSR HANDLE_ENEMY_AGAINST_PLAYER_SHOT				;SEE IF ANY ENEMIES COLLIDED WITH THE PLAYER'S SHOT
	LDA #BLACK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	STA BACKGND
HEN_NEXT
	INX
	CPX #NUM_OBJECTS						;GO THROUGH ALL ENEMIES ON PLANET
	BMI HEN_LOOP
	RTS

 

Ummmm........ Wow

0010.png.feebae8f691f46c2745f44271d96c526.png

 

Edited by PacManPlus
  • Like 1
  • Haha 1
Link to comment
Share on other sites

:) yeah, if you're looping between all enemies, and coloring the different subs within that loop, there's going to be a lot of banding. You could always just do separate enemy loops for each sub too. (which might also help if you want to split some of these subs across frames.)

 

That said, it looks like blue - the radar code - is your biggest cpu hog. (also, you're definitely over a frame). Breaking that one across multiple frames, or otherwise making it more efficient, will probably give you back a lot of cpu.

 

The big green bar at the bottom is likely due to an NMI interrupting the on-screen enemy handling, rather than that particular routine taking extra long for one of the enemies. That also might be the case for the biggest blue bar near the top too.

  • Like 2
Link to comment
Share on other sites

Hi all:

 

So, can anyone help reduce the CPU time of this routine please? It's the big CPU time hog to do for at most 42 objects on the planet (32 enemies, 10 humanoids)

 

The routine is what gets an object's vertical and horizontal position relative to the planet (e.g. not the screen), and reduces it to the scanner x and y position.

Some background:

 

Planet: 1024 pixels horizontally (arcade Defender had 2048, but we move in 2 pixel increments).  I had originally created a single pixel offset graphics set to use, but that quickly became a major PITA.

           128 COLUMNS HORIZONTALLY

Scanner: 64 Pixels horizontally, split into 16 bytes.  Each byte has 4 places where a 'blip' can go: XX000000, 00XX0000, 0000XX00, or 000000XX

 

The routine:

 

;	PUT ENEMY ON SCANNER
	LDA OBJVPOS,X							;TAKE VERTICAL POSITION
	LSR								;DIVIDE BY 2
	TAY								;MAP FROM VERTICAL POSITION ON SCREEN TO VERTICAL SCANNER POS
	LDA VPOS_TO_SCANNERV_TABLE,Y					;VERTICAL POSITION IN SCANNER
	TAY
	LDA SCNADDRESSL,Y						;INDEX INTO SCANNER RAM, VERTICALLY
	STA TEMP0
	LDA SCNADDRESSH,Y
	STA TEMP1
	LDA OBJHCOL,X							;GET HORISONTAL PLANET COLUMN OF ENEMY ($00-$7F)
	CLC								;WE NEED TO GET A STATIC COLUMN IN THE SCANNER BETWEEN THE PLANET AND THE ENEMY/HUMANOID
	ADC #$35							;I FEEL LIKE THIS SHOULD BE #$40, BUT $35 IS WHAT GIVES AN ACCURATE NUMBER
	SEC
	SBC WINDOWSTARTCOL						;SUBTRACT CURRENT WINDOW COLUMN TO GET COLUMN FOR SCANNER
	LSR								;DIVIDE BY 2 TO GET THE NUMBER OF X POSITIONS IN THE SCANNER
	AND #$3F							;FOR WRAPAROUND
	STA TEMP5							;64 PIXELS IN SCANNER ($00-$3F)
	LSR								;WE NOW DIVIDE BY 4 TO GET THE BYTE INDEX INTO THE SCANNER RAM
	LSR								;THERE ARE 16 BYTES PER ROW OF SCANNER RAM
	STA TEMP4							;HORIZONTAL POSITION BYTE IN SCANNER
	LDA TEMP5
	AND #$03							;GET POSITION WITHIN BYTE IN SCANNER
	STA TEMP5
	LDA OBJTYPE,X							;GET ENEMY TYPE
	ASL								;SHIFT OVER TO CREATE AN INDEX 
	ASL								; WITHIN THE TABLE OF OBJECT TYPE / BYTE POSITION
	ORA TEMP5							;'OR' WITH DOUBLE-BIT POSITION
	TAY								;Y INDEX
	LDA NMYSCNLINE1,Y						;THIS GETS THE ENEMY TYPE AND POSITION WITHIN THE SCANNER BYTE
	STA TEMP5
	LDY TEMP4							;HORIZONTAL BYTE POSITION IN SCANNER RAM
	LDA (TEMP0),Y							;GET WHAT IS ALREADY THERE
	ORA TEMP5							;'OR' VALUE WITH NEW OBJECT
	STA (TEMP0),Y							;STORE TOP LINE
	INC TEMP1							;NEXT PAGE DOWN
	LDA (TEMP0),Y							;GET WHAT WAS ALREADY THERE
	ORA TEMP5							;'OR' VALUES WITH NEW OBJECT
	STA (TEMP0),Y

 

While it works, we've seen how much time it takes.  If I can reduce the CPU usage of this routine, we should be good. ?

 

Thanks,

Bob

Edited by PacManPlus
  • Like 5
  • Confused 1
Link to comment
Share on other sites

Hi guys:

 

I'm afraid I have bad news.

I am unable to get this working any faster, and it's not even at the point where the maximum amount of enemies are on the planet.  So, for the second time I'm going to have to put this down permanently. 

 

Thank you for the help and feedback, but I think it's time to move on to another project. :(

Bob

  • Sad 6
Link to comment
Share on other sites

[edit - just saw your last reply as I was in the process of editing+posting this, Bob. Figured I might as well go ahead with the post anyway, so the info is here in case you ever want to pick it up.]

 

On 1/9/2022 at 3:10 PM, PacManPlus said:

So, can anyone help reduce the CPU time of this routine please? It's the big CPU time hog to do for at most 42 objects on the planet (32 enemies, 10 humanoids)



 

While it works, we've seen how much time it takes.  If I can reduce the CPU usage of this routine, we should be good. ?

 

Here's a first-pass at a cycle-bummed version. Assuming I haven't goofed with any of my assumptions, it should save 26 cycles per object, which unfortunately only gets you down to ~75% of the original cycles. If you haven't already, you should also avoid the JSR+RTS pair per object, per Pat Brady's previous post.

 

I've used some undocumented opcodes, but only ones that are known stable, and have been in use with the 7800 for a while. 

;    PUT ENEMY ON SCANNER
    LDA OBJVPOS,X                            ;TAKE VERTICAL POSITION
    ;LSR                                ;DIVIDE BY 2
    ASR #$FE ; *** AND+LSR, so we can force carry clear
    TAY                                ;MAP FROM VERTICAL POSITION ON SCREEN TO VERTICAL SCANNER POS
    ; *** -0 cycles / -0 cycles total


    ; *** with a bit of expansion we can combine both of these table lookups...
    ;LDA VPOS_TO_SCANNERV_TABLE,Y                    ;VERTICAL POSITION IN SCANNER
    ;TAY
    ;LDA SCNADDRESSL,Y                        ;INDEX INTO SCANNER RAM, VERTICALLY
    ;STA TEMP0
    ;LDA SCNADDRESSH,Y
    ;STA TEMP1
    ; *** somthing like this...
    LDA VPOS_TO_SCANNER_ADDRESSL,Y
    STA TEMP0
    LDA VPOS_TO_SCANNER_ADDRESSH,Y
    STA TEMP1
    ; *** -6 cycles / -6 cycles total


    ; *** do this once, prior to the radar enemy loop...
        ;   LDA WINDOWSTARTCOL
        ;   SEC
        ;   SBC #$36 ; $36 instead of $35, due to later carry=clear
        ;   STA WINDOWSTARTCOL_ADJUSTED
    ; *** so we can streamline this...
    ;LDA OBJHCOL,X                            ;GET HORISONTAL PLANET COLUMN OF ENEMY ($00-$7F)
    ;CLC                                ;WE NEED TO GET A STATIC COLUMN IN THE SCANNER BETWEEN THE PLANET AND THE ENEMY/HUMANOID
    ;ADC #$35                            ;I FEEL LIKE THIS SHOULD BE #$40, BUT $35 IS WHAT GIVES AN ACCURATE NUMBER
    ;SEC
    ;SBC WINDOWSTARTCOL                        ;SUBTRACT CURRENT WINDOW COLUMN TO GET COLUMN FOR SCANNER
    ; *** to this...
    LDA OBJHCOL,X                            ;GET HORISONTAL PLANET COLUMN OF ENEMY ($00-$7F)
    SBC WINDOWSTARTCOL_ADJUSTED 
    ; *** -6 cycles / -12 cycles total

    ;LSR                                ;DIVIDE BY 2 TO GET THE NUMBER OF X POSITIONS IN THE SCANNER
    ;AND #$3F                            ;FOR WRAPAROUND
    ASR #$7F ; *** AND+LSR
    ; *** -2 cycles / -14 cycles total

    ;STA TEMP5                            ;64 PIXELS IN SCANNER ($00-$3F)
    TAY ; *** save
    LSR                                ;WE NOW DIVIDE BY 4 TO GET THE BYTE INDEX INTO THE SCANNER RAM
    LSR                                ;THERE ARE 16 BYTES PER ROW OF SCANNER RAM
    STA TEMP4                            ;HORIZONTAL POSITION BYTE IN SCANNER
    ;LDA TEMP5
    TYA ; *** restore
    ; *** -2 cycles / -16 cycles total


    AND #$03                            ;GET POSITION WITHIN BYTE IN SCANNER
    ; *** TEMP5 is about to be wiped after this, so just keep the value in A
    ; STA TEMP5 
    ;LDA OBJTYPE,X                            ;GET ENEMY TYPE
    ;ASL                                ;SHIFT OVER TO CREATE AN INDEX 
    ;ASL                                ; WITHIN THE TABLE OF OBJECT TYPE / BYTE POSITION
    ;ORA TEMP5                            ;'OR' WITH DOUBLE-BIT POSITION
    ORA OBJTYPEX4,X    ; *** OBJTYPE*4 can be pre-calculated/statically-assigned
    TAY                                ;Y INDEX
    ; *** -10 cycles / -26 cycles total


    LDA NMYSCNLINE1,Y                        ;THIS GETS THE ENEMY TYPE AND POSITION WITHIN THE SCANNER BYTE
    STA TEMP5
    LDY TEMP4                            ;HORIZONTAL BYTE POSITION IN SCANNER RAM
    LDA (TEMP0),Y                            ;GET WHAT IS ALREADY THERE
    ORA TEMP5                            ;'OR' VALUE WITH NEW OBJECT
    STA (TEMP0),Y                            ;STORE TOP LINE
    INC TEMP1                            ;NEXT PAGE DOWN
    LDA (TEMP0),Y                            ;GET WHAT WAS ALREADY THERE
    ORA TEMP5                            ;'OR' VALUES WITH NEW OBJECT
    STA (TEMP0),Y

 

I think to do much better you'll need something more algorithmic, like caching
 

  • Like 2
Link to comment
Share on other sites

Seems like splitting the scanner processing across multiple frames would be a viable option.

 

As RevEng already pointed out, you're doing a lot of calculations for each enemy that could be pre-calculated.

 

One of the reasons those old Eugene Jarvis games ran so fast on such primitive hardware is because he wasn't shy about just skipping or delaying any non-critical tasks that would have impacted the frame rate. Too many enemies on the screen? Move 'em! Too much AI processing? Do it later! So no shame in using the same tricks for a port.

  • Like 6
Link to comment
Share on other sites

39 minutes ago, ZylonBane said:

Seems like splitting the scanner processing across multiple frames would be a viable option.

 

As RevEng already pointed out, you're doing a lot of calculations for each enemy that could be pre-calculated.

 

One of the reasons those old Eugene Jarvis games ran so fast on such primitive hardware is because he wasn't shy about just skipping or delaying any non-critical tasks that would have impacted the frame rate. Too many enemies on the screen? Move 'em! Too much AI processing? Do it later! So no shame in using the same tricks for a port.

 

Excellent point, in case Bob's decision is not final.

 

In this case, distributing that function across 2 frames (i.e. update half of the objects each frame) combined with @RevEng's optimizations should save about 3000 cycles per frame.

 

For slow moving objects it would probably be fine to update even less often, maybe every 4 frames for slow-moving enemies and 5 frames for humanoids (since there are 10 of them). Alternatively, you could update humanoids only when they move, though that might not help the worst case.

  • Like 2
Link to comment
Share on other sites

13 hours ago, Zonie said:

When I play Defender/Stargate, I rarely if ever look at the scanner. Too much going on in the playfield to take your eyes off of it...

How about skipping the scanner?

Oh sure, throw out one of the most distinctive aspects of the original game, which is present in literally every port. Nobody would mind that.

 

Jesus, some people really don't think before they post.

  • Haha 3
Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...