Jump to content

Photo

Need info on zero page loading of index registers


11 replies to this topic

#1 Engeliron OFFLINE  

Engeliron

    Space Invader

  • 16 posts

Posted Tue Mar 8, 2016 10:37 PM

Greetings all, I'm confused about zero page loading after seeing a few examples of code.  The first is from Kirk Israel's subpixel positioning code called "Milquetoast the ghost" and the second from Minidig's advanced coding 101 of the "Showing Missiles/Ball using PHP.

On milquetoast after a org $F000 there is a constant called C_KERNAL_HEIGHT and is shown as C_KERNAL_HEIGHT = 192.  It is called later with ldy #C_KERNAL_HEIGHT - 1.  This should load a literal value of 192 -1 (or 191) into the Y register.  Also in the code is org $FEC0 and below that is GhostGraphic followed by 8 designated and defined .bytes.  So when a lda #<GhostGraphic is called then the accumulator should have a literal value of "C0" in it.

Then moving to the Showing Missiles/Ball using PHP they use a ldx #ENAM1+1 followed by txs to set the stack pointer to the address of $1F before the push.

I am confused by these examples.  I can see how a  ldy #C_KERNAL_HEIGHT - 1 puts a literal value into Y of 191 but the other 2 I don't get.

How does the ldx #ENAM1+1 make the value in X to be a literal value of $1F, which is its address. Shouldn't it load into x whatever literal value was stored in the enable missile register plus a literal value of #1?  

How can you load an address into a register when you have a # in front?  

Any info would be greatly appreciated, thanks. 



#2 cd-w OFFLINE  

cd-w

    Stargunner

  • 1,473 posts
  • Juno First!
  • Location:Glasgow, UK

Posted Tue Mar 8, 2016 11:38 PM

The # is evaluated at compile (assembly) time so it cannot read the value of a memory address - all it does is take the literal value of the symbol.

Chris

Edited by cd-w, Tue Mar 8, 2016 11:41 PM.


#3 Engeliron OFFLINE  

Engeliron

    Space Invader

  • Topic Starter
  • 16 posts

Posted Wed Mar 9, 2016 12:05 AM

OK, so does that mean that somewhere at the beginning of the code you load the value, for example, $1F into ENABL, $1E into ENAM1, and $1D into the registers.  Then, using the Showing Missiles/Ball using PHP example, when the ldx #ENAM1+1 occurs.  The compiler sees that the X register has a literal value of $1E + $01 which equals $1F into it.  Then takes that literal value to set the stack pointer.  I hope I got that right?



#4 cd-w OFFLINE  

cd-w

    Stargunner

  • 1,473 posts
  • Juno First!
  • Location:Glasgow, UK

Posted Wed Mar 9, 2016 12:13 AM

Yes - look in vcs.h

#5 Engeliron OFFLINE  

Engeliron

    Space Invader

  • Topic Starter
  • 16 posts

Posted Wed Mar 9, 2016 12:30 AM

It's starting to make sense for me.  When looking in the vcs.h, ENABL is written ENABL ds 1.  I  thought that its address was at $1F and it had no literal value in the header file until a write to it somewhere later on in ROM (aka after org $F000).  Or does a person have to write lda #$1F then sta ENABL for the stack push example?



#6 Engeliron OFFLINE  

Engeliron

    Space Invader

  • Topic Starter
  • 16 posts

Posted Wed Mar 9, 2016 12:47 AM

Sorry, I think I answered my own question there.  You've been very helpful, thanks.

If I could pick your brain on one more question regaring zero page loading. 

It's in the milquetoast example which is coded:

   org $FEC0

GhostGraphic

     .byte #%xxxxxxxx

     .byte #%xxxxxxxx

     .byte #%xxxxxxxx

     .byte #%xxxxxxxx

     .byte #%xxxxxxxx

     .byte #%xxxxxxxx

     .byte #%xxxxxxxx

     .byte #%xxxxxxxx

 

The x's are just some random 1's or 0's.

In the code prior to this is a lda #<GhostGraphic

I can see that 'a' gets the 'C0' value but don't quite understand how that works and not getting one of the .byte literal values loaded into the accumulator. I don't quite understand how it gets it's value when dealing with arrays. Thats the last thing that is confusing for me.



#7 Nukey Shay ONLINE  

Nukey Shay

    Sheik Yerbouti

  • 21,549 posts
  • Location:The land of Gorch

Posted Wed Mar 9, 2016 3:02 AM

Again, you are confusing labels (like GhostGraphic) with values (like the bitpattern stored at the address specified by the GhostGraphic label). LDA#<GhostGraphic is putting the LSB of the label's address into the accumulator. Since the pattern table exists at address $FEC0, the LSB is $C0. If you were trying to read the contents of the bitpattern, you'd be using something like LDA GhostGraphic (notice the exclusion of the # character?). You didn't post enough code to be sure, but chances are your example is setting up a pointer in ram memory to that bitpattern table so the graphic can be called via (indirect),y addressing. The way that this works is you'd set the address of the pattern table into 2 consecutive ram locations...LSB first (for example...#$C0 into ram $80, and #$FE into ram $81). Then you'd use the Y register to read that table's contents. Supposing Y held the value of zero...LDA($80),Y would put the very first bitpattern value into the accumulator. If Y held the value of 1, the second bitpattern value would end up in the accumulator...and so on. This makes it easy to read the entire shape using a very small loop of code...reading the values one-by-one and passing them to a graphic register via STA (as in STA GRP0). Likewise, the Y register can be set to read from a higher or lower position in the pattern table by simply adjusting the LSB beforehand (which is at ram address $80 in my example). Take care that you do not confuse (indirect),Y addressing with (indirect,X) addressing...which operates on a different principle. The latter is referencing a select number of indirect pointers from RAM as indexed by X.

Edited by Nukey Shay, Wed Mar 9, 2016 3:08 AM.


#8 Nukey Shay ONLINE  

Nukey Shay

    Sheik Yerbouti

  • 21,549 posts
  • Location:The land of Gorch

Posted Wed Mar 9, 2016 3:03 AM

Short example code section to illustrate (indirect),Y addressing which draws a square or circle to player sprite 0 depending on the left player's joystick trigger status...
Example:
      LDA #>Shapes     ;get MSB of table
      STA $81          ;store at indirect pointer + 1
      LDA #<Square     ;setup to draw a square
      LDY INPT4        ;check trigger of left stick
      BMI Not_Pressed  ;branch if not currently pressed
      LDA #<Circle     ;otherwise, setup to draw a circle
Not_Pressed:
      STA $80          ;store LSB at indirect pointer
      LDY #$06         ;set Y index to do 7 passes in the loop below
                       ;SEVEN because we will be using pass #zero too
Loop_Start:
      STA WSYNC        ;start a new scanline
      LDA ($80),Y      ;read bitmap value at indirect pointer address + Y
      STA GRP0         ;send it to Player0 sprite
      DEY              ;decrease Y index
      BPL Loop_Start   ;branch back up if Y has not rolled negative yet
      JMP Done         ;shape has been drawn, go elsewhere

Shapes:
Square:
      .byte %11111111
      .byte %10000001
      .byte %10000001
      .byte %10000001
      .byte %10000001
      .byte %10000001
      .byte %11111111
Circle:
      .byte %00111100
      .byte %01000010
      .byte %10000001
      .byte %10000001
      .byte %10000001
      .byte %01000010
      .byte %00111100

Edited by Nukey Shay, Wed Mar 9, 2016 3:52 AM.


#9 Nukey Shay ONLINE  

Nukey Shay

    Sheik Yerbouti

  • 21,549 posts
  • Location:The land of Gorch

Posted Wed Mar 9, 2016 5:21 AM

BTW regarding the missile/ball question, I strongly advise NOT using stack pointer manipulation if you are just learning assembly.  The "stack" keeps track of your subroutine return addresses, and it will take care of itself if you leave it alone.  The stack begins at the END of user ram and fills downward as it accumulates more addresses (the hardware set it up this way so it's less likely to run into your variables as you fill it up from the START of user ram).  The stack pointer keeps track of where in ram it's adding or removing more data...and jumping into advanced tricks too soon will just make a mess of things.

 

But anyway, the trick is that it is enabling or disabling the sprite if the result of the last test was EQUAL or NOT EQUAL respectively.  This result is part of the "status register", and resides in bit location 1 (remember this for a moment).  The status register is similar to the A, X, and Y registers except that you don't normally write data directly to it...it keeps track of the handful of things that various opcodes need to perform their function (like if the last addition resulted in carry being set).

 

Missile sprites and the Ball sprite have no bitpattern.  They can only be turned on or off.  To turn one of them on, you'd write a value which has this status in bit 1's position to the register (sound familiar?).  Value 0 or 1 = disable, 2 or 3 = enable, 4 or 5 = disable, 6 or 7 disable, etc.  This is best visible in binary:

0 = %00000000 disable
1 = %00000001 disable
2 = %00000010 enable
3 = %00000011 enable
4 = %00000100 disable
5 = %00000101 disable
6 = %00000110 enable
7 = %00000111 enable
...etc

See the pattern?  The only thing those sprite registers are interested in is just that second-to-last binary bit of the value you write to them.

 

So the "magic" part of the code is to set the stack pointer so that it no longer points at user ram...but the hardware registers that are used for these sprites.  This is done by setting X to the value of the sprite register (LDX #$1F for the ball), then using X to update the stack pointer (TXS).  Now if we compare our scanline counter (LDA scancount) with where we want the ball to appear (CMP ball_position)...tada - bit 1 in the status register has already been updated.  So using PHP (push processor status) throws the status register's value into the sprite's hardware address.  Whatever other bits were set or unset in the status register  does not matter to these guys...M0, M1, and BL are only interested in bit 1.  Better yet, PUSHING the value into the stack automatically rolls the stack address down by 1 (remember, the stack goes in reverse), so you can do the ball, M1, and M0 one after another without needing to reset the pointer in the meantime.

 

Again, this is a method that should not be used if you are just learning to code.  It's really just a space-saving technique...at least until your display kernel starts to become really complex and you are in dire need of machine cycles.



#10 SpiceWare OFFLINE  

SpiceWare

    Quadrunner

  • 10,854 posts
  • Medieval Mayhem
  • Location:Planet Houston

Posted Wed Mar 9, 2016 9:00 AM

Are you generating a listing and/or symbol dump when you compile your program?  That can help a lot in when trying to understand what's going on.  
 
The readme.txt file found in the source for Collect explains how to generate them:
Collect is a simple Atari 2600 game where the objective is
to collect randomly positioned boxes.

Use dasm to compile the program.  Dasm can be found here:
http://dasm-dillon.sourceforge.net

The command used to compile is:
dasm collect.asm -f3 -v0 -scollect.sym -lcollect.lst -ocollect.bin


the options after the source file are:
    -f3 sets output format to 3, RAW.

    -v0 sets verboseness.  Values are 0-4, see dasm documentation.

    -s requests a symbol dump, saved to specified file.  Stella uses the symbol
	dump in order to show your variable names in Stellas debugger.  The
	*.sym filename must match the *.bin filename for this to work.

    -l requests a detailed listing, saved to specified file.

    -o specifies the output file.  If not specified, the output file will be a.out

Besides the source file, only option -f3 is required to build a Atari 2600 program.
 
Collect is my in-depth tutorial on writing an Atari game from scratch - you might like to follow thru it, I put a lot of extra comments in the source to explain things that I normally wouldn't.

The symbol list is an alphabetical listing of all the symbols in your project. A subset of the output from the final version of Collect:
ENABL                    001f              (R )
ENAM0                    001d              (R )
ENAM1                    001e              (R )
Frame                    009c              (R )
GameState                00a9              (R )
GRP0                     001b              (R )
GRP1                     001c              (R )
HMBL                     0024                  
HMCLR                    002b                  
HMM0                     0022                  
HMM1                     0023                  
HMOVE                    002a              (R )
HMP0                     0020              (R )
HMP1                     0021                  
 
Anything with an (R ) is a referenced value - that means if you search your code you'll find that symbol used somewhere. So Collect uses ENAM0, but it doesn't use HMBL. Do note that just because a value isn't directly used, it might be indirectly used. Of the HMxx labels only HMP0 is referenced but HMP0, HMP1, HMM0, HMM1 and HMBL are all used in the PosObject function.


You might also like to check out my blog entries about jEdit.  Here's a summary topic about Using jEdit for 2600 development, it includes some screenshots showing of why you might like to use it.

#11 Engeliron OFFLINE  

Engeliron

    Space Invader

  • Topic Starter
  • 16 posts

Posted Wed Mar 9, 2016 12:24 PM

Thanks guys, that clears it up for me.  Trying to learn everything about 6502 assembly has required me to hunt around the internet a lot to get little snippets and then trying to piece it all together.  I am currently trying to program my first homebrew of a boardgame called the farming game and I think i am doing alright then something basic like what I was asking you guys comes along and I haven't a clue why it works, I just know the end result (which bothers me).

Can you guys recommend a book on 6502 assembly I should purchase that would give me an excellent foundation on the basics as well as some intermediate to advanced info?  



#12 SpiceWare OFFLINE  

SpiceWare

    Quadrunner

  • 10,854 posts
  • Medieval Mayhem
  • Location:Planet Houston

Posted Wed Mar 9, 2016 2:10 PM

I start off the Collect series with a pointer to Easy 6502 - it's an interactive tutorial that includes a 6502 assembler and simulator that runs in your browser. After you make changes to the examples just click Assemble and Run to see the results. I posted some detailed screenshots about it in this topic.

I also recommend you check out my Collect series - each entry has source you should download, review, and try making changes to. Also check out the comments for each blog entry - there's some good questions that I've answered that will also further your understanding. And don't hesitate to add comments with your own questions.




0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users