Jump to content

vidak's Blog

  • entries
  • comments
  • views

Guerrilla Game #5.2



I Problems

One thing doesn't add up in my analysis of how the Y Register in the Stay Frosty kernel is formed. This code forms the Heights variable:

        ldy #SECTIONS*DATA_BYTES_PER_SECTION-4-1+2+2        ; -4 for the 4 unused platform bytes        ; +2 for fireball size info        ; +2 for level control init.plLoop                lda (LevelDataPtr),y        sta Heights,y        dey        bpl .plLoop

The SECTIONS constant equals 5. The DATA_BYTES_PER_SECTION constant equals 11. Cancelling out the minor adjustments, the first LDY operation should load Y with 54. As we know from the previous blog post, the LevelDataPtr is pointing to the Level1 label in ROM.

The first inconsistency is this: There is only
bytes of information apart from game logic in those sections of ROM.

The second inconsistency with my analysis is: there are only 6 or 7 bytes of information in ROM under those labels that could be construed as the Height of things.

The third inconsistency is this: There are only 5 bytes set aside for Heights in the RAM. This equals the number of sections of Height that the variable is supposed to store. The STA operation in the above code is using Absolute Indexed addressing. This means the Y register is used to increment the
memory location
relative to Heights, not the
in the Heights memory location.
This means
if we increment the memory location more than 5 times we are starting to store data in
other memory labels than Heights

I am thoroughly confused. If anyone can help me understand this, I would be very grateful. I feel as if I don't understand the order of operations being done with the constants in the first LDY operation. I also don't understand what DATA_BYTES_PER_SECTION refers to. I cannot find any groups of 11 bytes of data anywhere.

I know there are 4 unused bytes for drawing the platforms. Anyway let's continue attempting to understand how the kernel is drawn.

II The Actual Pointer

Remember, this is how the kernel is drawn:

        ldy Heights+.SEC             ...        lda (FrostyImagePtr+.SEC*2),y        and (FrostyMaskPtr+.SEC*2),y         sta GRP0                             lda (FrostyColorPtr+.SEC*2),y        sta COLUP0                   

We need not worry about the strange loading of 54 bytes of information into the memory location relative to Heights, because only the data in the first 5 bytes relative to Heights is loaded into the Y Register. Heights is only set out as 5 bytes large, and the first 5 bytes of ROM contain the section heights, and the .SEC pseudo-variable only increments Heights by 5 bytes of memory addresses.

A FrostyImagePtr

The first element of the kernel we need to trace is the Image Pointer. This pointer contains the 16 bit memory address that contains the main player graphics. How is this pointer formed?

LINE 1975:        SET_POINTER FrostyImagePtr, FrostyGraphic

It is formed with the macro SET_POINTER. All of this code is executed in Vertical Blank. You can find the set of instructions which corresponds to SET_POINTER in MACRO.H:

            MAC SET_POINTER.POINTER    SET {1}.ADDRESS    SET {2}                LDA #<.ADDRESS  ; Get Lowbyte of Address                STA .POINTER    ; Store in pointer                LDA #>.ADDRESS  ; Get Hibyte of Address                STA .POINTER+1  ; Store in pointer+1            ENDM

There are two arguments for this macro. The first argument is the pointer variable in memory. The second argument is the memory address. What this macro does is store the 16 bit memory address location inside the 2 bytes of pointer memory. Simple enough to understand.

For your reference, this is what exists at the memory address we have now stored inside the FrostyImagePtr: FrostyGraphic is a label in ROM:

LINE 986:        .byte zz__XXXX__ ; 0        .byte zz_XXXXXX_ ; 1        .byte zzXXXXXXXX ; 2        .byte zzXXXXXXXX ; 3        .byte zzXXXXXXXX ; 4        .byte zzXXXXXXXX ; 5        .byte zzXXXX_XXX ; 6        .byte zz_XXXXXX_ ; 7        .byte zz__XXXX__ ; 8        .byte zz_XXXXXX_ ; 9        .byte zz_XXX_XX_ ; 10        .byte zz_XXXXXX_ ; 11        .byte zz_XXXXXX_ ; 12        .byte zz_XXX_XX_ ; 13        .byte zz__XXXX__ ; 14        .byte zz___XX___ ; 15        .byte zz__X__X__ ; 16        .byte zz__XXXX__ ; 17        .byte zz__X_X___ ; 18        .byte zz__XXXX__ ; 19        .byte zz___XX___ ; 20        .byte zz__XXXX__ ; 21        .byte zz___XX___ ; 22        .byte zz___XX___ ; 23        .byte zz___XX___ ; 24        .byte zz___XX___ ; 25FrostyGraphic

There is some code directly underneath the SET_POINTER macro call:

LINE 1979:                  sec                  lda FrostyImagePtr                  sbc FrostyY                  sta FrostyImagePtr                  lda FrostyImagePtr+1                  sbc #0                  sta FrostyImagePtr+1

This adjusts the line of graphics we load into the kernel. The variable FrostyY stores the vertical position of the main player character on the screen. So what we are doing is adjusting the line of graphics we load into the kernel relative to what FrostyY contains. What happens in this game is that Frosty--the main player character--melts. As Frosty melts, we load less and less of the player graphics as Frosty sinks into the ground.

The above setting of the pointer is sufficient for a game that does not have a kernel which is repeated in bands down the screen. Because we have a game with bands, we need to adjust the graphics pointers. If we do not adjust the pointers for the main player character, it will only be visible on a single band. The main player character needs to (a) be visble in different bands; and (b) be visible moving between bands. I assume this can all be achieved by adjusting the pointers relative to the values of the heights of the different bands up and down the screen.

This is how SpiceWare prepared the graphics pointer for Sections 1-4. This means the main player character must appear at Section 0 at the beginning of the game, and is the origin for the Y position of the main player character.

LINE 2006:        ldx #0        ldy #0pfLoop                clc                             lda FrostyImagePtr,y            adc Heights,x                   iny ; 1                         iny ; 2                         sta FrostyImagePtr,y   ; Graphics Address (0) + Heights -> Graphics Address + 2        dey ; 1                        lda FrostyImagePtr,y   ; LDA Graphics Address + 1        adc #0        iny ; 2        iny ; 3        sta FrostyImagePtr,y   ; Graphics Address + 1 -> Graphics Address + 3<Repeated Process for the Mask and Colours here>        dey ; 2        inx                cpx #SECTIONS-1        bcc pfLoop

I think what is happening in the above code is this:

  1. We loop through this above code 5 times - as many times as there are bands up the screen. (5 times)
  2. Everytime we move through the loop, the Y Register increases by 2. So the 5 times we move through the loop Y increases through these values:
  3. 0 -> 2, 1 -> 3
  4. 2 -> 4, 3 -> 5
  5. 4 -> 6, 5 -> 7
  6. 6 -> 8, 7 -> 9
  7. 8 -> 10, 9 -> 11

  • This means we are increasing the graphics pointer memory address by 2 every time--both the lower byte of the pointer, and the higher byte. We are shifting the line of graphics by 2 up every band of the screen. This is confusing - why do we need to do this?
  • What seems to be happening to the lower byte is that we increment its memory address by the number of bytes equal to the height of the band. Why don't we also do this to the higher byte? Wouldn't this lead to the lower byte increasing something like 30+2 locations forward, where the higher byte only moves 2 locations forward?
  • I need some clarification on the above issues.

    The final process of forming the main player's graphics pointers is tweaking the pointers so that the memory addresses they point to don't cross any page boundaries. When the kernel uses the LDA (ZP),Y operation in the kernel, it needs to take a precise amount of time. If the CPU has to cross a page boundary in memory in order to fetch the data at the pointer's address, it will add a cycle and disrupt the very precise timing of the kernel.
    LINE 2059:;+-------------------------+;| tweek Frosty's pointers |;| so no page breaks occur |;+-------------------------+        ldy #1MaskLoop                lda FrostyMaskPtr,y    ; Load higher byte of Mask Pointer        cmp #>BlankGraphic     ; CMP with higher byte of BlankGraphic        beq MaskCheck2         ; If it is blank then branch        lda #>BlankGraphic     ; If not, load higher byte of BlankGraphic        sta FrostyMaskPtr,y             sta FrostyImagePtr,y        sta FrostyColorPtr,y   ; ...and store it into the higher byte of the pointers.        dey                    ; This blanks them all out.        lda #0        sta FrostyMaskPtr,y        sta FrostyImagePtr,y        sta FrostyColorPtr,y   ; ...blank out all of the lower bytes of the pointers as well        iny        bne NextMask           ; Always branchMaskCheck2        dey        lda FrostyMaskPtr,y        cmp #<FrostyMask        bcc NextMask2          ; Branch if lower byte of Mask Pointer (less or) equal to lower byte of FrostyMask        lda #0                         sta FrostyMaskPtr,y        sta FrostyImagePtr,y        sta FrostyColorPtr,y   ; ...if not (higher than) blank out lower byte of pointers        iny        lda #>BlankGraphic        sta FrostyMaskPtr,y        sta FrostyImagePtr,y        sta FrostyColorPtr,y   ; ...and blank out the higher byte as well        bne NextMask           ; Always branch?NextMask2                inyNextMask        iny        iny        cpy #SECTIONS*2       ; Have we gone through the loop 10 times? (5 x 16 bits worth of memory addresses)        bcc MaskLoop          ; If we haven't, keep looping!

    This is about all I can achieve for now...


Recommended Comments

There are no comments to display.

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.

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...