Jump to content
IGNORED

Another beginner project


Recommended Posts

Hi everybody! I started playing around with 2600 assembly today, trying to make a breakout clone first for practice. I've been following the guide so far but I haven't had much luck with the horizontal movement of sprites. I understand the concept of it (waiting until the electron gun is at a certain X, resetting sprite position there, HMOVE for finer tuning), but I couldn't get any of the examples I've found to work, so I'm trying to figure it out on my own for now.

 

My code is here:

https://github.com/Lana-chan/break2600

 

I haven't implemented HMOVE yet but the current code is used as a test to see what delays I can use with RESP0 to get the paddle to move.

 

I'd love to hear any comments and suggestions on my code! I've worked with Z80 assembly for Sega Master System before, though I never got too far with it, but it gave me a basic understanding of assembly programming in general.

Link to comment
Share on other sites

Check out my blog series Collect - the series starts at the bottom of page 2.

 

I hope you'll be using paddles instead of joystick. If so also check out my blog entries covering the development of Medieval Mayhem. For paddles you have to check the state of the paddle mutiple times during the kernel. I used these macros:

 

        MAC READ_PADDLE_1
        lda INPT0         ; 3   - always 9
        bpl .save         ; 2 3
        .byte $2d         ; 4 0
.save   sty Paddle1       ; 0 3
        ENDM

        MAC READ_PADDLE_2
        lda INPT1         ; 3   - always 9
        bpl .save         ; 2 3
        .byte $2d         ; 4 0
.save   sty Paddle2       ; 0 3
        ENDM

        MAC READ_PADDLE_3
        lda INPT2         ; 3   - always 9
        bpl .save         ; 2 3
        .byte $2d         ; 4 0
.save   sty Paddle3       ; 0 3
        ENDM

        MAC READ_PADDLE_4
        lda INPT3         ; 3   - always 9
        bpl .save         ; 2 3
        .byte $2d         ; 4 0
.save   sty Paddle4       ; 0 3
        ENDM


        MAC READ_PADDLE_1_OR_2
        ldx Paddles2Read  ; 13-14  3
        lda INPT0,x       ; |      4
        bpl .save         ; |      2 3
        .byte $2c         ; |      4 0
.save   sty Paddle1,x     ; |      0 4
                          ; +-14 worse case scenerio
        ENDM

        MAC READ_PADDLE_3_OR_4
        ldx Paddles2Read  ; 13-14  3
        lda INPT2,x       ; |      4
        bpl .save         ; |      2 3
        .byte $2c         ; |      4 0
.save   sty Paddle3,x     ; |      0 4
                          ; +-14 worse case scenerio

        ENDM

        MAC READ_TWO_PADDLES
        ldx Paddles2Read   ; 21-23  3
        lda INPT0,x        ; |      4
        bpl .save1         ; |      2 3
        .byte $2c          ; |      4 0
.save1  sty Paddle1,x      ; |      0 4
        lda INPT2,x        ; |      4
        bpl .save2         ; |      2 3
        .byte $2c          ; |      4 0
.save2  sty Paddle3,x      ; |      0 4
                           ; +-23 worse case scenerio
        ENDM
Link to comment
Share on other sites

I hope you'll be using paddles instead of joystick. If so also check out my blog entries covering the development of Medieval Mayhem. For paddles you have to check the state of the paddle mutiple times during the kernel.

I wanted to use the paddles but the paddle controller was never bundled with the Brazilian clones of the 2600, so it would make it impossible for me to try it on hardware if I ever managed to build an Atari cartridge. (I already built a homebrew SMS cartridge using an EEPROM once)

So I'm still thinking on what input to use.

 

If I'm using only paddle 1 to move a single object in the screen, why do you need to check it more than once per frame?

 

EDIT: So I'm having the same trouble with the routine linked by ZackAttack as I was having with the other routines, wherever in the frame I put the routine in, it just glitches out the screen with some intermittent beeps. I guess I'm not too clear on how to put this function into my code? I replaced the area in my code labeled "WaitPos" with that snippet.

 

EDIT 2: Nevermind, I fixed -that-, but the routine doesn't seem to be updating the horizontal movement properly. The code in the GitHub is updated with that routine, I'll include a binary here for convenience.

brick.bin

Edited by Svetlana
Link to comment
Share on other sites

If I'm using only paddle 1 to move a single object in the screen, why do you need to check it more than once per frame?

On other systems you can read a single register and get an 8 bit value that tells you how far the paddle (or analog stick) is turned. The Atari is much more primitive. Instead, the paddle controls how long it takes to recharge a capacitor (the capacitors are the Atari, not in the paddles - there's one capacitor per paddle). The INPTx registers are just 1 bit, not 8. The process is:

  • discharge the capacitors during Vertical Sync (this grounds the capacitors):

    LDA #$82

    STA VBLANK

  • Remove the ground at the start of the kernel so the capacitors can start recharging. Rate of charge is controlled by the paddle:

    LDA #$00

    STA VBLANK

  • During the kernel repeatedly check if the capacitor has fully recharged. The scanline you're on is the value for how far the paddle is turned.
Paddle games tend to have simpler graphics because you have to spend so much time during the kernel to read the state of the paddles. In Medieval Mayhem I was able to get better graphics by using a 32K ROM and totally unrolling the kernel.

 

 

EDIT: So I'm having the same trouble with the routine linked by ZackAttack as I was having with the other routines, wherever in the frame I put the routine in, it just glitches out the screen with some intermittent beeps. I guess I'm not too clear on how to put this function into my code? I replaced the area in my code labeled "WaitPos" with that snippet.

 

EDIT 2: Nevermind, I fixed -that-, but the routine doesn't seem to be updating the horizontal movement properly. The code in the GitHub is updated with that routine, I'll include a binary here for convenience.

PosObject is normally called in a loop, and after the loop is done HMOVE is triggered to use the fine-tuning to set the final position of the objects. From Collect:

 

PositionObjects:
        ldx #4              ; position all objects
POloop        
        lda ObjectX,x       ; get the object's X position
        jsr PosObject       ; set coarse X position and fine-tune amount 
        dex                 ; DEcrement X
        bpl POloop          ; Branch PLus so we position all objects
        sta WSYNC           ; wait for end of scanline
        sta HMOVE           ; use fine-tune values to set final X positions

I put a lot of comments in the source for Collect, way more than I normally would. The PosObject function with comments is this:

;===============================================================================
; PosObject
;----------
; subroutine for setting the X position of any TIA object
; when called, set the following registers:
;   A - holds the X position of the object
;   X - holds which object to position
;       0 = player0
;       1 = player1
;       2 = missile0
;       3 = missile1
;       4 = ball
; the routine will set the coarse X position of the object, as well as the
; fine-tune register that will be used when HMOVE is used.
;
; Note: The X position differs based on the object, for player0 and player1
;       0 is the leftmost pixel while for missile0, missile1 and ball 1 is
;       the leftmost pixel:
;           players     - X range is 0-159
;           missiles    - X range is 1-160
;           ball        - X range is 1-160
; Note: Setting players to double or quad size will affect the position of
;       the players.
;===============================================================================
PosObject:
        sec
        sta WSYNC
DivideLoop
        sbc #15        ; 2  2 - each time thru this loop takes 5 cycles, which is 
        bcs DivideLoop ; 2  4 - the same amount of time it takes to draw 15 pixels
        eor #7         ; 2  6 - The EOR & ASL statements convert the remainder
        asl            ; 2  8 - of position/15 to the value needed to fine tune
        asl            ; 2 10 - the X position
        asl            ; 2 12
        asl            ; 2 14
        sta.wx HMP0,X  ; 5 19 - store fine tuning of X
        sta RESP0,X    ; 4 23 - set coarse X position of object
        rts            ; 6 29

All those comments are why I recommend the series to people who are beginning to learn programming for the 2600.

Link to comment
Share on other sites

After you position all the objects with that routine you need to do Sta hmove right after a Sta wsync.

sta HMOVE is exactly what I was missing, thanks.

 

On other systems you can read a single register and get an 8 bit value that tells you how far the paddle (or analog stick) is turned. The Atari is much more primitive. Instead, the paddle controls how long it takes to recharge a capacitor (the capacitors are the Atari, not in the paddles - there's one capacitor per paddle).

That sounds pretty difficult.. I think I might leave paddle for a later project, since I wouldn't even have a way to play it with an actual paddle anyway.

 

PosObject is normally called in a loop, and after the loop is done HMOVE is triggered to use the fine-tuning to set the final position of the objects. From Collect:

I was calling it correctly but I was missing the HMOVE call, I got it to work now.

 

All those comments are why I recommend the series to people who are beginning to learn programming for the 2600.

Sorry I didn't look much into it at first, I was already getting kinda overwhelmed by the guide I was trying to follow but yours is much clearer. I started rewriting my code using your timer source as a template and I got everything to work more or less right, but I'm still getting some oddities I'll work out later. That code is on my GitHub now, I hope it's okay to use your code as a starting point. I've credited you in the comments.

 

Thanks for the help so far, everybody!

Link to comment
Share on other sites

That sounds pretty difficult.. I think I might leave paddle for a later project, since I wouldn't even have a way to play it with an actual paddle anyway.

Yeah, the way you read paddles on the 2600 is just as odd as the way you position the sprites - their both time based :)

 

I hope it's okay to use your code as a starting point. I've credited you in the comments.

That's fine, that's why I wrote the tutorial.

 

What are you using as an editor? If you're not already using a programmers editor then I suggest you check out jEdit. I posted this topic about it, and have this blog series about it.

 

If you do decide to use jEdit you'll want to make a revision to your source. Change this line:

    ; put into a "reduced package".  This package limits the 6507 to an 8K


To this:

    ; put into a "reduced package".  This packaging limits the 6507 to an 8K


 

jEdit picks up on the word package and uses what follows it when compiling the source and launching of Stella. For Collect it makes dasm's output:

  • limits.collect.bin
  • limits.collect.lst
  • limits.collect.sym

Instead of:

  • collect.bin
  • collect.lst
  • collect.sym
Link to comment
Share on other sites

That code is on my GitHub now

I do see one thing you'll want to change now so it doesn't potentially give you problems later. You have PosObject at the end of your project, move it to the start so it's directly after the ORG $F000.

 

The reason for this is if a branch command (the BCS in PosObject) crosses a page it takes an extra cycle of processor time which will throw off the routine. Items are on the same page when the high-byte of their addresses have the same value, so addresses $F100 and $F1FF are both on the same page (page F1) while $F1FF and $F200 are on different pages.

 

As an example, if DivideLoop ends up at address F5FE the BCS command will be at address F600. Since F5 is a different page than F6 the branch command will take 6 cycles, instead of 5, to branch back to Divideloop. If that happens your objects won't end up at the proper X locations.

Link to comment
Share on other sites

What are you using as an editor? If you're not already using a programmers editor then I suggest you check out jEdit.

I already use Notepad++ under Windows and gedit under Linux, which are the ones I'm most comfortable with at the moment.

 

I do see one thing you'll want to change now so it doesn't potentially give you problems later. You have PosObject at the end of your project, move it to the start so it's directly after the ORG $F000.

I made the change on my copy (not pushed to GitHub yet) but I didn't notice anything different visually.

 

The weirdness I'm still getting is the colors are different than what I had before even though the values should be the same, and when the paddle reaches around the last quarter of the screen horizontally, the screen goes monochrome and shifts down one scanline.

I'm sure it's something I'm doing wrong in the code, but I haven't looked much into it yet. This is just a hobby project anyway :P

Link to comment
Share on other sites

I already use Notepad++ under Windows and gedit under Linux, which are the ones I'm most comfortable with at the moment.

I don't know if anybody's set up syntax highlighting rules for those, but they'll work :)

 

I made the change on my copy (not pushed to GitHub yet) but I didn't notice anything different visually.

I suggested the change to prevent the issue from cropping up in the future. As you add more code to your program, the location of PosObject will change - depending upon where, you could end up with the branch crossing a page. It's hard to track things like that down, so it's a good practice to put routines with critical timing at the start of the ROM.

 

The weirdness I'm still getting is the colors are different than what I had before even though the values should be the same, and when the paddle reaches around the last quarter of the screen horizontally, the screen goes monochrome and shifts down one scanline.

I'm sure it's something I'm doing wrong in the code, but I haven't looked much into it yet. This is just a hobby project anyway :P

Your scanline count used to be 270, which Stella detected as NTSC. In the new version it's 290, which Stella detected as PAL. The Atari outputs different colors for each video system: NTSC, PAL, and SECAM.

 

In Stella you can use the Developer Key COMMAND-L (on Mac) or ALT-L (Linux or Windows) to display the scanline count, video format, and cartridge type:

 

prior version detected as NTSC

post-3056-0-50266500-1457823747_thumb.png

 

current version detected as PAL

post-3056-0-13781000-1457823761_thumb.png

When the paddle gets to the far right the scanline count increases to 291. Color information is lost on PAL displays if the scanline count is odd.

 

In checking the code I see you're calling PosObject in the middle of your kernel. The scanline count increased by 1 because PosObject takes longer (more CPU time) to position objects on the right side of the screen. You should move the call to PosObject to the end of Vertical Blank where an extra scanline won't cause problems (that's why we use timers to check for the end of Vertical Blank and Overscan). I've changed it here:

brick.asm

 

It's possible to reposition objects during the kernel; though, that is a more advanced technique, in part because you need to make sure the scanline count stays consistent.

 

You'll need to decide if you're targeting NTSC or PAL and use the appropriate number of scanlines. For NTSC you want to output 262, for PAL 312. While Stella will work with counts different than those, real TVs often have problems.

Link to comment
Share on other sites

I suggested the change to prevent the issue from cropping up in the future. As you add more code to your program, the location of PosObject will change - depending upon where, you could end up with the branch crossing a page. It's hard to track things like that down, so it's a good practice to put routines with critical timing at the start of the ROM.

Got it!

 

Your scanline count used to be 270, which Stella detected as NTSC. In the new version it's 290, which Stella detected as PAL. The Atari outputs different colors for each video system: NTSC, PAL, and SECAM.

You'll need to decide if you're targeting NTSC or PAL and use the appropriate number of scanlines. For NTSC you want to output 262, for PAL 312. While Stella will work with counts different than those, real TVs often have problems.

That makes sense! The reason why I was getting a wonky scanline count though is I forgot the RTS instruction at the end of my kernel -- it was calling overscan twice :P

I fixed and tweaked the code (now pushed) to go back to the NTSC linecount I had before, but I need to check if my Brazilian 2600 clone (Dactar) uses NTSC or PAL TIA. (Brazil uses PAL-M or PAL60, which is 60hz NTSC timing with PAL color freqs.)

 

In checking the code I see you're calling PosObject in the middle of your kernel. The scanline count increased by 1 because PosObject takes longer (more CPU time) to position objects on the right side of the screen. You should move the call to PosObject to the end of Vertical Blank where an extra scanline won't cause problems (that's why we use timers to check for the end of Vertical Blank and Overscan). I've changed it here:

For some reason in my mind I had to call it somewhere in there because of how the scanlines worked, since I was thinking about visible space, but now I realize I can do it outside that.

 

Thanks for the patience and big help!

Link to comment
Share on other sites

I need to check if my Brazilian 2600 clone (Dactar) uses NTSC or PAL TIA. (Brazil uses PAL-M or PAL60, which is 60hz NTSC timing with PAL color freqs.)

That I don't know, though I do know that Stella supports PAL60, which is NTSC timing with PAL colors. While running Stella you can hit CONTROl-F to cycle through the systems: NTSC, PAL, SECAM, NTSC50, PAL60, SECAM60.

 

If you happen to be launching Stella via command line (or via Notepad++ or gedit) you can set it like this:

stella -format PAL60 brick.bin

 

Thanks for the patience and big help!

No problem!

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