Jump to content
IGNORED

What is Fine+Coarse XPOS?


Recommended Posts

I have been reading the Stella lists and checking out code from there and a few times I've come across people talking about "standard X" and "FC_X" coordinates.

 

I've looked some code examples (from air sea battle I believe) but I can't figure out what is happening, or why it is used. Is it a trick to save cycles for horizontal positioning of multiple sprites? What are the good and bad points about it?

 

Thanks!

Link to comment
Share on other sites

I have been reading the Stella lists and checking out code from there and a few times I've come across people talking about "standard X" and "FC_X" coordinates.

 

I've looked some code examples (from air sea battle I believe) but I can't figure out what is happening, or why it is used. Is it a trick to save cycles for horizontal positioning of multiple sprites? What are the good and bad points about it?

 

Thanks!

I haven't read the posts or the code examples that you're referring to, so I'm not sure what they are talking about. But I do know that you need to combine coarse positioning with fine positioning to set a player's position to a specific desired horizontal location, so I'm going to guess that "standard X" is maybe referring to the X position on the screen, and the "FC_X" coordinates are the coarse and fine position values that will let you set the player to the given "standard X" position.

 

The way the coarse/fine positioning works is something like this (I'll try to explain it so it's easy to understand, but no guarantees!):

 

A given X position represents a particular color clock value on the scan line, since a color clock is the smallest pixel size on the 2600's screen. Actually, a color clock is a time unit-- the time it takes to draw the smallest possible pixel-- but we can think of it as a pixel position. Anyway, the Atari's color clock is 3 times faster than the 6502's machine clock-- i.e., 1 machine cycle equals 3 color clocks. Also, the shortest (quickest) possible 6502 instruction takes 2 machine cycles, or 6 color clocks.

 

To set a player's horizontal position, you have to hit or strobe the RESPx register at the moment when the Atari has reached that position on the scan line. This is usually done by waiting in a loop for a certain number of times, as follows:

  LDX COARSE_VALUE
LOOP
  DEX
  BNE LOOP
  STX RESP0

The DEX instruction takes 2 machine cycles, and taking a branch takes 3 machine cycles (as long as you don't cross a page boundary), so each time you go through the loop, you're waiting a total of 5 machine cycles, or 15 color clocks-- hence, when you hit RESP0 you are setting player 0's position to a coarse location, where each possible coarse location is 15 color clocks after the previous coarse location. Hence, you probably won't be setting the player to the exact X position you wanted; more likely, it will be some 7 or 8 color clocks to the left or right of the desired location.

 

That's where the fine positioning comes into play. After you set the coarse position using RESPx, you set HMPx to a value that will shift the player to the left or right the necessary number of pixels, then you strobe WSYNC and HMOVE to fine-tune the player's position.

 

Thus, for any given "standard X" position, you need to know how many times to wait in the loop before hitting RESPx for the coarse position, and you also need to know what value to store in HMPx to fine-tune the horizontal position.

 

There are two ways you can convert the desired "standard X" position into the necessary coarse positioning value and fine positioning value. One way is to calculate the positioning values, such as by using a loop to convert the desired X position into a "mod 15" coarse value and a remainder. The other way is to use two lookup tables-- one for the coarse value, and one for the fine value. The calculation method takes fewer bytes but uses more cycles, whereas the lookup tables take more bytes but use fewer cycles, so which method you use can depend on whether it's more critical to save cycles or to save bytes.

 

Once you know the coarse and fine values to use, you would do something like the following:

  LDX FINE_VALUE
  STX HMP0
  LDX COARSE_VALUE
LOOP
  DEX
  BNE LOOP
  STX RESP0
  STX WSYNC
  STX HMOVE

 

As I said, I'm not sure that this is what the posts and code examples were referring to, but it might be. Hopefully my brief explanation was clear enough so that you can reread those posts and code examples, and determine if that's what they're talking about!

 

In any case, this type of positioning takes at least one scan line to do, or maybe two, since you must calculate or look up the coarse/fine values, set HMPx and the loop counter (X or Y register), wait in a loop, and hit RESPx on one line, then finish that line before you do the HMOVE, so you can't really display the player on that line (since it's being repositioned), then you can start displaying the player on the next line. If you aren't using the player for multiple sprites, you can just do the positioning during the vertical blank interval. Otherwise, you can reposition the player multiple times on the screen, as long as you leave at least one blank player line between the multiple sprites. And note that I didn't include clearing the player graphics register before setting the new position, or loading the player graphics after setting the new position, so don't forget to do that too.

 

Michael Rideout

Link to comment
Share on other sites

I have been reading the Stella lists and checking out code from there and a few times I've come across people talking about "standard X" and "FC_X" coordinates.

 

The Atari 2600 can set sprite positions two ways: (1) hit the sprite reset register when the sprite is at the desired position, or (2) use the HMOVE circuit to "nudge" sprites +/- 0 to 8 pixels. Using approach #1 it's difficult to get better than 15-pixel resolution, and generally impossible to get better than three-pixel resolution. But many games use approach #1 with fifteen pixel resolution, combined with a nudge to put the sprite in the exact right place.

 

That style of coding is perhaps slightly obsolete, given that a technique demonstrated in <i>Battlezone</i> makes it easy to combine both operations into a single routine using a single position value.

Link to comment
Share on other sites

That style of coding is perhaps slightly obsolete, given that a technique demonstrated in <i>Battlezone</i> makes it easy to combine both operations into a single routine using a single position value.

Wow, I need to study a disassembly of Battlezone! Has anyone disassembled and commented it already?

 

Michael Rideout

Link to comment
Share on other sites

Thanks for your help guys.

 

I've actually been using the Battelzone routine (I think I got it from Andrew's tutorials) and I understand how it works:

 

; Sprite Horizontal Positioning Routine (Battlezone Variant)
; On Entry: A = Coordinate and X = Object ID
XPosition
sta WSYNC  ; [0]
sec  ; [0] + 2
       ; Divide Position by 15
Divide15
sbc #15  ; [2] + 2
bcs Divide15  ; [4] + 2/3
nop  ; [6] + 2
tay  ; [8] + 2
; Store Coarse and Fine Position
lda fineAdjustTable,y	; [10] + 4
sta HMP0,x  ; [14] + 4
sta RESP0,x  ; [18] + 4
			
; Position At Cycles: [22/27/32/37/42/47/52/57/62/67/72/77]
		
rts

ORG $F300
fineAdjustBegin
DC.B %01110000; Left 7
DC.B %01100000; Left 6
DC.B %01010000; Left 5
DC.B %01000000; Left 4
DC.B %00110000; Left 3
DC.B %00100000; Left 2
DC.B %00010000; Left 1
DC.B %00000000; No movement.
DC.B %11110000; Right 1
DC.B %11100000; Right 2
DC.B %11010000; Right 3
DC.B %11000000; Right 4
DC.B %10110000; Right 5
DC.B %10100000; Right 6
DC.B %10010000; Right 7
fineAdjustTable EQU fineAdjustBegin - %11110001; NOTE: %11110001 = -15

 

But now I am trying to do multiple "rows" of enemies with a single sprite on each rows. Kind of "frogger" style multiple-sprite placement.

 

Using the battlezone method seems to take too many cycles to be called once every "row" of enemies (though maybe I'm just doing it wrong). I found an example on the Stella list to a multi-sprite demo that does this in the vblank area:

 

ldx #NUMBER_LINES-1; Load number of rows of enemy
SetupEnemies 
lda EnemyX,x 	; Load the X pos for each enemy
jsr ConvertToFC	; Magical convert it to "FC" positioning that I dont understand
sta EnemyFC_X,x	; and save the result in in the FC array
dex
bpl SetupEnemies

 

Then in the Kernel, for each row of enemies it does:

ldy LineCounter; Current row
lda EnemyFC_X,y; Get Player1 position

sta WSYNC	; Start with a new scanline. 
sta HMP1	; Prepare Player1 fine motion
and #$0F	; Prepare Player1 coarse positioning
tay
PositionLine 
dey			; Waste time
bpl PositionLine
sta RESP1	; Position Player1
							
sta WSYNC	; Wait for next scanline
sta HMOVE	; Apply fine motion

To position each enemy correctly. This works fine (though doing move HMOVE messes up my player 0 sprite at the bottom of the screen for some reason) but adds those short black lines on the left side of the screen as in Air Sea Battle. Which probably isnt a co-incidence, as the mystical ConvertToFC routine is this:

 

; Straight from "Air sea battle", here's the routine
; to convert from standard X positions to FC positions.

ConvertToFC
   STA    TEMP+1
      BPL    LF34B
      CMP    #$9E
      BCC    LF34B
      LDA    #$00
      STA    TEMP+1
LF34B: LSR
      LSR
      LSR
      LSR
      TAY
      LDA    TEMP+1
      AND    #$0F
      STY    TEMP+1
      CLC
      ADC    TEMP+1
      CMP    #$0F
      BCC    LF360
      SBC    #$0F
      INY
LF360: CMP    #$08
      EOR    #$0F
      BCS    LF369
      ADC    #$01
      DEY
LF369: INY
      ASL
      ASL
      ASL
      ASL
      STA    TEMP+1
      TYA
      ORA    TEMP+1
      RTS

 

Which is the F+C question I was asking about originally. I thought this must be a standard way to do multiple sprite X placement. Perhaps it isnt?! I guess that the magical FC routine somehow squishes both fine and coarse placement into one byte? Is this better than using the battlezone method?

 

Thanks!

Link to comment
Share on other sites

Hi there,

 

Check out my Air-Sea Battle disassembly. Maybe that can give a little more insight to Joe Decuir's method.

 

Using the battlezone method seems to take too many cycles to be called once every "row" of enemies (though maybe I'm just doing it wrong).

The positioning can potentially take a whole scan line. Check out my Climber 5 source for an example. When I first started coding Climber 5 I was using the routine used in Air-Sea Battle to position the girders (missiles). While working on Climber 5, Manuel discovered this routine in Battlezone and I adopted it for the released version of Climber 5. I think it works really well.

 

Edit:

I forgot to add that Garry Kitchen's Space Jockey and Donkey Kong use a similar positioning technique used in Battlezone.

 

Also there are other methods to moving sprites horizontally. I liked the technique John Harris used in Jawbreaker. The upper nybble was used for the fine motion and the lower nybble was used for the coarse positioning. He would adjust the player's position by adding (or subtracting) 16. If an overflow occurred he would adjust the coarse position value accordingly. Check out my Jawbreaker disassembly.

Edited by DEBRO
Link to comment
Share on other sites

Those disassemblies are excellent! And after re-reading Michael's description of sprite positioning, it all makes much more sense now. Still, this is a bloody tricky machine... I can't believe there were ANY games made for it!

 

(Actually one question: how do you compile those disassemblies with dasm? I get errors on lines that have "ldy #-1". It doesnt like -1!)

Edited by nmoog
Link to comment
Share on other sites

Those disassemblies are excellent!

There are some errors but I'm glad you like them.

 

(Actually one question: how do you compile those disassemblies with dasm? I get errors on lines that have "ldy #-1". It doesnt like -1!)

Something happended with the new version of DASM that doesn't allow this to assemble anymore :sad: Search the source for #-1 and change them to #<-1 or #$FF. It should assemble then.

Link to comment
Share on other sites

Those disassemblies are excellent!

There are some errors but I'm glad you like them.

 

(Actually one question: how do you compile those disassemblies with dasm? I get errors on lines that have "ldy #-1". It doesnt like -1!)

Something happended with the new version of DASM that doesn't allow this to assemble anymore :sad: Search the source for #-1 and change them to #<-1 or #$FF. It should assemble then.

What happened is that the new version of DASM evaluates everything as 32-bit (or 16-bit?) numbers - so #-1 evaluates to #%11111111111111111111111111111111 and obviously you can't load a 32-bit number into an 8-bit register, so it chokes.

 

It also chokes on things like #~%00001111 for the same reason.

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