Jump to content

The Southsider

  • entries
    81
  • comments
    767
  • views
    138,545

Flashing Cockpits


cd-w

1,678 views

There is a discussion currently in the homebrew forum about adding a flashing cockpit to the Juno First ship. One approach would be to use the ball sprite overlaying the player sprite. The ball sprite is used for the laser and is already correctly positioned (horizontally), but I can't find the spare cycles to enable and disable it at the correct time. The main loop where the ship is drawn is a 1LK as follows (the actual code is in kernel1.h in the zip attached to the previous entry):

 

A_ShipLoop
; Calculate Pointer to Alien Sprite Data (aliens are 8 pixels high)
 tya					; [45] + 2
 sbc ALIENY			 ; [47] + 3
 bcs A_NoDraw		; [50] + 2/3
 adc #8				 ; [52] + 2
 bcc A_NewSprite		; [54] + 2/3
 adc TYPE			; [56] + 3
 tax					; [59] + 2
 
; Preload Ship Data
 lda (SPTR),Y		; [61] + 5
 sta GRP0			; [66] + 3	VDEL'ED
  
; Draw Alien & Ship
 lda A_Aliens,X		 ; [69] + 4
 sta GRP1			; [73] + 3	> 75
 lda A_AlienCols,X; [0] + 4
 sta COLUP1			 ; [4] + 3	 > 75 < 23

; Set Ship Colour
 lda (CPTR),Y		; [7] + 5
 sta COLUP0			 ; [12] + 3	< 23
 
; Draw Bullets (using PHP stack trick)
 tya					; [15] + 2
 lsr					; [17] + 2
 cmp B2				 ; [19] + 3
 php					; [22] + 3
 cmp B1				 ; [25] + 3
 php					; [28] + 3 = 16

; Reset Stack Pointer
 ldx #ENAM1			 ; [31] + 2
 txs					; [33] + 2

; Check End of Kernel
 dey					; [35] + 2
 bmi A_EndShipKernel	; [37] + 2/3
 
; Check If Grid Line Should Be Drawn
 cpy LINE			; [39] + 3
 bcs A_ShipLoop		 ; [42] + 2/3
; ... Draw Grid Line, Alien & Ship and Return to Loop

 

The y register holds the line count, and there are variants of this loop for drawing background lines, calculating a pointer to the next alien, and repositioning the alien sprite. In these variants the bullets are not drawn, which frees up the necessary extra cycles.

 

To get a flashing cockpit, the ball sprite needs to be enabled and disabled at the correct time. One approach would be to use the lowest bit of the colour data, but would require 5 extra cycles as follows:

 

  lda (CPTR),Y
 sta COLUP0
 asl
 sta ENABL

 

Unfortunately I can't see any way to accomplish this. Unrolling the loop is not really possible as this would require 40 copies of this code (and all of the variants for repositioning etc.). I was wondering if any of the optimisation wizards around here could spot any way to fit this in. If not, it doesn't matter too much as this feature is not exactly essential to the game!

 

Chris

12 Comments


Recommended Comments

If you unrolled the loop once (i.e., draw two lines before looping back) then you could probably do it, since you would only have to draw the bullets once per loop plus you wouldn't have to LSR the scanline counter for your compares.

 

Let's see...

You would save 4 cycles per scanline by changing this:

  tya					; [15] + 2
 lsr					; [17] + 2
 cmp B2				 ; [19] + 3
 php					; [22] + 3
 cmp B1				 ; [25] + 3
 php					; [28] + 3 = 16

to this:

  cpy B2				 ; [19] + 3
 php					; [22] + 3
 cpy B1				 ; [25] + 3
 php					; [28] + 3 = 12

You would save 12+4=16 cycles per 2 scanlines since you only have to draw the bullets every other scanline.

There are probably other savings you could get.

 

The tricky part would be entering and exiting from the partially-unrolled loop, so you might have to give some of those savings back.

Link to comment

I see you had another way in mind for the cockpit but if that way prove impossible or not worth the effort, why not alternatively have the ship sprite redesigned to accomodate similar results as in the sample below?

 

It's probably not up to your standards but I feel it works better than what you now have in place.

 

msg-7623-1212128044.png

 

Chris, -I remember you commented to me earlier that you think the ship's front look too separated in the new design.

I don't think it look bad.

It certainly do look alot closer to it's arcade counter-part.

 

Why don't you give it a try in your next build and get the play tester's input on this?

Or at least ask the community openly about the new version?

 

It don't surprize me that Nathan won't comment on this, but if this is about making his work exclusive just because he put in alot of effort don't make sense.

 

I'll say Nathan did a great job on the enemies, but honestly, his player ship don't even capture the colors of the arcade version.

If Nathan can't pull it off and someone else has, he should be gratful for the game's sake and not of himself.

Nathan does great work, but nobody (including you and myself) have all the solutions to everything.

Remember: Nathan couldn't do a snowmobile sprite to save his life. I'm not trying to be cynical, I'm just backing my point in stating a fact.

 

@ Nathan, as you once told me long ago, I think you should welcome any help, graphicwise, that can be extended to making this game look and play as close as possible to the real thing if it can be helped at all. It's all about contributing to the hobby.

Link to comment
Then you could save 2 cylces (ldx #ENAM1) more than if you setup the stack pointer directly before and after one of the php blocks.

 

The code presently uses 20 cycles per line to handle both missiles with two-line resolution. As noted, unrolling the loop 2x would cut that time to 16 cycles every two lines. If you keep running Y on a per-line basis and check for exit on every line, I wouldn't think the unrolling should pose any difficulty; even if the extension of branches past +/-127 bytes means you need some extra JMPs that wouldn't otherwise be necessary, the cycle savings would more than make up for it.

 

Another option might be to do something like (assume carry is known to be clear):

  pla
 bpl nope
 sta ENAM0
 adc #1  ; Toggle bit 1 if bit 0 is set (will leave carry clear, since acc. is in range $80-$83)
 sta.w ENAM1
; 15 cycles
back_in:
 ... rest of loop

nope:; 7 cycles so far
 adc #4
 pha
 bcc back_in; Branch always

This variation uses up a bit of RAM that you may not have, but it avoids using the X register. Code execution time is constant at 15 cycles.

 

The top item on the stack should be 128-4*the number of scan lines before the next time ENAM0/ENAM1 changes, plus 0-3 based upon the next values of ENAM0/ENAM1. Worst-case stack usage would be 5 bytes, plus one byte for each 32 scan lines.

Link to comment

espire8,

 

I'm not going to be pushed into making any changes to my game that I feel are inappropriate. Also, please avoid personal attacks on other forum members - everyone is just trying to make the game better and is entitled to their opinion.

 

There are two problems with the flashing cockpit idea that you have proposed:

 

  1. I have previously stated that I think it makes the front of the ship look detached, which I don't like. I'm investigating an idea here that would avoid this problem. If this can be made to work then I will include the flashing cockpit, otherwise not.
  2. The coloring that you have produces does match the arcade version more closely. However, it currently makes the ship look as if it is pointing directly upwards. My version of Juno First is viewed slightly from behind, rather than the overhead arcade view, so the ship needs to be shaded darker towards the front (as in the current ship). If you can produce a coloring that fixes this problem (without the flashing cockpit bit) then I will use it.

 

Chris

Link to comment

This kernel is only as tall as the ship, right? Can you fit the alien graphics in a single page, padded with zeros, such that you could replace this:

  tya					; [45] + 2
 sbc ALIENY			 ; [47] + 3
 bcs A_NoDraw		   ; [50] + 2/3
 adc #8				 ; [52] + 2
 bcc A_NewSprite		; [54] + 2/3
 adc TYPE			   ; [56] + 3
 tax					; [59] + 2

 

With:

  tya
 adc var (=8+TYPE-ALIENY, precalculated)
 tax

 

That will save 9 cycles per line. If you need to use two pages for graphics, e.g. for two animation frames, you might have the cycles to fit that in. If not, you may need two separate copies of the kernel.

Link to comment

Another thought: If you can spare 4 bytes of RAM, you can save even more than 9 per scanline, and wouldn't need to fit all graphics on one page. This:

  tya				; [45] + 2
 sbc ALIENY			; [47] + 3
 bcs A_NoDraw		  ; [50] + 2/3
 adc #8				; [52] + 2
 bcc A_NewSprite	; [54] + 2/3
 adc TYPE			  ; [56] + 3
 tax				; [59] + 2
 
; Preload Ship Data
 lda (SPTR),Y		  ; [61] + 5
 sta GRP0			  ; [66] + 3	VDEL'ED
  
; Draw Alien & Ship
 lda A_Aliens,X		; [69] + 4
 sta GRP1			  ; [73] + 3	> 75
 lda A_AlienCols,X  ; [0] + 4
 sta COLUP1			; [4] + 3	 > 75 < 23

becomes this:

				; [59] + 2
 
; Preload Ship Data
 lda (SPTR),Y		  ; [61] + 5
 sta GRP0			  ; [66] + 3	VDEL'ED
  
; Draw Alien & Ship
 lda (Aliens),y		; [69] + 4
 sta GRP1			  ; [73] + 3	> 75
 lda (AlienCols),y  ; [0] + 4
 sta COLUP1			; [4] + 3	 > 75 < 23

Also, you can preload X with #ENAM1. Total savings: 16 cycles per scanline.

Link to comment

Here is my revised 3rd idea. Would this work? The idea is to interleave ship graphics and color, and also alien graphics and color, using indirect indexing for each, but only one pointer each so no extra RAM is used. The same idea applies above with the alien graphics being padded with zeros, but since the color and graphics are interleaved, it might use less space.

 

A_ShipLoop
; Preload Ship Data
 lda (SPTR),Y		  ; [61] + 5
 sta GRP0			  ; [66] + 3	VDEL'ED
 dec SPTR; interleaved color (pointer reused below)
  
; Draw Alien & Ship
 lda (Aliens),y		; [69] + 4
 sta GRP1			  ; [73] + 3	> 75
 dec Aliens; interleaved alien GFX & color
 lda (Aliens),y  ; [0] + 4
 sta COLUP1			; [4] + 3	 > 75 < 23

; Set Ship Colour
 lda (SPTR),Y		  ; [7] + 5
 sta COLUP0			; [12] + 3	< 23
 asl
 sta.w ENABL  

; Draw Bullets (using PHP stack trick)
 tya				; [15] + 2
 lsr				; [17] + 2
 cmp B2				; [19] + 3
 php				; [22] + 3
 cmp B1				; [25] + 3
 php				; [28] + 3 = 16

; Reset Stack Pointer
;;;; X may be preloaded:  (ldx #ENAM1 not needed)		; [31] + 2
 txs				; [33] + 2

; Check End of Kernel
 dey				; [35] + 2
 bmi A_EndShipKernel; [37] + 2/3
 
; Check If Grid Line Should Be Drawn
 cpy LINE			  ; [39] + 3
 bcs A_ShipLoop		; [42] + 2/3
; ... Draw Grid Line, Alien & Ship and Return to Loop

Link to comment

Thanks for the suggestions. This would work if we were displaying just one alien sprite. However, there may be several alien sprites in this region, so we need to check when we are finished drawing the current alien and load the information for the next alien. The A_NewSprite bit (not shown) loads ALIENY and TYPE with the next values and does in-line sprite repositioning.

 

Chris

 

This kernel is only as tall as the ship, right? Can you fit the alien graphics in a single page, padded with zeros, such that you could replace this:

  tya				; [45] + 2
 sbc ALIENY			; [47] + 3
 bcs A_NoDraw		  ; [50] + 2/3
 adc #8				; [52] + 2
 bcc A_NewSprite	; [54] + 2/3
 adc TYPE			  ; [56] + 3
 tax				; [59] + 2

 

With:

  tya
 adc var (=8+TYPE-ALIENY, precalculated)
 tax

 

That will save 9 cycles per line. If you need to use two pages for graphics, e.g. for two animation frames, you might have the cycles to fit that in. If not, you may need two separate copies of the kernel.

Link to comment
Thanks for the suggestions. This would work if we were displaying just one alien sprite. However, there may be several alien sprites in this region, so we need to check when we are finished drawing the current alien and load the information for the next alien. The A_NewSprite bit (not shown) loads ALIENY and TYPE with the next values and does in-line sprite repositioning.

 

Chris

Makes sense. I played the game and only ever saw one alien in the ship area, so I made this assumption. However, the check for extra aliens is only 5 cycles by my count, so it might fit somewhere. E.g.:

cpy alienbottom

bcc A_NewSprite

Link to comment

Thanks for the suggestions - I got it to work using by using the unrolling trick (see test binary attached to the parent). My solution uses two version of the loop: one which displays the bullets, and one which displays the cockpit. These two versions are used on alternate scanlines, which works since the bullets only need to be displayed every second scanline. I haven't actually made the cockpit flash yet, but this should be easy enough to implement.

 

Unfortunately, there is another problem that I hadn't properly considered. When the laser beam is fired, the X position of the ship is stored and used until the laser beam is finished. This means that the laser beam doesn't line up properly with the cockpit when the ship is moving and firing. In the test binary I have forced the laser to follow the ship, but this doesn't "feel" right in the game (compare wave 1 with wave 2). I need to figure out some way to nudge the ball sprite into the correct position. It can probably be done using HMOVEs as the laser position will always be close to the ship position, but it is going to be a bit of a pain to implement! This is definitely the most complex and pointless feature that I have added to the game so far :)

 

Chris

Link to comment
Guest
Add a comment...

×   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...