Jump to content

Photo

Introduction: 2600 Programming for Newbies


171 replies to this topic

#101 Moose1900 OFFLINE  

Moose1900

    Space Invader

  • 13 posts

Posted Sat Dec 5, 2015 10:49 AM

 

The X register is being used to keep track of which row of the Arena to draw.  Your new sprite routine is trampling all over the value of the X register.

oh, ok, so then I need to use two separate registers, one for the sprite and one for the playfield.  Is that right?



#102 SpiceWare OFFLINE  

SpiceWare

    Draconian

  • 12,467 posts
  • Medieval Mayhem
  • Location:Planet Houston

Posted Sat Dec 5, 2015 11:03 AM

oh, ok, so then I need to use two separate registers, one for the sprite and one for the playfield.  Is that right?


Normally you use the same register for the sprite that you do for your scanline counter. That does require the use of zero page pointers for the sprite graphics. In Collect those are Player0Ptr and Player1Ptr.

Have you finished going thru the Collect blog series?  Later on in the series the X register is replaced with RAM variable ArenaIndex, which would have let you use X like you did.  Namely:
 
ArenaLoop:
  tya
  and #%11
  bne SkipX
  inc ArenaIndex <-- this used to be inx
...
  ldx ArenaIndex
  lda ArenaPF0,x
  sta PF0
  ...


#103 Omegamatrix OFFLINE  

Omegamatrix

    Quadrunner

  • 6,215 posts
  • Location:Canada

Posted Sat Dec 5, 2015 12:15 PM

Wow- that's really advanced.  Pushing processor status on to the stack?  I'm not sure why that would work since all I know is the stack keeps track of addresses for subroutines. 

 

Now, if I could only figure out why that ball isn't moving...

The status register holds all of the flags for the processor. When you look in Stella's debugger this is it:

 

status.jpg

 

 

That register holds a bunch of individual bits. A bit can either be 0 or 1 (high or low). The lowercase letters represent 0's, and the uppercase represent 1's. The letter that we are interested in here is the 'z' (z-flag) which when you look at it matches the position of the bit used in the enable registers. 

 

status2.jpg

 

 

ENAxx.jpg

 

 

Pushing the processor reads the contents of the status register and writes it to where the stack pointer is indexed to. If the stack pointer is pointing to the ENABL register ($1F) then the PHP (Push Processor) will set or clear ENABL with its z-flag. The other bits are ignored because the ENAxx register doesn't use them. Also the PHP instruction decrements the stack pointer by 1 each time it's used. So if the stack pointer was pointing at ENABL ($1F) and PHP was used, then the stack pointer would then be pointing at ENAM1 ($1E). This makes it easy to set up a routine that will set ENABL, ENAM1, and ENAM0 sequentially.

 

 

Finally, about the z-flag. It stands for zero flag. It gets set 'Z' or cleared 'z' a few different ways. In this particular example we are concerned with setting or clearing it with a compare instruction. With a compare the z-flag will set when the value compared to matches exactly, and cleared otherwise.

 

Here's a little bit of a code using this trick from a game I'm converting:

; Earlier in the code, set up stack pointer
    ldx    #ENABL
    txs


; Y holds the current scanline
    cpy    ball_Line   
    php              ; ENABL, SP decrements from $1F to $1E (ENAM1)
    cpy    m1_Line
    php              ; ENAM1, SP decrements from $1E to $1D (ENAM0)
    cpy    m0_Line
    php              ; ENAM0, SP decrements from $1D to $1C

By using a few ram registers and throwing this all in a loop, you have a fast way of setting or clearing all the enable registers. If this all seems convoluted just remember you only have 76 cycles per line, and that can fill up quickly. This routine is fast. 



#104 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Sat Dec 5, 2015 1:51 PM

Thanks.. Its making some sense but:

 

Are you saying some of the locations the stack pointer runs through are registers for graphics and other stuff?  I guess I imagined that it only looks at program code locations.

 

I think I have a good idea of the processor status register, but can't grasp why we would have Z the way we wanted for D1 for ENABL.



#105 Omegamatrix OFFLINE  

Omegamatrix

    Quadrunner

  • 6,215 posts
  • Location:Canada

Posted Sat Dec 5, 2015 2:26 PM

The stack pointer can be anywhere from $00 to $FF. Normally use of the stack pointer is to initialize it to a known ram location (usually $FF) and leave it alone. Every time you jump to subroutine (JSR) the return address gets pushed on the stack and the stack pointers decrements for each byte pushed on the stack. The addresses are two bytes, so the stack pointer would go from $FF to $FD when you do a JSR. When you do a return from subroutine (RTS) the address you return to is pulled from the stack. The stack pointer 'points' to the location to get the return address.

 

As to where the stack pointer can be, locations $80 to $FF are the zero page RIOT ram and $00 to $2F are one of the mirrors of the TIA registers. The stack pointer can point to anywhere in that space. In this case we are deliberately setting it up to ENABL to perform this trick.

 

 

In the code from the previous post, the idea is to have Ball, M0, and M1 appear for 1 scanline wherever we want on the screen. By using a separate ram register for each we have made the lines independent for each object. We could have M0 on line 56, the ball on line 130, and so on. The code is meant to be put in a loop that draws the kernel allowing this to happen.

 

 

Each object will only be 1 scanline in height. To make them taller the code becomes longer. This I won't show here, but if you look at Star Wars Arcade, the crosshairs and stars are all set with this trick. The cross hairs are several scanlines in height, but the stars are all 1 scanline in height.

 

 

Back to how it works with the Z flag... you use a compare instruction, in this case CPY. The Y register in this example is holding the current scanline number and will either get decremented or incremented (your choice). In the ram location you are comparing it to is the scanline you want the particular object to appear on. When Y is the same as that value the Z-flag goes high. When it is any other scanline the z-flag is low.

 

 

What we are doing with PHP is taking the contents of the status register (which includes the Z-Flag in the D1 bit position) and writing it to ENAxx. On every line that we do that we are either setting or clearing ENAxx.



#106 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Sat Dec 5, 2015 2:57 PM

Awesome.  I get it.  Thanks!  My little games I've been trying to write always run into kernel issues so I'm sure it will come in handy.



#107 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Sun Dec 6, 2015 7:22 AM

Good Morning..  Can anyone explain this a little bit?  I've never seen the DCP command before (looks like it means decrement and compare but I can't find documentation on it) Also, I have no idea how the .byte even does anything here.  I thought .byte just placed data in memory.

 

        lda #HUMAN_HEIGHT-1 ; 2 15 - height of the humanoid graphics, subtract 1 due to starting with 0

        dcp HumanDraw       ; 5 20 - Decrement HumanDraw and compare with height
        bcs DoDrawGrp0      ; 2 22 - (3 23) if Carry is Set, then humanoid is on current scanline
        lda #0              ; 2 24 - otherwise use 0 to turn off player0
        .byte $2C           ; 4 28 - $2C = BIT with absolute addressing, trick that
                            ;        causes the lda (HumanPtr),y to be skipped


Edited by BNE Jeff, Sun Dec 6, 2015 7:23 AM.


#108 SpiceWare OFFLINE  

SpiceWare

    Draconian

  • 12,467 posts
  • Medieval Mayhem
  • Location:Planet Houston

Posted Sun Dec 6, 2015 10:05 AM

Good Morning..  Can anyone explain this a little bit?  I've never seen the DCP command before (looks like it means decrement and compare but I can't find documentation on it) Also, I have no idea how the .byte even does anything here.  I thought .byte just placed data in memory.


Both are answered in the comments of Step 4 of the Collect series.

#109 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Sun Dec 6, 2015 11:06 AM

Thanks!

 

Is it fair to say that the BITabs doesn't really produce anything useful here?  The instruction causes consistent cycles, and also performs a mini-branch, but whatever value is the result, is not used- correct?



#110 Omegamatrix OFFLINE  

Omegamatrix

    Quadrunner

  • 6,215 posts
  • Location:Canada

Posted Sun Dec 6, 2015 11:33 AM

Thanks!

 

Is it fair to say that the BITabs doesn't really produce anything useful here?  The instruction causes consistent cycles, and also performs a mini-branch, but whatever value is the result, is not used- correct?

BIT is used because it won't trash A when you are skipping over the next instruction. In the DoDraw code you posted it doesn't matter what BIT does, but in general everyone has to be aware of what flags an instruction affects. I always use this page for a 6502 instruction set. You can see from that page that BIT affects the N, V, and Z flags.

 

 

You will sometimes see people use the illegal NOP's instead of BIT for this skipping purpose, because NOP affects no flags. The absolute NOP instruction is $0C.



#111 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Mon Dec 7, 2015 6:34 PM

Thanks- I like that instruction link a lot...

 

I think I could use some help figuring out why my ball doesn't move.  In my start up code, I have:

 

 LDA #1
 STA CTRLPF
 STA HMBL ;Ball Speed

 

Then in the main loop I have:

 

 LDA #2
 STA VSYNC 
 STA WSYNC 
 STA WSYNC  
 STA WSYNC
 STA HMOVE ;Move Ball

 

and this is in the kernel:

 

CheckBall
     CPY Ball
     BNE NoBall
     LDA #2
     STA ENABL

NoBall

 

Does anything look wrong?  Its an HMOVE immediately after a WSYNC, 1 is a legitimate speed for HMBL.  The ball shows up (sort of) but doesn't move..

 

Thanks!



#112 LS_Dracon ONLINE  

LS_Dracon

    Dragonstomper

  • 767 posts

Posted Mon Dec 7, 2015 7:38 PM

The 4 bits used for hmove are in the left nibble.

Helps if you write in hexadecimal (#$):

 

LDA #1
STA CTRLPF

LDA #$10                    ;%00010000
STA HMBL ;Ball Speed

 

Remember, once you write a HMOVE value, it keeps until you set HMCLR (hmove clear)

I set HMCLR every frame, so I aways know the frame starts with all HMOVE as 0. 



#113 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Tue Dec 8, 2015 4:22 AM

Right there on page 41.  I can't believe I made basically the same mistake as I did when I tried to ENABL... I checked the HMOVE register over and over, but not HMBL..  Thank you.



#114 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Sat Dec 19, 2015 6:39 AM

Good Morning,  I'm looking through some code and found some commands I don't recognize.. what are these Basic-looking commands?  They're not really indented which makes me think they are labels for a subroutine, but they repeat which makes me think they can't be.

 

Random:
        lda Rand8
        lsr
 ifconst Rand16
        rol Rand16    
 endif
        bcc noeor
        eor #$B4
noeor
        sta Rand8
 ifconst Rand16
        eor Rand16     
 endif
        rts  



#115 SpiceWare OFFLINE  

SpiceWare

    Draconian

  • 12,467 posts
  • Medieval Mayhem
  • Location:Planet Houston

Posted Sat Dec 19, 2015 9:30 AM

Good Morning,  I'm looking through some code and found some commands I don't recognize.. what are these Basic-looking commands?

 
While I don't specifically point it out, there's comments in the code of step 10 of the Collect series which explain what those do.  It's also covered in dasm.txt, the manual that comes with dasm, under the section titled PSEUDOPS:
 

They're not really indented which makes me think they are labels for a subroutine, but they repeat which makes me think they can't be.


A single space is all that's required to indent something. When using jEdit, the different syntax coloring of inconst and endif helps to reinforce that they're not subroutine labels:
Screen Shot 2015-12-19 at 9.25.38 AM.png



#116 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Sun Dec 20, 2015 4:31 PM

I guess I'm pretty lost on this one and might have to drop it for now.  I read through half of the DASM.txt and so much of it was above my head that I wasn't getting anything out of it.  I read Collect #10 and I could follow, except for the terms "endif, and "ifconst", and the rest that I was specifically looking for- just not getting it. I could only figure that they were used in conjunction with the LFSG.  I did learn what LFSG was- pretty cool.. and downloaded JEdit which I figured I'd have to start using eventually anyway.  (It says its an editor for the "mature" programmer. uh-oh..)

 

My idea after completing Collect #4 with some difficulty was to just go ahead and read through one of your more complete versions of a Collect assembly to get a sense of as many parts as possible of the entire program.  I've been really enjoying that and learning a lot..

 

Thanks for the help!



#117 SpiceWare OFFLINE  

SpiceWare

    Draconian

  • 12,467 posts
  • Medieval Mayhem
  • Location:Planet Houston

Posted Sun Dec 20, 2015 10:25 PM

I guess I'm pretty lost on this one and might have to drop it for now


I've family in town thru Tuesday, will go into this after they've left.

#118 SpiceWare OFFLINE  

SpiceWare

    Draconian

  • 12,467 posts
  • Medieval Mayhem
  • Location:Planet Houston

Posted Mon Dec 21, 2015 9:45 AM

I guess I'm pretty lost on this one and might have to drop it for now.


Everybody was still asleep, so I added a comment to Collect step 10 that might help.



#119 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Mon Dec 21, 2015 5:07 PM

Ahh..  Now it makes sense.  Thanks. So, why write it this way?  Is it to make it a sort of all-purpose code that you can paste into any program?



#120 SpiceWare OFFLINE  

SpiceWare

    Draconian

  • 12,467 posts
  • Medieval Mayhem
  • Location:Planet Houston

Posted Mon Dec 21, 2015 11:54 PM

Is it to make it a sort of all-purpose code that you can paste into any program?


Yep

#121 Moose1900 OFFLINE  

Moose1900

    Space Invader

  • 13 posts

Posted Tue Dec 22, 2015 3:19 PM

Does anyone have a working sample program that includes a skipdraw routine?  I have seen many postings of the code for just the routine, but never a simple program that draws a sprite on the screen using skip draw.  I have tried for the past few weeks, but can't seem to get it to work. I think if I saw a simple example that works, then I could see where I am going wrong.  Any help would be much appreciated.

 Thanks.



#122 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Wed Dec 30, 2015 7:49 PM

Moose.. I'm not sure if it will help, but I believe  Spiceware's Collect program uses DoDraw which looks identical to SkipDraw to me (though its supposed to be different in some way.)

 

There's an explanation in the comments at Step 4:

http://atariage.com/...cat-188-collect


Edited by BNE Jeff, Wed Dec 30, 2015 7:50 PM.


#123 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Wed Dec 30, 2015 7:57 PM

Good evening..

 

I finally downloaded Makewav, created a file for my Starpath Supercharger, played one of my little programs on an actual Atari, and it worked!  However, all of the colors were completely wrong.  The black background was orange, and the gray playfield was black.  Anybody know why that might happen?



#124 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Sun Jan 3, 2016 6:49 AM

Does anyone have any tips on adding variable playfield graphics to a kernel?  I'm trying to have a prize (just a dot) show up in a random location using PF1 and can't figure out how to get the kernel deal with it.  I've thought about decrementing and comparing a memory location or displaying an entirely new playfield just to show the one dot but those ways seem over-complicated and/or wrong.  Note: my playfield is otherwise blank in the prize location.

 

Thanks!



#125 BNE Jeff OFFLINE  

BNE Jeff

    Moonsweeper

  • 344 posts
  • Location:$5787

Posted Sat Apr 9, 2016 7:12 AM

Good Morning!

 

I had to put this (post above) aside for a bit while finishing up a certification I needed for work..  I decided to start by just having the kernel look to a single RAM location and then storing it in PF1.  I've been working at it all morning and I just cant figure out why nothing shows.  Here is the relevant code (Note this started as Darrell Spice's "Collect"):

 

​Here is where I set aside 6 bytes for 6 or more prizes:

 

PillPF:            ds 6    ; stored in $9D,9E,9F,A0,A1,A2

 

Here is where I put in a test value:

 

InitSystem:
    ; CLEAN_START is a macro found in macro.h
    ; it sets all RAM, TIA registers and CPU registers to 0
        CLEAN_START   
     

        lda #%10101010    ;added to test if pills will display  
        sta PillPF                ;added to test if pills will display

 

And here is what is in the kernal.  It's supposed to use PillPF,0 to make 2 sets of stripes on the screen.  It doesn't show up:

 

    ; start of line 1 of the 2LK
        sta GRP1            ; 3  3 - @0-22, update player1 graphics
        stx ENAM1           ; 3  6 - @0-22, update missile1 graphics
        ldx ArenaIndex      ; 3  9
        lda ArenaPF0,x      ; 4 13 - get current scanline's playfield pattern
        sta PF0             ; 3 16 - @0-22 and update it
        lda PillPF         ; 4 20 - get pill PF .  Also deleted offset
        sta PF1             ; 3 23 - @71-28 and update it
        lda ArenaPF2,x      ; 4 27 - get current scanline's playfield pattern
        sta PF2             ; 3 30 - @60-39

 

Any ideas?






0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users