Well, I've run into 2 big problems.
First, is that although I thought I had all the scanline jitter solved, it turns out that I had a few places where there was still jitter. And this has been really hard to track down. After a few angry evenings staying up too late debugging, I finally realized that it wasn't actually my display kernel causing the problem (where I had spent the longest time debugging), but that occasionally I was taking too long in my main update code, and not rendering the screen in time. So I need to do some combination of optimizing, removing code, or running updates every other frame.
The other problem is harder to deal with. I asked a friend if he could test with his Harmony Cart (an sd-card-based atari cartridge to run on a real Atari), and the game doesn't work. AT ALL. It won't do anything, not even display anything on the screen. So something's really wrong. Unfortunately, I have no idea what. At this point, I think the only thing I can do is get my Atari up and running, buy a Harmony Cart myself, and go through my git history testing different builds. At one point, about a year ago, I know that it ran. I need to find out where it stopped.
So I got the Atari out of the garage last night, and went through about 8 or 9 combinations of adapters and cords trying to get it to work. (The RF switch box that came with it is somewhat broken. I tried with my NES RF box, SuperNES switch box, and a few other gizmos from my box of adapters. I had the most luck with the NES switch) The best I got was an extremely blurry screen that you could somewhat see. Not enough to really enjoy playing on. Now do I want to spend $60 on a special cart just to debug this on my terrible TV?
I have a few more things I can try to get it working better. If I'm going to spend the money on a flash cart, I want it to at least look good. We'll see what happens.
I started by blogging the process of porting my homebrew game Anguna from the Gameboy Advance to the Nintendo DS. Now, my random thoughts on development and chronicling whatever hobby project catches my fancy.
Friday, January 22, 2016
Tuesday, January 19, 2016
Title screen
Thanks to the sample code of really smart people out there, I now have a working title screen!
The hardest part was similar to the last problems I talked about: cycle timings getting messed up while crossing a page boundary.
To get a nice large "hi-res" graphic on the atari, you have to take both sprite objects, tell the Atari to repeat them 3 times close together, and then stagger them. (Imagine the 3 repeated planes on Combat, or the 3 players on Football. Then imagine 2 sets of those interleaved with each other) That gets you halfway there, but then you have to very exact timings to update the sprite's graphic data mid-scanline. Because the Atari is so doggone slow, the timings are really tricky. (Although the technique eventually became fairly well-known -- any game with a 6-digit score display most likely used this technique).
I got the code all working thanks to that sample code I posted to above, but if I fiddled much with the graphics data, the timings would get off by 1 clock cycle, and it would fail. Turns out, again, that it was crossing a page boundary. This time with a LDA instruction (loading the accumulator from memory) -- if you do a "LDA foo,X" command, which loads the accumulator with whatever byte is at memory location (foo + the value of the X register), and foo+X crosses a page boundary from foo, it takes 5 cycles instead of 4.
Luckily the assembler gives you a nice "align" pseudo-opcode that lets you force alignment of your data into page boundaries, which fixed it.
After getting the title displayed, I wanted to make it pretty. I have 8 whole clock cycles free per line to do what I want with, but to update the color of the whole thing, I have to write to 2 registers (COLUP0 and COLUP1, the color registers for each sprite), which alone takes 6 cycles. There's not enough time left to pick or calculate the color for the scanline.
So I unrolled the kernel loop by half, which lets me use 16 clock cycles every 2 lines. That's plenty of time to load a base color, add to it based on the current line, and then write it to the color registers. The end result is a pretty animated title graphic:
The half-unrolled loop ended up looking like:
.loop:
sty tmpVar ; 3
;update the color
tya ; 2
adc PlayerColorDef ; 3
sta COLUP0 ; 3 = 11
lda (ptr+$0),y ; 5
sta GRP0 ; 3
lda (ptr+$2),y ; 5
sta GRP1 ; 3
lda (ptr+$4),y ; 5
sta GRP0 ; 3
lda (ptr+$6),y ; 5
sta tmpVar2 ; 3 = 32
lax (ptr+$a),y ; 5
lda (ptr+$8),y ; 5
ldy tmpVar2 ; 3
sty GRP1 ; 3
sta GRP0 ; 3
stx GRP1 ; 3
sta GRP0 ; 3 = 25
ldy tmpVar ; 3
dey ; 2
;burning 3 cycles in place of the bpl since the loop is half-unrolled
sty tmpVar ;3 (in place of the bpl)
sty tmpVar ; 3
;update the color
tya ; 2
adc PlayerColorDef ; 3
sta COLUP1 ; 3 = 11
lda (ptr+$0),y ; 5
sta GRP0 ; 3
lda (ptr+$2),y ; 5
sta GRP1 ; 3
lda (ptr+$4),y ; 5
sta GRP0 ; 3
lda (ptr+$6),y ; 5
sta tmpVar2 ; 3 = 32
lax (ptr+$a),y ; 5
lda (ptr+$8),y ; 5
ldy tmpVar2 ; 3
sty GRP1 ; 3
sta GRP0 ; 3
stx GRP1 ; 3
sta GRP0 ; 3 = 25
ldy tmpVar ; 3
dey ; 2
bpl .loop ; 2/3 = 7/8
The hardest part was similar to the last problems I talked about: cycle timings getting messed up while crossing a page boundary.
To get a nice large "hi-res" graphic on the atari, you have to take both sprite objects, tell the Atari to repeat them 3 times close together, and then stagger them. (Imagine the 3 repeated planes on Combat, or the 3 players on Football. Then imagine 2 sets of those interleaved with each other) That gets you halfway there, but then you have to very exact timings to update the sprite's graphic data mid-scanline. Because the Atari is so doggone slow, the timings are really tricky. (Although the technique eventually became fairly well-known -- any game with a 6-digit score display most likely used this technique).
I got the code all working thanks to that sample code I posted to above, but if I fiddled much with the graphics data, the timings would get off by 1 clock cycle, and it would fail. Turns out, again, that it was crossing a page boundary. This time with a LDA instruction (loading the accumulator from memory) -- if you do a "LDA foo,X" command, which loads the accumulator with whatever byte is at memory location (foo + the value of the X register), and foo+X crosses a page boundary from foo, it takes 5 cycles instead of 4.
Luckily the assembler gives you a nice "align" pseudo-opcode that lets you force alignment of your data into page boundaries, which fixed it.
After getting the title displayed, I wanted to make it pretty. I have 8 whole clock cycles free per line to do what I want with, but to update the color of the whole thing, I have to write to 2 registers (COLUP0 and COLUP1, the color registers for each sprite), which alone takes 6 cycles. There's not enough time left to pick or calculate the color for the scanline.
So I unrolled the kernel loop by half, which lets me use 16 clock cycles every 2 lines. That's plenty of time to load a base color, add to it based on the current line, and then write it to the color registers. The end result is a pretty animated title graphic:
Right now the PASSWD option doesn't do anything :-( |
The half-unrolled loop ended up looking like:
.loop:
sty tmpVar ; 3
;update the color
tya ; 2
adc PlayerColorDef ; 3
sta COLUP0 ; 3 = 11
lda (ptr+$0),y ; 5
sta GRP0 ; 3
lda (ptr+$2),y ; 5
sta GRP1 ; 3
lda (ptr+$4),y ; 5
sta GRP0 ; 3
lda (ptr+$6),y ; 5
sta tmpVar2 ; 3 = 32
lax (ptr+$a),y ; 5
lda (ptr+$8),y ; 5
ldy tmpVar2 ; 3
sty GRP1 ; 3
sta GRP0 ; 3
stx GRP1 ; 3
sta GRP0 ; 3 = 25
ldy tmpVar ; 3
dey ; 2
;burning 3 cycles in place of the bpl since the loop is half-unrolled
sty tmpVar ;3 (in place of the bpl)
sty tmpVar ; 3
;update the color
tya ; 2
adc PlayerColorDef ; 3
sta COLUP1 ; 3 = 11
lda (ptr+$0),y ; 5
sta GRP0 ; 3
lda (ptr+$2),y ; 5
sta GRP1 ; 3
lda (ptr+$4),y ; 5
sta GRP0 ; 3
lda (ptr+$6),y ; 5
sta tmpVar2 ; 3 = 32
lax (ptr+$a),y ; 5
lda (ptr+$8),y ; 5
ldy tmpVar2 ; 3
sty GRP1 ; 3
sta GRP0 ; 3
stx GRP1 ; 3
sta GRP0 ; 3 = 25
ldy tmpVar ; 3
dey ; 2
bpl .loop ; 2/3 = 7/8
Saturday, January 16, 2016
Crossing Page Boundaries
For awhile, everything was working perfectly in my 3-enemy display kernel on Atari Anguna. Then this weekend, I added some new graphics (for the crocky boss monster at the end of dungeon 1).
Suddenly, things stopped working right. Enemies started jumping around horizontally instead of smoothly moving. It looked choppy and horrible. And all I did was add some new graphics data.
HUH?
Well, the issue was probably pretty obvious to any veteran Atari programmers.
On the Atari, when you reuse a sprite with a new horizontal position, you often get the weird black lines that appear on the left of the screen. I wrote a post about it awhile back, how if you write to the re-positioning register on exactly the 74th clock cycle the scanline, you'd prevent the line. If you did it earlier, you'd get the line. If you did it later, it might not HMOVE at all.
Another important piece of the puzzle is the 6502's conditional branch opcodes. In general, they take 2 clock cycles if the jump wasn't taken, and 3 if they do. BUT if the jump passes a memory page boundary, it takes 4 cycles. (the 6502 divides memory into pages of 256 bytes each).
So it turns out, by adding additional graphics data, which ended up coming BEFORE my kernel code, it relocated the kernel code slightly, pushing one of my branches across a page boundary, and changing the clock cycle count of the routine by 1. Which made the HMOVE come too late, which made my re-position fail!
Well, the solution ended up being really easy, once I figured it out. Moving all the graphical data AFTER the kernel logic fixed it, and made sure that additional changes won't end up moving the kernel code.
WHEW.
And with that, I have most of a re-creation of the first dungeon of Anguna finished for the Atari. Since I had decided somewhere along the line that the first dungeon would be the same, but the rest would be mapped out differently, this means it's time to start doing level design again!
Really, at this point, the majority of the game logic is done. I still want to add a title screen, game over sequence, maybe a subscreen that shows what items/keys/etc you have, maybe a password entry system (like old NES games) so you can save your progress, and if I'm not sick of the game by then, a shop where you can buy stuff. Wow, that sounds like a lot when I list it that way....
Suddenly, things stopped working right. Enemies started jumping around horizontally instead of smoothly moving. It looked choppy and horrible. And all I did was add some new graphics data.
HUH?
Well, the issue was probably pretty obvious to any veteran Atari programmers.
On the Atari, when you reuse a sprite with a new horizontal position, you often get the weird black lines that appear on the left of the screen. I wrote a post about it awhile back, how if you write to the re-positioning register on exactly the 74th clock cycle the scanline, you'd prevent the line. If you did it earlier, you'd get the line. If you did it later, it might not HMOVE at all.
I've shown pictures of this before, but here's a sample of the black lines from doing the mid-frame HMOVE |
Another important piece of the puzzle is the 6502's conditional branch opcodes. In general, they take 2 clock cycles if the jump wasn't taken, and 3 if they do. BUT if the jump passes a memory page boundary, it takes 4 cycles. (the 6502 divides memory into pages of 256 bytes each).
So it turns out, by adding additional graphics data, which ended up coming BEFORE my kernel code, it relocated the kernel code slightly, pushing one of my branches across a page boundary, and changing the clock cycle count of the routine by 1. Which made the HMOVE come too late, which made my re-position fail!
Well, the solution ended up being really easy, once I figured it out. Moving all the graphical data AFTER the kernel logic fixed it, and made sure that additional changes won't end up moving the kernel code.
WHEW.
And with that, I have most of a re-creation of the first dungeon of Anguna finished for the Atari. Since I had decided somewhere along the line that the first dungeon would be the same, but the rest would be mapped out differently, this means it's time to start doing level design again!
Fighting the Crocky boss. Like the GBA version, the Bow and Arrows make this fight easier! |
Really, at this point, the majority of the game logic is done. I still want to add a title screen, game over sequence, maybe a subscreen that shows what items/keys/etc you have, maybe a password entry system (like old NES games) so you can save your progress, and if I'm not sick of the game by then, a shop where you can buy stuff. Wow, that sounds like a lot when I list it that way....
Monday, January 11, 2016
Making things!
A few random updates:
- My Life in Space game is done, I think. The Jam ends in about 5 days. I'm doing some last testing, and then will submit it. If anyone has an Android phone and wants to test for me, holler. It's pretty small compared to some of the crazy stuff that other people are doing for the jam, but I'm just happy to have a finished project that I think is interesting.
- I'm finally about done with the crop insurance project, which means more time for hobby projects!
- I've actually started working on Atari Anguna again in earnest. I'll probably be talking a bit more about that, but I've done a bunch of work on cleaning up my half-finished door code, fixed a number of small timing issues with my kernel, and even got the Bow and Arrow item ALMOST working!
- I really liked having 2 projects at once with Robo-Ninja and Atari Anguna, so that means it's time to start thinking about my 2nd project. Right now, the major options are either port Anguna to Android (using the C++ code that I had 2/3 finished from years ago when I attempted to port it to iPhone), or start on a NES game. I'm leaning toward the NES game, which means I might spend a lot of time here droning on about the interesting differences between the Atari and the NES. (They both use the 6502 processor, but there's not much similarity beyond that!)
Subscribe to:
Posts (Atom)
NES Anguna
Well, I had a little bit of time still, while Frankengraphics is finishing up her game Project Blue, to have a little downtime on Halcyon, s...
-
So I've recently purchased DosBox Turbo , the best (as far as I can find) implementation of DosBox for Android, and have been playing th...
-
Well, I had a little bit of time still, while Frankengraphics is finishing up her game Project Blue, to have a little downtime on Halcyon, s...
-
When I'm too tired/frustrated to work on my NES game, I've been playing around with ideas for another hobby project that I've he...