Jump to content

Constructive help needed on finishing first 2600 Assembly Game


Recommended Posts

Hi everyone, I've poked my head up occasionally for other consoles. I've done some demo stuff for both the Intellivision and the Fairchild Channel F, but haven't really got to the point of truly finishing a project yet - other than a random original odyssey demake from the Channel F (but that doesn't really count!).


I've realised that starting from the Fairchild as someone who had never used assembly before was a bit silly, and decided to take full advantage of the better access to 2600 resources instead.

I found that this course https://www.udemy.com/course/programming-games-for-the-atari-2600/ really helped as he went through it line by line, but in a way I understood better than many resources online. I have a number of books that help starting out too that is available on lulu etc.


I decided my first serious project for the 2600 should be to port submarine from the PC-50x, mainly because it appeared to be the only game on the console that didn't have an obvious comparison on the 2600 (perhaps shooting gallery too, but maybe that can be the next project!). Submarine offers a varied number of modes, both single and 2 player. There is also a really bad "space" version of the same game on the cartridge, which from memory I think is only 2 player. I intend to get all of it in a 2600 rom in time but just trying to get all the basics first.


If anyone is curious about the console (and game) itself check out: 

 here TheProgrammerIncarnate has done an amazing job regarding converting it to fpga, check it out!


Anyway, back to my unworkable rubbish. I've been working on my game through https://8bitworkshop.com/ which is incredibly helpful for testing games live. The site now includes other consoles including the 7800 too.

I've found I get different results depending on whether I use the website, stella emulator or my UNO cart on my PAL consoles. That's fine, I guess its to be expected, I haven't even attempted to worry about NTSC and PAL just yet.

I'm very slowly learning the ropes, so I would appreciate constructive feedback in the form of help (ideally particularly on the code itself) about specific parts. I've made this by watching the above udemy course, and editing and altering, adding as I tend to learn languages that way, and then it should move onto other projects being more independent. What I have here is probably the first project where I've actually been able to write code for assembly without pure copy paste and editing numbers, so go easy on me as I'm already pretty proud I got this far! Also, please assume I'm stupid, if there is something that you think I should have understood by this point to make something this far Assume I don't know it and I've just been guessing. I learn by breaking things and then fixing them again, but it does mean I don't learn it "right". My goal is to not only do a few 2600 games (not going over the top, I'm not going to be Champ Games level at any point) but to move back to Fairchild and other assembly consoles. What you should get is a few years of me developing homebrew games for a number of consoles, original and ports - so please help support me reach my goal and I will reward with games!


I'll upload my code and rom:

submarine aa help.zip

submarine aa help.rom


Specific things I'm after is:


1) Why doesn't collision detection between Player 0 and Missile 1 work? (CHeckCXM1P)   This was because I was clearing the collision flag from copy pasting code between M0 and P1 - this isn't required and I just deleted the line sta CXCLR

2) Why is the second black ship (the pyramid looking one) jumping instead of "sliding" like the player and the top ship? (This is me using Player 1 for multiple objects, I was pretty proud of myself as I worked out some of this on my own!) I worked this one out - I needed to reuse HMOVE once switching Player 1 for the 3rd ship in the middle. After doing this, the player started skipping, so I moved it to when the 3rd ship changes X co-ordinate as well!

3) Why is Missile 1 a line and not a dot like Missile 0 (is this linked to stretching of the player? is it linked because Player1 is stretched using NUSIZ1?) This is because of NUSIZ1, I just haven't worked out how to change it yet without also changing the ship stretch.

4) How do I add sound effects effectively? There is essentially three in the original game, the "submarine" sound, the "missile" sound and the "hit" sound. Submarine is a continuing 2 tone sonar like sound during play, missile is a beep-beep-beep-beep sound after firing while moving (Missile 0 only) and the hit sound is like a weird twang, which then goes silent until the players can move again (slight delay). I have a really basic understanding from the udemy course for how it works, but any help on sound creation for the 2600 would be greatly appreciated. I don't think I've found a true "tutorial" on it yet, if someone can link me to one if they exist, would be extremely thankful!

5) If there is anything obvious that 8bitworkshop is doing that is effecting differences in stella or on actual hardware (I'm aware that it won't look the same on my PAL console - I'll work on that later).


Things I think I can work out on my own or I'll deal with later:


1) Closer colours and proportions to the original game (I know its off, I'm still getting used to the idea of 'racing the beam' though do explain it to me if there is really obvious things you think I need to know to help).

2) Some optimisation. There is lots to do, and I think I know how to optimise some of it, be kind if you mention this! and explain why its better.

3) Getting the other modes in. I currently only have one, the standard game where you have two ships you can hit. I have 1 and 2 player in, but I still need to add the other subtypes, including a "blind" version and the space game that was also added into the cartridge.

4) Adapting it for PAL later on, I'm getting the basics in first and understanding how it works, I'll be doing a PAL version for sure as its what I use.

5) I'm aware that I've saved it as a 2kb game. I'd love to get the whole thing into 2kb, if only to get last place in the Atari Homebrew awards next year ?


Thanks in advance for any support given, know that genuine offers for support is really appreciated, and that due to my own neurological condition, while I type smart sometimes, it takes me quite awhile to learn new things and develop them. The end goal (other than finishing this game) should be that you'll see a lot more projects from me in a playable format. I quite like demakes/upmakes so it might be my main domain of development - but I imagine I'll get a few original stuff out too.


Thanks Thanks Thanks.

Edited by Mikebloke
updating on progress.
Link to comment
Share on other sites

I've tried to have a go with it, does this look any better than the first version? I think its more stable, I've made vertical blank and overscan into better loops, and changed a couple of values that was used to work out the "skyline" including changing one into a different format (which is probably where I was going wrong). I'm also taking advantage of the debugger in stella too trying to work out some of the issues as 8bitworkshop one runs slightly differently. I still get two different things from both emulators, but it seems better than before.


submarine help2.rom




I'm smart enough now to realise that missile1 is influenced by player1 being stretched. I attempted to rectify it but it caused more problems (either with screen flickering or returning player1 to not-stretched). So back to the drawing board with all the rest of it! Anyone know why missile1+player0 collision isn't being detected in my code?

Link to comment
Share on other sites

1 hour ago, Mikebloke said:

Probably cause I don't know what that is, could you explain/link me some information?

There are "timers" on the machine which when set to a value will reliably count-down to zero.

You can read a register to find out when they reach zero. So the principle is to set the timer at the start of your vblank processing time, for example, then do whatever you want - and after it is finished you just wait for the timer to reach zero. You are guaranteed to have a "stable" frame if you use timers, and if your "whatever your want" stuff executes quickly enough.

Here's sample usage...


                    lda #38				; or whatever time you allocate for this
                    sta TIM64T			; in 64-cycle chunks

	; do some stuff here
    ; as long as it takes at most 38*64 cycles, we're good

Waitforit           bit TIMINT
                    bpl Waitforit		; wait for timer to reach zero

                    jmp .StartFrame

ALSO, rather than just waiting for the timer to reach 0 (via the "bit" instruction), you can actually ask how much time is left...

                lda INTIM
                cmp #SAFETIME
                bcc .skip            ; not enough time to do the next bit
    ; do some stuff here that takes < SAFETIME*64 cycles

This allows you to do complex "multitasking" if you have a scheduler that simply checks if there's enough time to do a task, and fires off a call to the task if there is.  You need, of course, to calculate the time requirements for a task by cycle-counting (or even easier now with the new stella timing capability) and making sure there's enough time (6502 cycles the task takes/64) for the task.

There are other timers other than TIM64T (TIM1T, TIM8T, T1024T) but I have only used the TIM64T myself.


Edited by Andrew Davie
  • Like 1
Link to comment
Share on other sites

Thanks to Andrew Davie and SpiceWare.


Im starting to get a handle for it. I don't think I've implemented it quite right yet, but it's stabilising more as I update. 


I've also worked out why it's not been detecting collision ; I've been clearing the collision flag between the two checks, a result of copy pasting code. This means it's been ignoring the second check between missile1 and player0, so that is one problem fixed! Also means there is now something "playable". Away from home at the moment so I'm just assembling through dasm instead of using 8bitworkshop; going to work on it some more over the next few days and hopefully get another version up later this week. 

  • Like 1
Link to comment
Share on other sites



maybe this blog covering my first project on the VCS is of help by providing another perspective: https://www.masswerk.at/rc2018/04/


Background: This was a project for RetroChallenge, which is a loose gathering about doing a project on retro hardware over a particular month (in this case April 2018) and providing means to participate by following your progress. Since the project was done in a month, it's not too complex. The intended audience of the blog were other RetroChallengers, meaning, folks who know a bit about programming and the 6502 (I covered the 6502 in another project before), but are not particularly familiar with the VCS / Atari 2600. So, regarding the VCS, the blog starts from zero. I had read up on the VCS before (mostly Andrew Davie's tutorial here on AtariAge, and the Stella Programmer's Guide), but these were my first actual steps. As always, things are actually a bit different, when doing them for real, and I tried to share everything I knew and what I had found on the way in these write-ups.


  • Like 2
Link to comment
Share on other sites

I took a brief look at your code. The idea behind setting timers for Vertical Blank and Overscan is to be able to make use of the processing time for your game logic. The normal flow for each frame should be something like:


  • Do vertical sync (you can use the macro VERICAL_SYNC for this if desired)
  • Set a timer for Vertical Blank (usually 37 scanlines, or 44 64-cycle chunks for TIM64T)
  • Do processing before the visible screen is displayed (e.g. positioning objects)
  • Run out remainder of timer after you are done with processing in vertical blank
  • Unblank the screen
  • Display the visible screen. This will usually be about 192 scanlines for a NTSC display. Be sure to keep track of your scanlines, and also count cycles within the display logic
  • Blank the screen again
  • Set a timer for Overscan (usually 30 scanlines, or 36 64-cycle chunks for TIM64T)
  • Do processing after the visible screen, and before the next frame (e.g. checking for collisions)
  • Run out remainder of timer after you are done with processing in overscan
  • Jump to the next frame

In addition to SpiceWare's tutorial, I would recommend the book Making Games for the Atari 2600. It was hugely helpful for me when I was learning.

  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...

I've grabbed the book from 8bitworshop, it looks quite helpful. Before that I did move some of the code to the end, and it seems to have helped slightly, but I'm still getting some variance of between 2 or 3 lines. I also added reset button to reset, but want to hold out on any more features till I've stabilised the scanlines.

Link to comment
Share on other sites

On 2/4/2020 at 5:35 PM, Mikebloke said:

4) How do I add sound effects effectively? There is essentially three in the original game, the "submarine" sound, the "missile" sound and the "hit" sound. Submarine is a continuing 2 tone sonar like sound during play, missile is a beep-beep-beep-beep sound after firing while moving (Missile 0 only) and the hit sound is like a weird twang, which then goes silent until the players can move again (slight delay). I have a really basic understanding from the udemy course for how it works, but any help on sound creation for the 2600 would be greatly appreciated. I don't think I've found a true "tutorial" on it yet, if someone can link me to one if they exist, would be extremely thankful!


My ASM is abysmal but here is one way you could do it; You just need to declare three variables to be used to control the AUDC0, AUDF0, and duration in frames. (AUDV0 can be left at 4 in this example, to save on RAM) Then something like this in your main loop :

 LDA FXtone
 BNE .loaddur
 jmp .FXnullify
 LDA FXdur
 BNE .loadpitch
 LDA #0
 STA FXpitch
 LDA FXpitch
 BNE .cyclesound
 LDA #0
 jmp .muted
 DEC FXtone
 DEC FXpitch
 DEC FXdur
 LDA FXtone
 LDA #4
 LDA FXpitch
 ; muted

You can then turn on a sound effect by setting the FXtone (translates to AUDC0/ waveform you want) the FXpitch (which specifies the starting pitch, to be decreased each frame) and the FXdur (which acts as a frame-count limiter) anywhere during the programs execution (such as on a successful collision check) in this way you can specify different sound effects by just playing with these three values. I don't pretend that this code is optimized, but I think it's a fairly simple way of doing it. The values of AUDC0, AUDF0 and FXdur are all decreased by 1 each frame until any of them equal zero, then the sound is turned off. Hope this at least provides you with some ideas.
Disclaimer: Did not compile and test, but this is based on working code.

  • Like 1
Link to comment
Share on other sites

This is a sound player, I once came up with. You may address any of the two channels and play a sound, which is defined in a table:

; RAM addresses for sound control (use your own!)

SoundIdx0   = $B9
SoundIdx1   = $BA

SoundTmr0   = $BB
SoundTmr1   = $BC

; sound subroutines

PlaySound                  ; sound in Y, channel/player in X (0,1)
    sty SoundIdx0,X
    cpy #0
    beq playSoundReset     ; index is zero, mute and return
    lda SoundTable + 3,Y   ; get duration in frames
    sta SoundTmr0,X
    lda SoundTable,Y
    sta AUDC0,X            ; tone
    lda SoundTable + 1,Y
    sta AUDF0,X            ; frequency/pitch
    lda SoundTable + 2,Y
    sta AUDV0,X            ; volume
    lda #0
    sta AUDV0,X            ; reset volume
    sta SoundIdx0,X
    sta SoundTmr0,X

HandleSounds               ; channel in X (0,1)
    ldy SoundIdx0,X
    beq handleSoundsDone   ; index = 0, no sound
    dec SoundTmr0,X        ; decrement frame counter
    beq handleSoundsNext   ; run to zero? next sound from table
    lda SoundTable + 4,Y   ; next sound
    jmp PlaySound

; sound table

    .byte 0                ; no sound / stop

;                              tone pitch vol frames next
;                              -------------------------
Snd_MissileBounce = * - SoundTable
    .byte                       $04, $04, $06, $04, $00

Snd_Barrier = * - SoundTable
    .byte                       $0F, $19, $07, $04, Snd_Barrier1
Snd_Barrier1 = * - SoundTable
    .byte                       $0F, $1a, $07, $10, Snd_Barrier2
Snd_Barrier2 = * - SoundTable
    .byte                       $0F, $1b, $04, $04, S00


; "Snd_MissileBounce = * - SoundTable":
; just defines a label holding the current offset into the sound table
; to be used when calling the subroutine, like in

ldx #0                 ; channel 0
ldy #Snd_MissileBounce ; sound index
jsr PlaySound          ; do it

; each frame, we then call subroutine HandleSounds for each channel:

ldx #0
jsr HandleSounds
ldx #1
jsr HandleSounds

; a call to PlaySound will stop the current sound and replace it
; by the new one, as it simply overwrites the timer and index data.
; (You may check SoundIdx0 and SoundIdx1 to check for any non-zero
: values, indicating that the channel is occupied, before.)

; This will stop a sound in a given channel:
ldx #0                 ; channel 0
ldy #0                 ; sound index: no sound
jsr PlaySound          ; mute the channel

Mind the 5th value, "next", for each of the table entries. This will allow you continue with another sound. (This may be also a pause, like "0, 0, 0, 5, <labelOfNextSoundToPlay>" for a pause of 5 frames.)

For a bit more of a background, see https://www.masswerk.at/rc2018/04/13.html

This one has a bit more on VCS sound basics and links to a test application: https://www.masswerk.at/rc2018/04/12.html

(The sound test application is here, online using Javatari, you may also download ROMs: https://www.masswerk.at/rc2018/04/studio2600/ )

Edited by NoLand
  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...

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.

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.

  • Recently Browsing   0 members

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