[stella] Combat REFPx mystery

Subject: [stella] Combat REFPx mystery
From: Glenn Saunders <mos6507@xxxxxxxxxxx>
Date: Mon, 22 Aug 2005 23:34:21 -0400
Here is something that's been bugging me for a long time.  Maybe Dennis can 
comment on this.

The first time I looked at the Indy 500 disassembly (or Combat for that 
matter which this appears to come from) I wondered why it stores the player 
graphics like this:


CarGraphics
    .byte $EE ; |XXX.XXX.|
    .byte $EE ; |XXX.XXX.|
    .byte $44 ; |.X...X..|
    .byte $7F ; |.XXXXXXX|
    .byte $7F ; |.XXXXXXX|
    .byte $44 ; |.X...X..|
    .byte $EE ; |XXX.XXX.|
    .byte $EE ; |XXX.XXX.|

    .byte $18 ; |...XX...|
    .byte $D8 ; |XX.XX...|
    .byte $CB ; |XX..X.XX|
    .byte $5E ; |.X.XXXX.|
    .byte $7E ; |.XXXXXX.|
    .byte $64 ; |.XX..X..|
    .byte $36 ; |..XX.XX.|
    .byte $36 ; |..XX.XX.|

    .byte $30 ; |..XX....|
    .byte $32 ; |..XX..X.|
    .byte $CC ; |XX..XX..|
    .byte $DC ; |XX.XXX..|
    .byte $3B ; |..XXX.XX|
    .byte $33 ; |..XX..XX|
    .byte $0C ; |....XX..|
    .byte $0C ; |....XX..|

    .byte $04 ; |.....X..|
    .byte $CC ; |XX..XX..|
    .byte $F8 ; |XXXXX...|
    .byte $1F ; |...XXXXX|
    .byte $DB ; |XX.XX.XX|
    .byte $F0 ; |XXXX....|
    .byte $3E ; |..XXXXX.|
    .byte $06 ; |.....XX.|

    .byte $18 ; |...XX...|
    .byte $DB ; |XX.XX.XX|
    .byte $FF ; |XXXXXXXX|
    .byte $DB ; |XX.XX.XX|
    .byte $18 ; |...XX...|
    .byte $DB ; |XX.XX.XX|
    .byte $FF ; |XXXXXXXX|
    .byte $C3 ; |XX....XX|

    .byte $20 ; |..X.....|
    .byte $33 ; |..XX..XX|
    .byte $1F ; |...XXXXX|
    .byte $F8 ; |XXXXX...|
    .byte $DB ; |XX.XX.XX|
    .byte $0F ; |....XXXX|
    .byte $7C ; |.XXXXX..|
    .byte $60 ; |.XX.....|

    .byte $0C ; |....XX..|
    .byte $4C ; |.X..XX..|
    .byte $33 ; |..XX..XX|
    .byte $3B ; |..XXX.XX|
    .byte $DC ; |XX.XXX..|
    .byte $CC ; |XX..XX..|
    .byte $30 ; |..XX....|
    .byte $30 ; |..XX....|

    .byte $18 ; |...XX...|
    .byte $1B ; |...XX.XX|
    .byte $D3 ; |XX.X..XX|
    .byte $7A ; |.XXXX.X.|
    .byte $3E ; |..XXXXX.|
    .byte $26 ; |..X..XX.|
    .byte $6C ; |.XX.XX..|
    .byte $6C ; |.XX.XX..|


It's storing the top half of the rotation.  This requires 8 sets of 
graphics.  So depending on the rotational position, it appears to load up 
an 8-byte buffer in RAM by either INCing or DECing through the ROM.  This 
seems very wasteful of RAM to me.

But not only that, it's also still necessary to reflect the sprite, like to 
have the car point left instead of right.

Some of that code is below:

LoadCarGraphics
    lda frameCount                   ; get the current frame count
    and #1                           ; alternate between player 1 and 2 each
    tax                              ; frame
    lda playerDirections,x           ; get the player's direction
    sta REFP0,x                      ; set the player's reflect state
    asl                              ; multiply the value by 8 (i.e number
    asl                              ; of rotations)
    asl
    cmp #MAX_CAR_SPRITES-1
    clc
    bmi .setCarGraphicsOffset
    sec
    eor #$47

If it really wanted to save ROM it could have stored only 5 sprite shapes 
for the top-right quarter-rotation and better utilized REFPx, but it 
doesn't do this!





For Death Derby, I store 9 car shapes which represents the complete 
right-hand half of the rotation:

Car_Rotation_Frame0:;UP (ACTUAL)
   ;.byte %00000000
	.byte %01011010; .X.XX.X.
	.byte %01111110; .XXXXXX.
	.byte %01100110; .XX..XX.
	.byte %00011000; ...XX...
	.byte %01011010; .X.XX.X.
	.byte %01111110; .XXXXXX.
	.byte %01011010; .X.XX.X.
	.byte %00011000; ...XX...

Car_Rotation_Frame1:
   ;.byte %00000000

	.byte %00001100; ....XX..
   	.byte %00111100; ..XXXX..
	.byte %11101000; XXX.X...
	.byte %11011010; XX.XX.X.
   	.byte %00011010; ...XX.X.
   	.byte %01011111; .X.XXXXX
   	.byte %00111100; ..XXXX..
	.byte %00101100; ..X.XX..


Car_Rotation_Frame2:
   ;.byte %00000000
	.byte %00011000; ...XX...
	.byte %01111000; .XXXX...
	.byte %01000010; .X....X.
	.byte %11011011; XX.XX.XX
	.byte %11011100; XX.XXX..
	.byte %00011110; ...XXXX.
	.byte %00110110; ..XX.XX.
	.byte %00010000; ...X....

Car_Rotation_Frame3:
   ;.byte %00000000
   	.byte %00110000; ..XX....
   	.byte %00110010; ..XX.X..
	.byte %01100011; .XX...XX
   	.byte %01011110; .X.XXXX.
	.byte %11111111; XXXXXXXX
   	.byte %11000111; XX...XXX
   	.byte %00011100; ...XXX..
	.byte %00000100; .....X..


Car_Rotation_Frame4:;RIGHT|LEFT;OK
   ;.byte %00000000
	.byte %00000000; ........
	.byte %11101110; XXX.XXX.
	.byte %01100100; .XX..X..
	.byte %11011111; XX.XXXXX
	.byte %11011111; XX.XXXXX
	.byte %01100100; .XX..X..
	.byte %11101110; XXX.XXX.
	.byte %00000000; ........

Car_Rotation_Frame5:;OK
   ;.byte %00000000
	.byte %00000100; .....X..
	.byte %00011100; ...XXX..
	.byte %11000111; XX...XXX
	.byte %11111111; XXXXXXXX
	.byte %01011110; .X.XXXX.
	.byte %01100011; .XX...XX
	.byte %00110010; ..XX.X..
	.byte %00110000; ..XX....

Car_Rotation_Frame6:
   ;.byte %00000000
	.byte %00010000; ...X....
	.byte %00110110; ..XX.XX.
	.byte %00011110; ...XXXX.
	.byte %11011100; XX.XXX..
	.byte %11011011; XX.XX.XX
	.byte %01000010; .X....X.
	.byte %01111000; .XXXX...
	.byte %00011000; ...XX...

Car_Rotation_Frame7:
   ;.byte %00000000
	.byte %00101100; ..X.XX..
	.byte %00111100; ..XXXX..
	.byte %01011111; .X.XXXXX
	.byte %00011010; ...XX.X.
	.byte %11011010; XX.XX.X.
	.byte %11101000; XXX.X...
	.byte %00111100; ..XXXX..
	.byte %00001100; ....XX..



Car_Rotation_Frame8:;DOWN (ACTUAL)
   ;.byte %00000000
	.byte %00011000; ...XX...
	.byte %01011010; .X.XX.X.
	.byte %01111110; .XXXXXX.
	.byte %01011010; .X.XX.X.
	.byte %00011000; ...XX...
	.byte %01100110; .XX..XX.
	.byte %01111110; .XXXXXX.
	.byte %01011010; .X.XX.X.

I then have a lookup table that provides adjusted pointers to these frames 
for each rotational value:

Car_Rotation_Sequence:
	;NO FLIP BIT - FACE RIGHT
	.byte <Car_Rotation_Frame0 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;0  - point up
	.byte <Car_Rotation_Frame1 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;1  -
	.byte <Car_Rotation_Frame2 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;2  -
	.byte <Car_Rotation_Frame3 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;3  -
	.byte <Car_Rotation_Frame4 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;4  - point right
	.byte <Car_Rotation_Frame5 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;5  -
	.byte <Car_Rotation_Frame6 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;6  -
	.byte <Car_Rotation_Frame7 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;7  -
	.byte <Car_Rotation_Frame8 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;8  - point down
	; WITH FlipThreshold - FACE LEFT
	.byte <Car_Rotation_Frame7 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;9  -
	.byte <Car_Rotation_Frame6 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;10 -
	.byte <Car_Rotation_Frame5 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;11 -
	.byte <Car_Rotation_Frame4 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;12 - point left
	.byte <Car_Rotation_Frame3 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;13 -
	.byte <Car_Rotation_Frame2 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;14 -
	.byte <Car_Rotation_Frame1 - TOMBSTONE_ROWS*TOMBSTONE_HEIGHT - 
CAR_HEIGHT;15 -


The logic is then relatively simple to figure out which shape to load and 
whether or not to reflect the sprite (rotation > 8).  My implementation 
could use some optimization.  But in theory, it's simple.

Then the shape is brought in via indirect Y loads.  Because of Thomas' 
kernel it's possible to do this and maintain the dyanmic playfield display, 
whereas the amount of work in the Indy 500 or Combat kernel is 
significantly less involved.  I don't think they really needed the speed 
boost of using RAM to store the current frame.  Plus, since during the 
design of the 2600 they were flirting with 64-byte variations of the 6502, 
16 bytes would have eaten up a 1/4 of the system RAM right there!

I'm just wondering if anyone knew a distinct advantage in the Combat 
approach?  It seems that REFPx was tailor-made to save ROM for rotating 
sprites.  I'm not sure why Jay Miner decided to put that feature in if it 
wasn't going to be better utilized by the initial crop of games.  All you'd 
have to do is reserve 8 more bytes in Indy 500 or Combat to complete the 
top-half of the rotation and you wouldn't have needed it at all.

Archives (includes files) at http://www.biglist.com/lists/stella/archives/
Unsub & more at http://stella.biglist.com

Current Thread