function BuildTitleString() { return "2600 101 - My First Program"; } ?>
mode name | example | what it does |
---|---|---|
Absolute addressing | lda $2000 | load a byte from location hexadecimal 2000 (8192 decimal) |
Zero-page addressing | lda $80 | load a byte from location $80 (128 decimal). This is the first byte of RAM |
Absolute indexed addressing | lda $2000,x | load a byte from location hexadecimal 2000 + x, where x is the 8-bit value in the X register |
Indirect indexed, y | lda (0),y | load a byte from location formed by the two bytes at 0,1 (in low, high format) added to the 8-bit value in the Y register |
; thin red line by Kirk Israel processor 6502 include vcs.h org $F000 Start SEI CLD LDX #$FF TXS LDA #0 ClearMem STA 0,X DEX BNE ClearMem LDA #$00 STA COLUBK LDA #33 STA COLUP0 MainLoop LDA #2 STA VSYNC STA WSYNC STA WSYNC STA WSYNC LDA #43 STA TIM64T LDA #0 STA VSYNC WaitForVblankEnd LDA INTIM BNE WaitForVblankEnd LDY #191 STA WSYNC STA VBLANK LDA #$F0 STA HMM0 STA WSYNC STA HMOVE ScanLoop STA WSYNC LDA #2 STA ENAM0 DEY BNE ScanLoop LDA #2 STA WSYNC STA VBLANK LDX #30 OverScanWait STA WSYNC DEX BNE OverScanWait JMP MainLoop org $FFFC .word Start .word Start
; thin red line by Kirk Israel ; ; (anything after a ; is treated as a comment and ; ignored by DASM) ; ; First we have to tell DASM that we're ; coding to the 6502: ; processor 6502 ; ; then we have to include the "vcs.h" file ; that includes all the "convenience names" ; for all the special atari memory locations... ; include vcs.h ; ; now tell DASM where in the memory to place ; all the code that follows...$F000 is the preferred ; spot where it goes to make an atari program ; (so "org" isn't a 6502 or atari specific command... ; it's an "assembler directive" that's ; giving directions to the program that's going to ; turn our code into binary bits) ; org $F000 ; ; Notice everything we've done so far is "indented" ; Anything that's not indented, DASM treats as a "label" ; Labels make our lives easier...they say "wherever the ; next bit of code ends up sitting in physical memory, ; remember that location as 'labelname'. That way we ; can give commands lke "JMP labelname" rather than ; "JMP $F012" or what not. ; So we'll call the start of our program "Start". ; Inspired genius, that. Clever students will have ; figured out that since we just told DASM "put the ; next command at $F000", and then "Call the next memory ; location 'Start':, we've implicitly said that ; "Start is $F000" ; Start ; ; The next bit of code is pretty standard. When the Atari ; starts up, all its memory is random scrambled. So the first ; thing we run is "SEI" "CLD" and "TXS". ; Look these up if you want, ; for now know that they're just good things to cleanse ; the palette... SEI ;Disable Any Interrupts (hey look! we can put comments here) CLD ; Clear BCD math bit. LDX #$FF ; put X to the top... TXS ; ...and use it reset the stack pointer ; ; Now this is another pretty standard bit of code to start ; your program with..it makes a noticeable delay when your ; atari program starts, and if you're a hot shot you could consider ; zeroing out only the memory locations you care about, but ; for now we're gonna start at the top of memory, walk our way ; down, and put zeros in all of that. ; ; One thing you may notice is that a lot of atari programming ; involves starting at a number, and counting your way down ; to zero, rather than starting at zero and counting your ; way up. That's because when you're using a Register to ; hold your counter, it's faster/easier to compare that ; value to zero than to compare it to the target value ; you want to stop at. ; ; So X is going to hold the starting memory location ; (top of memory, $#FF)...in fact, it's already set to ; $FF from the previous instruction, so we're not going to ; bother to set it again...you see that kind of shortcut all ; the time in people's code, and sometimes it can be confusing, ; but Atari programs have to be *tight*. The "A"ccumulator is ; going to hold what we put into each memory location (i.e. zero) LDA #0 ;Put Zero into A, X is at $FF ClearMem STA 0,X ;Now, this doesn't mean what you think... DEX ;decrement X (decrease X by one) BNE ClearMem ;if the last command resulted in something ;that's "N"ot "Equal" to Zero, branch back ;to "ClearMem" ; ; Ok...a word of explanation about "STA 0,X" ; You might assume that that said "store zero into the memory ; location pointed to by X..." but rather, it's saying ; "store whatever's in the accumulator at the location pointed ; to by (X plus zero)" ; ; So why does the command do that? Why isn't there just a ; "STA X" command? (Go ahead and make the change if you want, ; DASM will give you an unhelpful error message when you go ; to assemble.) Here's one explanation, and it has to do with ; some handwaving I've been doing...memory goes from $0000-$FFFF ; but those first two hex digits represent the "page" you're dealing ; with. $0000-$00FF is the "zero page", $0100-$01FF is the first ; page, etc. A lot of the 6502 commands take up less memory ; when you use the special mode that deals with the zero page, ; where a lot of the action in atari land takes place. ; ...sooooo, STA $#nnnn would tell it to grab the next two bytes ; for a full 4 byte address, but this mode only grabs the one ; value from the zero page ; ; ; Now we can finally get into some more interesting ; stuff. First lets make the background black ; (Technically we don't have to do this, since $00=black, ; and we've already set all that memory to zero. ; But one easy experiment might be to try different two ; digit hex values here, and see some different colors ; LDA #$00 ;load value into A ("it's a black thing") STA COLUBK ;put the value of A into the background color register ; ; Do the same basic thing for missile zero... ; (except missiles are the same color as their associated ; player, so we're setting the player's color instead ; LDA #33 STA COLUP0 ; ; Now we start our main loop ; like most Atari programs, we'll have distinct ; times of Vertical Sync, Vertical Blank, ; Horizontal blank/screen draw, and then Overscan ; ; So every time we return control to Mainloop. ; we're doing another television frame of our humble demo ; And inside mainloop, we'll keep looping through the ; section labeled Scanloop...once for each scanline ; MainLoop ;*********************** VERTICAL SYNC HANDLER ; ; If you read your Stella Programmer's Guide, ; you'll learn that bit "D1" of VSYNC needs to be ; set to 1 to turn on the VSYNC, and then later ; you set the same bit to zero to turn it off. ; bits are numbered from right to left, starting ; with zero...that means VSYNC needs to be set with something ; like 0010 , or any other pattern where "D1" (i.e. second ; bit from the right) is set to 1. 0010 in binary ; is two in decimal, so let's just do that: ; LDA #2 STA VSYNC ; Sync it up you damn dirty television! ; and that vsync on needs to be held for three scanlines... ; count with me here, STA WSYNC ; one... (our program waited for the first scanline to finish...) STA WSYNC ; two... (btw, it doesn't matter what we put in WSYNC, it could be anything) STA WSYNC ; three... ; We blew off that time of those three scanlines, though we could have ; done some logic there...but most programs will definately want the vertical blank time ; that follows... ; you might want to do a lot of things in those 37 lines...so many ; things that you might become like Dirty Harry: "Did I use up 36 scanlines, ; or 37? Well, to tell you the truth, in all this excitement, I've kinda lost track ; myself." So here's what we do...The Atari has some Timers built in. You set these ; with a value, and it counts down...then when you're done thinking, you kill time ; until that timer has clicked to zero, and then you move on. ; ; So how much time will those 37 scan lines take? ; Each scanline takes 76 cycles (which are the same thing our clock is geared to) ; 37*76 = 2812 (no matter what Nick Bensema tries to tell us...his "How to Draw ; A Playfield" is good in a lot of other ways though..to quote the comments from ; that: ; We must also subtract the five cycles it will take to set the ; timer, and the three cycles it will take to STA WSYNC to the next ; line. Plus the checking loop is only accurate to six cycles, making ; a total of fourteen cycles we have to waste. ; ; So, we need to burn 2812-14=2798 cycles. Now, there are a couple of different ; timers we can use, and Nick says the one we usually use to make it work out right ; is TIM64T, which ticks down one every 64 cycles. 2798 / 64 = 43.something, ; but we have to play conservative and round down. ; LDA #43 ;load 43 (decimal) in the accumulator STA TIM64T ;and store that in the timer LDA #0 ;Zero out the VSYNC STA VSYNC ; 'cause that time is over ; ; So here we can do a ton of game logic, and we don't have ; to worry too much about how many instructions we're doin, ; as long as it's less than 37 scanlines worth (if it's not ; less, your program is screwed with a capital screw) ; ;*********************** VERTICAL BLANK WAIT-ER WaitForVblankEnd LDA INTIM ;load timer... BNE WaitForVblankEnd ;killing time if the timer's not yet zero LDY #191 ;Y is going to hold how many lines we have to do ;...we're going to count scanlines here. theoretically ; since this example is ass simple, we could just repeat ; the timer trick, but often its important to know ; just what scan line we're at. STA WSYNC STA VBLANK ;End the VBLANK period with the zero ;(since we already have a zero in "A"...or else ;the BNE wouldn't have let us get here! Atari ;is full of double use crap like that, and you ;should comment when you do those tricks) ;We do a WSYNC just before that so we don't turn on ;the image in the middle of a line...an error that ;would be visible if the background color wasn't black. ;HMM0 is the "horizontal movement register" for Missile 0 ;we're gonna put in a -1 in the left 4 bits ("left nibble", ; use the geeky term for it)...it's important ;to note that it's the left 4 bits that metters, what's in the ;right just doesn't matter, hence the number is #$X0, where ;X is a digit from 0-F. In two's complement notation, -1 ;is F if we're only dealing with a single byte. ; ; Are you having fun yet? ; LDA #$F0 ; -1 in the left nibble, who cares in the right STA HMM0 ; stick that in the missile mover STA WSYNC ;wait for one more line, so we know things line up STA HMOVE ;and activate that movement ;note...for godawful reasons, you must do HMOVE ;right after a damn WSYNC. I might be wasting a scanline ;with this, come to think of it ;*********************** Scan line Loop ScanLoop STA WSYNC ;Wait for the previous line to finish LDA #2 ;now sticking a 2 in ENAM0 (i.e. bit D1) will enable Missile 0 STA ENAM0 ;we could've done this just once for the whole program ;since we never turn it off, but i decided to do it again and ;again, since usually we'd have to put smarter logic in ; each horizontal blank ; ;so at some point in here, after 68 clock cycles to be exact, ;TIA will start drawing the line...all the missiles and players ;and what not better be ready...unless we *really* know what ;we're doing. DEY ;subtract one off the line counter thingy BNE ScanLoop ;and repeat if we're not finished with all the scanlines. LDA #2 ;#2 for the VBLANK... STA WSYNC ;Finish this final scanline. STA VBLANK ; Make TIA output invisible for the overscan, ; (and keep it that way for the vsync and vblank) ;***************************** OVERSCAN CALCULATIONS ; ;I'm just gonna count off the 30 lines of the overscan. ;You could do more program code if you wanted to. LDX #30 ;store 30 OverScanWait STA WSYNC DEX BNE OverScanWait JMP MainLoop ;Continue this loop forver! Back to the code for the vsync etc ; OK, last little bit of crap to take care of. ; there are two special memory locations, $FFFC and $FFFE ; When the atari starts up, a "reset" is done (which has nothing to do with ; the reset switch on the console!) When this happens, the 6502 looks at ; memory location $FFFC (and by extension its neighbor $FFFD, since it's ; seaching for both bytes of a full memory address) and then goes to the ; location pointed to by $FFFC/$FFFD...so our first .word Start tells DASM ; to put the binary data that we labeled "Start" at the location we established ; with org. And then we do it again for $FFFE/$FFFF, which is for a special ; event called a BRK which you don't have to worry about now. org $FFFC .word Start .word Start
Introduction -
The Development Environment -
Into The Breach -
My First Program - Kernal Clink - The Joy of Sticks - Happy Face - PlayerBufferStuffer |