Jump to content





Photo

Flashing Cockpits

Posted by cd-w, 04 June 2008 · 83 views

Atari Programming
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

Attached Files






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.
  • Report
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.
  • Report
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.

Posted Image

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.
  • Report

Thomas Jentzsch, on Wed Jun 4, 2008 10:49 AM, said:

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.
  • Report
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:

  • 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.
  • 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
  • Report
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.
  • Report
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.
  • Report
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
  • Report
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

batari, on Thu Jun 5, 2008 6:31 AM, said:

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.
  • Report

cd-w, on Thu Jun 5, 2008 2:32 AM, said:

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

Quote

cpy alienbottom
bcc A_NewSprite
  • Report
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
  • Report
Depending upon the sprite design, could the flashing cockpit be a "ready to fire" indicator?
  • Report

February 2012

S M T W T F S
   1234
56789 10 11
12131415161718
19202122232425
26272829   

Recent Entries

Recent Comments

Search My Blog