Jump to content
IGNORED

Concepting a Conversion #1: Fire One


Cybergoth

Recommended Posts

Hi there!

 

So here's the next milestone from Bad Panda Bear:

fire2.gif

 

Good to see we're already getting a bunch of sprites displayed. No bad result for the first two weeks I'd say! :)

 

Attached is the new base source, which also includes changed RAM declarations just as Tom suggested.

 

Again you'll have some time to check out the new source and discuss it. In tomorrows lesson we'll continue with getting those sprites some better looking shapes.

 

Gretings,

Manuel

fireone.zip

Link to comment
Share on other sites

Hi there!

 

Sprites. I'm sure you've all read the parts about sprites in Andrews tutorial, but let me say the basic things again.

 

Let's assume some sprite data for an arrow for example:

 

Arrow

   .byte %00010000

   .byte %00010000

   .byte %01010100

   .byte %00111000

   .byte %00010000

 

To get this displayed, we have to write our data byte per byte into one of the 2 sprite registers, one byte per scannline.

 

This is what our current sprite sub does:

;; SUBROUTINE: DisplayMainStripe
;; Displays a stripe of the main display
;; Arguments:
;; A: Background color for stripe
;; Y: Height in scanlines of stripe
;; Destroys:
;; A, Y, N, Z flags
;;
;; Notes:
;; Keeps X equal to the current scanline (i.e. decrements X for every WSYNC)
;; Expects to be called on the scanline before it starts displaying
;; Returns before last scanline has finished (caller must call WSYNC)

DisplayMainStripe SUBROUTINE

 ;; Set background color as first part of the scanline

  STA WSYNC

  STA COLUBK

  STX GRP0

  STX GRP1



  DEY

  DEX



 ;; Call WSYNC for all but the last line of the slice

.loop

  STA WSYNC

  STX GRP0

  STX GRP1

  DEX

  DEY

  BNE .loop

  RTS

 

As you see, basically it writes the content of the X register into the sprite register. To display the arrow, we'd need to load the shape for it first. How do we know, which byte of the sprite data is to displayed when?

 

Easy in our case: Remember we have Y counting the lines in our sprite subroutine. In the arrow line, Y is probably going from 4 to 0, so we have five different values for Y, which we could use to display the arrow, with code as simple as:

 


   LDA Arrow,Y

   STA GRP0

 

Ok, it's not that simple. If you think about it, you'll realize that this'll draw our arrow upside down, pointing upwards, as we read the 5th byte first, then the 4th and so on, as in:

 

Arrow

   .byte %00010000  ; Y = 0

   .byte %00010000  ; Y = 1

   .byte %01010100  ; Y = 2

   .byte %00111000  ; Y = 3

   .byte %00010000  ; Y = 4

 

Solution? Well, if you ever hacked anything in a 2600 binary, I'm sure you've nothiced that all gfx are stored upside down... :)

 

Ok, with that little tweak we get the arrow displayed properly.

 

Now, our subroutine is rather all-purpose, so it should not know what it is drawing. We need something more generic here, so what we're really wanting to do instead of

 


   LDA Arrow,Y

   STA GRP0

 

is this:

 


   LDA (spritePointer),Y

   STA GRP0

 

Whoa! What is this? :o

 

Well, here with have a pointer, pointing to any address in memory we want it to. And from this address we'd load our shape data. Let's assume spritePointer is pointing to "Arrow" for example, it'd draw our arrow, just like before. Next time we call our sub we just tweak the Pointer to our "Submarine" shape before calling it and it'll draw the submarine instead of the arrow!

 

Hm... so how is a pointer set? Most simple solution is using one of the powerful macros provided with macro.h:

 

SET_POINTER spritePointer, Arrow

 

Ok, but you're probably interested what the macro will do here. It looks like this:

 


LDA #<Arrow ; Get Lowbyte of Arrow Address

STA spritePointer   ; Store in pointer

LDA #>Arrow ; Get Hibyte of Arrow Address

STA spritePointer ; Store in pointer+1

 

So, just like I said, spritePointer is then pointing to our arrow data. You should also nothice that spritePointer needs two bytes for storing an absolute address, so we'd reserve it's memory as in:

 


   spritePointer     ds 2

 

That's it for this time. Now with adding to sprite pointers (one for each sprite), we should be able to display all sorts of shapes we want to.

 

Hm... time for the teams gfx artist to provide the shapes data... wait a minute, is there already an artist in the team?

 

Greetings,

Manuel

Link to comment
Share on other sites

I don't think there is an artist on the team, so I decided to try to make some sprites myself. Here is a screen shot with carriers on the left, tankers on the right, and an enemy sub in the middle. I left the bottom arrow alone for now. I'm not particularly happy with these so if anyone has suggestions for improving these sprites please let me know.

post-4723-1080371939_thumb.jpg

Link to comment
Share on other sites


LDA #<Arrow ; Get Lowbyte of Arrow Address

STA spritePointer   ; Store in pointer

LDA #>Arrow ; Get Hibyte of Arrow Address

STA spritePointer ; Store in pointer+1

 

Shouldn't the highbyte be "STA spritePointer+1" ? Otherwise your just overwriting the lowbyte.

Link to comment
Share on other sites

Hi there!

 

So, additionally to BPBs freshly added sprite shapes, Jason also submitted a first iteration of the title screen code:

 

fire3.gif

 

In tomorrows lesson, we'll take a break from learning new things. We'll scan through the source as is, looking for improvement and resource saving tricks. Resources such as ROM/RAM and most precious: Cycles! This should get especially interesting, so please check out the brandnew attached base source.

 

BTW: If you have troubles compiling it, make sure you have downloaded the newest versions of 2600 macro.h and vcs.h from the DASM homepage, as we're heavily using the macros in there.

 

BTW²: I think those sprite shapes make excellent dummys, we should keep them until a real artist joins the team ;)

 

Greetings,

Manuel

fireone.zip

Link to comment
Share on other sites

Hi there!

 

Ok, I said we're gonna analyse the main display some and look for optimisations this time. I'm mostly focusing on analysing the RAM requirements, but we'll probably also win some cycles and ROM along the way.

 

Llet's go from top to bottom and see what variables we might need. Keep in mind, we're looking at it from the display point of view.

 

1. The top arrows.

 

These have a shape, fixed size, a horizontal position and a color.

 

The shape is always the same, even for both. We can hardcode it. For now the SET_POINTER macro is fine, but we should keep in mind that there is room for optimisation.

 

The size is always "normal", so we can hardcode it in our preparation code:

 


LDA #$00

STA NUSIZ0

STA NUSIZ1

 

The horizontal position varies. We need two bytes of RAM for this:

topArrowsXPos ds 2

 

The color. One arrow is always black, the other is always red. We can hardcode this in the preparation section

 

2. The first row of ships

 

These have varying shapes and sizes and horizontal positions.

 

It's getting a little more complex here. We have 4 different ships on this line and they flicker. Basically any ship type can get displayed via any GRPX register any time.

 

If we look closer at the required data for those 4 ships, we'll see that there's two kinds of data:

 

- individual to each ship (eg: horizontal position, damage state)

- depending on the type (eg: speed, shape, size)

 

It'd be a great waste of RAM to assign extra bytes for those type depending values - instead, we just assign a single byte for it per ship:

 

topShipType ds 4

 

If we'd like to know the size of the 2nd ship for example, we'd get it like this:

 


LDX #$01

LDY topShipType,X  ; Get the type of our ship

LDA sizetable,Y       ; Get the actual size of this type

STA NUSIZ0

 

sizetable is a data table holding the NUSIZ values for all possible types of ships. We'll create such tables for all kinds of type specific data: speed, score, shape, hits required to sink, shape, etc

 

Now, for the horizontal position, RAM is required again. A LOT of RAM!!

Here's why:

 

The ships positions exceed the visible range of the screen. Let's assume their positions can go from 0-1024, that is more than 6 visible screens wide.

 

To store a value this big, we'd need two bytes per horizontal position, per ship. That is some 16 bytes for the two ship rows alone! 1/8 of all that we have!

 

Phew... and it get's worse. In those two bytes we have the "real" position only. We'll need also to know where the ships have to be dipsplayed on the screen!

 

Uhm... do we really? I'll leave you with this to think about... ;)

 

Well, thank heavens, for our current display point of view, we get away with something like

 

topShipsXPos ds 4

 

:)

 

3. The bottom ships line

 

See 2.

 

4. The sub and the bottom arrow.

 

Well, I'm not goin to repeat what I said above, so let's look what's special about these.

 

Well, the sub is alone on it's line, so here we have the possibility to actually compose it out of two sprites! Also its shape can be hardcoded again.

 

For its horizontal position we still need only a single byte, as we precisely know where the second sprite is: 8 pixel farther to the right!

 

The bottom arrow is special again. It's also composed out of two sprites, with probably differing sizes. It's position is sorta fixed also, but then again it's not. We'll probably need a complete extra lesson to tame that thing, so we better not worry about it at the moment :)

 

Ok, after such a big theory block, I'll give you one real optimisation for the current code:

 

Instead of storing the NUSIZ values into a variable and execute it in our sub, write it to the NUSIZ registers straight away in the preparation code!

 

Next week we'll have some real meat again and have a look at horizontal positioning... :o

 

Greetings,

Manuel

Link to comment
Share on other sites

Uhm... do we really? I'll leave you with this to think about... ;)

 

DOH! Nobody said there'd be any thinking involved. :dunce:

 

So I asked myself, what would I do in C/C++? Well I might try to get the screen position from the logical position by moding the logical position by 160.

 

But this won't work, because 1024 % 160 will be 64. This would leave an invisible wall at 64 pixels into the last screen, which would look bad.

 

So really the screen is just a window into the larger game world. So we could have a 2 byte variable that stores the upper left coordinate of the screen in logical coordinates. This would range from 0 to 864 (1024 - 160). To find the position of a ship in physical screen coordinates, simply subtract the screen's logical position from the ship's position. If it is < 0, then the ship is off screen and should not be drawn. If it is >= 0, and < 160 then the difference is the physical coordinates of the ship.

 

Hmmm...does the left corner of the sprite start when the RESPx is set? If so, then the above idea would have problems with ships that are partially off the screen to the left. So perhaps the logical coordinates should start at the right end of the logical world. Then the physical X position would be:


screenXPos - shipXPos

 

Assuming that screenXPos represents the left hand corner of the screen.

 

So if we could spare the cycles to do the above subtraction, we would not need the extra bytes to store the physical location of the ships. But we would need two bytes for the logical position of the screen.

 

So did anything I just wrote make sense to anyone? Anyway an another topic:

 

Instead of storing the NUSIZ values into a variable and execute it in our sub, write it to the NUSIZ registers straight away in the preparation code!

 

The reason I put the NUSIZ values into a variable was because I was afriad that changing the NUSIZ registers on the previous scan line could affect how that scanline displayed. This would only happen for the arrows because there are no empty scanlines between the arrows and the ships on the horizon.

 

So should I add a scanline between the arrows and the ships? It seems that this might be neccessary anyway for the horizontal positiong code.

 

That's enough thinking for tonight. It's sleepy time for me.

Link to comment
Share on other sites

Hi there!

 

So did anything I just wrote make sense to anyone?

 

Oh, it did make sense and I'm sure it'll work this way! :)

 

Yet watch out, here's another thought for you:

 

When turning left or right, instead of moving the sub left or right, could we possibly leave it where it is and rather turn the whole world around it? :D

 

So should I add a scanline between the arrows and the ships? It seems that this might be neccessary anyway for the horizontal positiong code.

 

Ok, I see why you used the extra variable. Well, we'll in fact going to need scannlines for repositioning, probably 2-3. So think it'd be best if the sprite drawing sub does a proper cleanup at the end like:

 

INY; Assuming Y is #$FF after the subroutine loop

STA WSYNC

STY GRP0 

STY GRP1

RTS

 

So now the NUSIZ value doesn't matter between the subroutine calls.

 

Greetings,

Manuel

Link to comment
Share on other sites

I'v got no idea how you could keep track of the ram in an overlay situation though.

 

for instance like this:

(anyway, at some point things like this get tedious and one might

want to do some awk wizardry to extract that information from the listing file. or modify dasm to output a list of all segments with their location and size)

 


       processor 6502

       

USEDRAM set 0



       seg.u   commondata

       org     $80

var1    ds      3

COMMONDATA_END = .

USEDRAM set .



       seg.u   bank0data

       org     COMMONDATA_END

foo     ds      3

       if USEDRAM < .

USEDRAM set .

       endif

       

       seg.u   bank1data

       org     COMMONDATA_END

bar     ds      1

       if USEDRAM < .

USEDRAM set .

       endif





       



       seg     code

       org     $f000

reset

       sei

       sta.w   0

       brk

       

       .echo   "ROM used:", .-reset, "bytes"

       .echo   "RAM used:", USEDRAM-$80, "bytes"

       

       org     $fffa

       .word   reset

       .word   reset

       .word   reset

Link to comment
Share on other sites

This week my main problem wasn't running out of RAM or ROM, but running out of cycles.

 

I first tried to implement what Cybergoth suggested for the ship types, but I also had to preserve X as the scanline counter, as I figure we'll need it when whenever we get around to firing torpedoes.

 


TXA                ; Preserve line counter 

LDX #$00

LDY topShipType,X  ; Get the type of our ship

TAX

LDA SizeTable,Y    ; Get the actual size of this type

STA NUSIZ0

 

Then I decided to do the same thing with the sprite pointers, using two tables for the high and low poritons of the addresses.

 


LDA SpriteLowTable,Y; Set the sprite to use for the ship

STA sprite2Pointer

LDA SpriteHighTable,Y

STA sprite2Pointer+1

 

But when I repeated this for both ships, and added in code to reset the sprite colors from the arrows, I ended up needing more than 1 scanline to prepare for the ships on the horizon. I thought this was unacceptable if additional scanlines are going to be taken up by the positioning code.

 

So in the code I submitted this week, I decided to simplify the code to get the ship type like this:


LDY topShipType      ; Get the type of the first ship

...

LDY topShipType+1    ; Get the type of the second ship

 

But when we go to a flickering display, this will only work if we sacrifice some additional cycles (and possibly RAM) in the vertical blank code to shift those types around between frames.

 

Doing this saved 14 cycles, which allowed me to barely fit everything in. But I suspect that this will have to be reoganized somehow because there won't be enough cycles to switch missile graphics on or off when it comes time to implement torpedoes.

Link to comment
Share on other sites

but I also had to preserve X as the scanline counter, as I figure we'll need it when whenever we get around to firing torpedoes.

Just use a ram location for a line counter.


line_counter      ds 1

<snip>

dec line_counter

bne loop

That way you have all registers free for work ;)

Link to comment
Share on other sites

Just use a ram location for a line counter.


line_counter      ds 1

<snip>

dec line_counter

bne loop

That way you have all registers free for work ;)

 

I considered that, but decrementing a memory location takes 5 cycles as opposed to the 2 it takes to decrement a register. That's 3 cycles the current version of the fireone code doesn't have.

 

Of course right now there isn't enough cycles to actually use the scanline counter anyway. I think its long past time that I start looking at other games to see how they handle this sort of thing.

Link to comment
Share on other sites

I first tried to implement what Cybergoth suggested for the ship types, but I also had to preserve X as the scanline counter, as I figure we'll need it when whenever we get around to firing torpedoes.

Without going into details too much, it is usually better to use Y as the scanline counter. I am sure Manuel will explain why if necessary. :)

Link to comment
Share on other sites

Without going into details too much, it is usually better to use Y as the scanline counter. I am sure Manuel will explain why if necessary. :)

Indirect Y addressing takes 5 cycles but indirect X takes 6.

Apart from that I don't see any difference between Y and X. ;)

Link to comment
Share on other sites

Hi there!

 

I first tried to implement what Cybergoth suggested for the ship types, but I also had to preserve X as the scanline counter, as I figure we'll need it when whenever we get around to firing torpedoes.

Without going into details too much, it is usually better to use Y as the scanline counter. I am sure Manuel will explain why if necessary. :)

 

Ok, first advice I have: Don't Panic! ;)

 

Let's think about our problems, starting with what we currently have:

 

We have actually 2 scannline counters. X _and_ Y!

 

As Thomas mentioned, there is good reason for using these exactly as they are currently used. We didn't spend too much thought about this yet, as accidently it just fell into place "correctly". :)

 

X:

 

This is counting the overall scannlines, which makes sense. We'll in fact need an overall scannline count for drawing the torpedos and we'll need it every single line of our main display (except repositioning), so it makes perfect sense to have it ready. Storing it in a variable would certainly work as well, but additionally to the additional decerement cycles PBP mentioned, it'd be also wastefull to load it every single scannline.

 

Y:

 

This is counting the scannlines of our stripes. Since we're going to make heavy use ($$$$),Y accesses, this is also the "best" way to do this.

 

So, with do the right thing so far, we're going to look somewhere else to save our cycles...

 

Let's have a look at the two snippets BPB showed us:

 

TXA                ; Preserve line counter 

LDX #$00 

LDY topShipType,X  ; Get the type of our ship 

TAX 

LDA SizeTable,Y    ; Get the actual size of this type 

STA NUSIZ0

 

I think this can be optimised a little. This code is placed in our "preparation" section, not in the drawing sub. So, actually Y should be free to use here:

 

LDY #$00 

LDA topShipType,Y  ; Get the type of our ship 

TAY 

LDA SizeTable,Y    ; Get the actual size of this type 

STA NUSIZ0

 

The second snippet also reveals a little room for otimisation:

 

LDA SpriteLowTable,Y; Set the sprite to use for the ship 

STA sprite2Pointer 

LDA SpriteHighTable,Y 

STA sprite2Pointer+1

 

This is setting both the LO and the HI byte of the spritepointer.

 

Now here's a "what if" for you:

 

->"What if we crammed all sprites into a single page?"

 

Another thing we should consider: On most lines of our main display we only have "void". Except the torpedos crossing it, there isn't displayed anything.

 

So, if actually our preparation code sometime later in the process will cost us another scannline, it might shift some things here and there, but in the end it won't kill us.

 

So, as for the "horizontal positioning" I wanted to discuss this weekend, this'll be delayed until tomorrow. For good reason. I'm now going to throw the code at you we're going to use for this purpose and I want you to have some time to think about it:

 

PosElement

      SEC

      STA WSYNC

.waitzone

      SBC #$0F

      BCS .waitzone

      EOR #$07

      ASL

      ASL

      ASL

      ASL

      STA HMP0,X

      STA RESP0,X

      RTS

 

Ok, we probably can't use it exactly as is, but in general that's it and our code will look at least very similar and use the same principle

 

Greetings,

Manuel

Link to comment
Share on other sites

Hi there!

 

Without going into details too much, it is usually better to use Y as the scanline counter. I am sure Manuel will explain why if necessary. :)

Indirect Y addressing takes 5 cycles but indirect X takes 6.

Apart from that I don't see any difference between Y and X. ;)

 

Have a closer look again, indirect X addressing works entirely different than indirect Y.

 

BTW: I'll add your ROM usage suggestions to the next update of the base source. Thanks!

 

Greetings,

Manuel

Link to comment
Share on other sites

Hi there!

 

So, as for the "horizontal positioning" I wanted to discuss this weekend, this'll be delayed until tomorrow.

 

Lucky me, with that extra day delay on my side, Andrew saved me the trouble and already covered all you need to know about this technique (better than I ever could):

 

http://www.atariage.com/forums/viewtopic.php?t=47639

 

Thanks! :)

 

I'd suggest for BPB (and anyone else interested) to give it a try on their own and next weekend we'll have a look at it together and resolve any occuring problems, ok?

 

Greetings,

Manuel

Link to comment
Share on other sites

  • 2 weeks later...

Well despite a fever, taxes, and a new digital camera I got some horizontal positioning code working in FireOne. I've sent off the code to Cybergoth but I thought I'd describe it here.

 

I started with the code Andrew posted overe here and tried to modify it so that it would display position 0 at pixel position 0 (TIA clock 68 ). The original lookup table had Left 7 as the adjustment for -15. So to position 0 at TIA clock 68, the STA RESP0,X had to happen at TIA clock 68 - 5 + 7. This is TIA clock 70, which won't work because we can only strobe at multiples of 3 TIA clocks.

 

So I changed the table to vary from Left 6 to Right 8. I then added a 2 cycle NOP to the positioning code. This allowed the STA RESP0,X to happen at TIA clock 69. 69 + 5 - 6 gives 68, which is exactly what I wanted.

 

Finally I had a problem with the positioning code taking a variable number of scanlines based on the arguments passed in. To solve this, I used a suggestion from Eric Ball and I moved the table to avoid the 1 cycle penalty. This meant that I had 3 extra cycles at the start of the positioning routine. I used these cycles to save off the X position to a temporary variable while positioning. Then after positioning was complete, I added code to do a STA WSYNC if the X position was far enough to the left that the routine did not take up a scanline.

 

So here it is:

; Positions an object horizontally
; Inputs: A = Desired pixel position (0-159)
; X = Desired object to be positioned (0-4).
; scanlines: If control comes on or before cycle 73 then 2 scanline are consumed.
; If control comes after cycle 73 then 3 scanlines are consumed.
; Outputs: X = unchanged
; A = Unchanged
; Y = the "remainder" of the division by 15 minus an additional 15.
; control is returned from 1 to 11 cpu cycles on the next scanline.
; Credits: Originally from a post on the Stella List from R. Mundschau.
; This was taken from the AtariAge 2600 Programming For Newbies forum
; where it was posted by Andrew Davie.  It has been modified based
; on suggestions from Eric Ball to avoid a 1 cycle penalty when
; crossing a page boundary and to add a delay loop so that it takes a
; consistent amount of scanlines.

PosObject   SUBROUTINE



           sta WSYNC                  ; 00     Sync to start of scanline

           sta tempVar2               ; 03     Preserve position for delay loop after positioning

           sec                        ; 05     Set the carry flag so no borrow will be applied during the division.

.divideby15 sbc #15                    ; 07     Waste the necessary amount of time dividing X-pos by 15!

           bcs .divideby15            ; 09/10  14/19/24/29/34/39/44/49/54/59



           tay                        ; 2

           lda fineAdjustTable,y      ; 4 cycles (Table setup guarantees no crossing page boundary)

           sta HMP0,x                 ; 4



           sta RESP0,x                ; 4  (23/28/33/38/43/48/53/58/63/68/73)



          ;; Double check Positioning:

          ;; If A = 0:

          ;;          STA RESP0,X happens at 23 cycles

          ;;          STA HMP0,X is Left 6 (-15 in lookup table)

          ;;          TIA clock position is 23*3 + 5 -6 = 68 = correct

          ;; If A = 14:

          ;;          STA RESP0,X happens at 23 cycles

          ;;          STA HMP0,X is Right 8 (-1 in lookup table)

          ;;          TIA clock position is 23*3+5 +8 = 82 = correct

          ;; If A = 15:

          ;;          STA RESP0,X happens at 28 cycles

          ;;          STA HMP0,X is Left 6 (-15 in lookup table)

          ;;          TIA clock position is 29*3+5 - 6 = 83 = correct

          ;; If A = 159:

          ;;          STA RESP0,X happens at 73 cycles

          ;;          STA HMP0,X is Right 3 (-6 in lookup table)

          ;;          TIA clock position is 73*3+5 +3 = 227 = correct



          ;; Now, whether the positioning algorithm goes past the scanline

          ;; or if a STA WSYNC to end the scanline, depends on the original

          ;; value of A.  Experiment has shown that a STA WSYNC is needed

          ;; when A < 120



          ;; Comments show time after scanline for A = 0, A= 120, and A =159

           LDA tempVar2           ; (26,66,76)

           CMP #120               ; (28,68,02)

           BCS .notLessThan120    ; (30,71,05)

           STA WSYNC



.notLessThan120



           rts                    ; (6,1,11)



          ;; Returns Between 1 to 11 cycles on new scanline



fineAdjustBegin



           DC.B %01100000; Left 6         -15

           DC.B %01010000; Left 5         -14

           DC.B %01000000; Left 4         -13

           DC.B %00110000; Left 3         -12

           DC.B %00100000; Left 2         -11

           DC.B %00010000; Left 1         -10

           DC.B %00000000; No movement.   -9

           DC.B %11110000; Right 1        -8

           DC.B %11100000; Right 2        -7

           DC.B %11010000; Right 3        -6

           DC.B %11000000; Right 4        -5

           DC.B %10110000; Right 5        -4

           DC.B %10100000; Right 6        -3

           DC.B %10010000; Right 7        -2

           DC.B %10000000; Right 8        -1



fineAdjustTable EQU fineAdjustBegin - %11110001; NOTE: %11110001 = -15



 

And let me thank Andrew Davie, R. Mundschau, Cybergoth, Eric Ball, and the entire Atari 2600 homebrew community for putting so much information out there on this. Without such a community I would have given up on Atari 2600 programming as soon I got to horizontal positioning.

Link to comment
Share on other sites

DOH! Testing revealed that I did not need to do the check for the STA WSYNC. It works fine if I do it right after the STA RESP0,X.

 

Originally I had the STA WSYNC outside of the subroutine, where the RTS had pushed it onto the next scanline. When I cleaned up the code to move it into the subroutine, I missed the fact that the check was no longer needed.

 

Thanks for pointing that out.

Link to comment
Share on other sites

Hi there!

 

Great progress! The demo looks really cool now already!

 

I'm just having a look back at Dustys original set of milestones:

 

Milestone 1: Get the background colors of the horizon and the ocean drawn.

Milestone 2: Get two ships in the foreground drawn.

Milestone 3: Get the two ships in the foreground moving.

Milestone 4: Get two more ships in the foreground, this involves adding flicker to alternate frames.

Milestone 5: Add the enemy sub.  This might be a part of mileston 4

Milestone 6: Add the ships on the horizon.

Milestone 7: Add the top black arrow.

Milestone 8: Add any top red targeting arrows

Milestone 9: Add the bottom arrow.

 

Oh-oh... I guess we cheated a bit here and there, as we actually did almost everything at once. :wink:

 

Let me have a look at the upcoming jobs that are still to be done:

 

1. Clean up and optimize what we have. The daily job of the 2600 coder. :)

2. Torpedos

3. Introduce flicker on the two ship rows

4. Expand our world, making it bigger than what's visible on the screen.

5. Fractional movement

 

Ok, there is probably a dozen things more to do, but this should be our next goals.

 

Torpedos are the most important. They are the last missing thing in the actual kernel code and if we get them properly added, we're done with the most difficult part of the whole project.

 

First, let us think about adding those beasts.

 

We're going to use the particle objects for these, so in theory we could use both missiles and the ball for this job. Flicker on these small objects is acceptable, so we can have 6 torpedos on the screen at once. That is, if we have enough cycles to use all three particles. We're probably forced to compromise here and can only use two.

 

Their horizontal position can easily be set during VBLANK.

 

The tough part is the vertical positioning. Every single scanline we'll need to check wether to display a torpedo there or not.

 

The fastest possible code we can use here, as invented by TJ, looks ~ like this:

 

    LDA #3

   DCP torpedoyPos

   ADC #2

   STA ENABL

 

This will be 12 cycles. Doing three particles like this will eat up 36 cycles of every single scanline a torpedo can be displayed :o

 

One problem are the repositioning scannlines. Simply put, it is not possible to have the torpedos properly switched on (or off in case) on these lines.

 

Now my idea was to have the torpedo code executed only every 2nd scannline. Assuming we have the torpedo code on every even scannline, we just do the repositiong just on odd scanlines, et voila! :)

 

Phew... All of this can get very complex very quickly, so in an attempt to simplify things again, we should talk about milestone 1 in the list I gave above:

 

1. Clean up and optimize what we have.

 

There is an important thing we have to do here, before even starting with the torpedos. We need to restructure things a little.

 

Currently the code is very good organized, yet there's room for improvement. Currently the sprites are repositioned in our preparation code. Yet, every sprite stripe in our kernel needs to reposition two sprites, so all of this code should wander from the preparation areas into the

DisplayMainStripe subroutine.

 

What the preparation code will do is store the desired positions into tempVar1 & tempVar2 for example and then our master subroutine will position it reading these values again.

 

In order to have the torpedos added in, the subroutine should handle things like this:

 

Line1: Do Torpedos, prepare positioning

Line2: Position sprite 1

Line3: Do Torpedos, prepare positioning

Line4: Position sprite 2

 

Loop:

Line5: Do Torpedos, draw sprites

Line6: Just draw sprites

Do Loop

 

Also, I think it might be best to not do the positioning code in a subroutine, but to just have it twice in the routine. Not sure though. Same with the sprite drawing code. I'd just doubdle it. In case, this'd also mean padding sprites with uneven heights with zeros.

 

Ok. I know this is very much and ultra complicated to do but if this last hurdle of the display code is mastered, all remaining tasks will be peanuts... (...hopefully! ;) )

 

Updated base source is attached.

 

Greetings,

Manuel

fireone.zip

Link to comment
Share on other sites

Hi there!

 

Since I bet BPB will ultimately busy still for a while giving the display kernel the finishing touches, we can use the time to spend some thoughts on continuing with the intro.

 

Have look at this:

fireone.gif

 

(Sorry about the see through background...)

 

I think we should try recreate this effect with our current PF title screen.

Any ideas anyone how we should do that? :)

 

Greetings,

Manuel

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