George Washingtoad Posted February 12, 2019 Share Posted February 12, 2019 Hey there everyone, I just started learning to program the Atari 2600 in Assembly recently and I think that I have learned a great deal. However, I seem to have hit a road block. Initially, when I first started programming playfields and backgrounds, I plugged the values in line by line like this: .fctbNone{ color:#e0e0e0; }.fctbStyle0{ color:#adff2f;font-style:oblique; }.fctbStyle3{ color:#ffd700; }.fctbStyle5{ color:#ee82ee; }.fctbStyle4{ color:#00bfff; }.fctbStyle2{ color:#ffa500; }.fctbStyle1{ color:#00ffff;font-weight:bold; }.fctbStyle1Style3{ color:#00ffff;font-weight:bold; }.fctbStyle2Style3{ color:#ffa500; }.fctbStyle6{ color:#ff6347; }.fctbStyle0Style6{ color:#adff2f;font-style:oblique; }; < GAME TITLE >; < AUTOR > processor 6502 include "vcs.h" include "macro.h" ;---------- LABELS ----------;---------------------------- SEG ORG $F000Reset; Initialize TIA/RAM; ------------------ LDX #0 LDA #0 Clear STA 0,X INX BNE Clear ; Initialize Labels; ----------------- COMPILE_VERSION = NTSC NTSC =#1 LDA #%00000001 STA $82 LDA #$BC STA COLUPF LDA $82 STA CTRLPF ; Start Game; ----------StartOfFrame LDA #0 STA VBLANK LDA #2 STA VSYNC ; VSYNC Signal STA WSYNC STA WSYNC STA WSYNC LDA #0 STA VSYNC ; Vertical Blank LDX #0 VerticalBlank STA WSYNC INX CPX #37 BNE VerticalBlank ; Scanlines LDX #0IDIOT INX LDA #$C4 STA COLUBK STA WSYNC CPX #40 BNE IDIOT LDA #$C4 STA COLUPF LDA #$0 STA COLUBK LDA #%11111111 STA PF0 STA PF1 LDA #%10111111 STA PF2 MORAL INX LDA #$CA STA COLUBK STA WSYNC CPX #42 BNE MORAL LDA #%11100000 STA PF0 LDA #%11001111 STA PF1 LDA #%00011111 STA PF2CORAL INX LDA #$CA STA COLUBK STA WSYNC CPX #44 BNE CORAL LDA #%11000000 STA PF0 LDA #%10000111 STA PF1 LDA #%00001111 STA PF2NORAL INX LDA #$CA STA COLUBK STA WSYNC CPX #46 BNE NORAL LDA #%10000000 STA PF0 LDA #%00000011 STA PF1 LDA #%00000111 STA PF2STEVE INX LDA #$CA STA COLUBK STA WSYNC CPX #48 BNE STEVE LDA #%00000000 STA PF0 LDA #%00000001 STA PF1 LDA #%00000011 STA PF2PTEVE INX LDA #$CA STA COLUBK STA WSYNC CPX #50 BNE PTEVE LDA #0 STA PF0 STA PF1 STA PF2BIGB INX LDA #$CA STA COLUBK STA WSYNC CPX #110 BNE BIGB BIGBB INX LDA #$2E STA COLUBK STA WSYNC CPX #130 BNE BIGBB BIGBBB INX LDA #$2C STA COLUBK STA WSYNC CPX #150 BNE BIGBBB FED INX LDA #$0 STA COLUBK STA WSYNC CPX #192 BNE FED EndScreen LDA #%01000010 STA VBLANK ; Overscan LDX #0Overscan STA WSYNC INX CPX #30 BNE Overscan ; Next Frame ; ---------- JMP StartOfFrame ORG $FFFA .word Reset .word Reset .word Reset END Thanks all! But while I was continuing in the tutorials, I found that my original methodology isnt really going to work (or will it? please let me know!) especially working with a great deal of sprites and backgrounds. So, I have found that most people tend to do play fields more like this: .fctbNone{ color:#e0e0e0; }.fctbStyle0{ color:#adff2f;font-style:oblique; }.fctbStyle3{ color:#ffd700; }.fctbStyle5{ color:#ee82ee; }.fctbStyle4{ color:#00bfff; }.fctbStyle2{ color:#ffa500; }.fctbStyle1{ color:#00ffff;font-weight:bold; }.fctbStyle1Style3{ color:#00ffff;font-weight:bold; }.fctbStyle2Style3{ color:#ffa500; }.fctbStyle6{ color:#ff6347; }.fctbStyle0Style6{ color:#adff2f;font-style:oblique; }; < GAME TITLE >; < AUTOR > processor 6502 include "vcs.h" include "macro.h" ;---------- LABELS ----------;---------------------------- SEG ORG $F000Reset; Initialize TIA/RAM; ------------------ LDX #0 LDA #0 Clear STA 0,X INX BNE Clear ; Initialize Labels; ----------------- COMPILE_VERSION = NTSC NTSC =#1 LDA #%00000001 STA $82 LDA #$BC STA COLUPF LDA $82 STA CTRLPF PFG0 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010PFG1 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010PFG2 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010 .byte %10101010; Start Game; ----------StartOfFrame LDA #0 STA VBLANK LDA #2 STA VSYNC ; VSYNC Signal STA WSYNC STA WSYNC STA WSYNC LDA #0 STA VSYNC ; Vertical Blank LDX #0 VerticalBlank STA WSYNC INX CPX #37 BNE VerticalBlank ; Scanlines LDX #0 ;THIS AREA IS ENCLOSED FOR THE PASTEINGLDY #0Woah LDA PFG0,X STA PF0 LDA PFG1,X STA PF1 LDA PFG2,X STA PF2 INX STA WSYNC cpx #190 bne Woah;FINISH AREA LDA %00000000 STA PF0 STA PF1 STA PF2FED INX LDA #$0 STA COLUBK STA WSYNC CPX #192 BNE FED EndScreen LDA #%01000010 STA VBLANK ; Overscan LDX #0Overscan STA WSYNC INX CPX #30 BNE Overscan ; Next Frame ; ---------- JMP StartOfFrame ORG $FFFA .word Reset .word Reset .word Reset END So once I tested this I found that I only got some of the playfield filled, the other parts are random. why is this? Can it be fixed? Quote Link to comment Share on other sites More sharing options...
George Washingtoad Posted February 12, 2019 Author Share Posted February 12, 2019 Disregard the parts on the top about the different colors, thats just from my IDE. Quote Link to comment Share on other sites More sharing options...
JeffJetton Posted February 12, 2019 Share Posted February 12, 2019 (edited) Hi George, Your set of data for PFG0 is only 48 bytes long. PFG1 and PFG2 are both 49 bytes. Your X register, on the other hand, is holding values ranging from 0 to 190. Since you're using X as an offset to the base address of each of your sets of playfield data, it eventually "overshoots the runway", causing your LDA to start loading the values of your actual program instructions and using those values to set the playfield. For example, once X reaches 49 (beyond the end of your PFG0 data), the LDA PFG0,X instruction actually loads A with the first byte of PFG1. When X gets to 98, it moves beyond PFG1 and reads the first byte of PFG2. And by the time X hits 147, you're now loading in the value of that LDA that comes after your data, at the "StartOfFrame" label. (It happens to have a value of $A9, by the way). Of course, your LDA PFG1,X and LDA PFG2,X instructions are doing the same thing, only they start farther down in your data and therefore wind up reading more of your program instructions. If you used each byte of playfield data for four consecutive scanlines, that would fix your problem. Woah LDA PFG0,X STA PF0 LDA PFG1,X STA PF1 LDA PFG2,X STA PF2 INX STA WSYNC STA WSYNC STA WSYNC STA WSYNC cpx #47 bne Woah ;FINISH AREA LDA %00000000 STA PF0 STA PF1 STA PF2 FED INX LDA #$0 STA COLUBK STA WSYNC CPX #48 BNE FED Alternatively, you could store your offset in memory somewhere instead of in X. You'll still use X to keep track of which scanline you were on, but you'd only INC your offset variable every 4 scanlines. Edited February 12, 2019 by JeffJetton Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted February 12, 2019 Share Posted February 12, 2019 You might find my tutorial useful. Quote Link to comment Share on other sites More sharing options...
JeffJetton Posted February 12, 2019 Share Posted February 12, 2019 (edited) Oh, and there are a few other things you might want to think about (and bear in mind that I'm no expert here either): Yes you absolutely should check out the above-mentioned tutorial. Your playfield data is sandwiched in-between your initialization code and the beginning of your kernel (your "frame" loop). I don't see that you're skipping over it with a jmp or anything, which means all that data is getting executed as if they were instructions! It just so happens that %10101010 (or $AA) is the TAX opcode. So your program starts out by transferring A to X 146 times in a row. (In fact, you could actually replace some or all of your .byte %10101010 lines with an equivalent number of TAX instructions and your program would run exactly the same.) You could skip the data with a jmp StartOfFrame, but it's probably more common to simply define your data at the end of your program, right before that final ORG $FFFA. And yes, unlike many (most?) structured programming languages, here it's perfectly legal to reference data that you don't "define" until later in your program. Another thing that's different from programming languages you may be used to is that it's usually better to count down to zero in all your loops rather than counting up to your target value. CPX is really just subtracting X from A and not storing the result anywhere. BNE merely tests to see if the last math operation resulted in a zero and branches if it didn't. By using a DEX instead of an INX, your final decrement to zero will trigger the desired no-branch condition. That eliminates the need for the compare instruction, saving two bytes and two processor cycles per iteration. For example... ; Vertical Blank LDX #37 VerticalBlank STA WSYNC DEX BNE VerticalBlank Edited February 12, 2019 by JeffJetton Quote Link to comment Share on other sites More sharing options...
George Washingtoad Posted February 13, 2019 Author Share Posted February 13, 2019 Thanks a lot for the advice everybody! That makes a lot more sense now! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.