I've recently finished GBAguys horribly inaccurate tutorial. The sound part about bit 5 of $4000 really pissed me off. And so did the .db 20 initial do-nothing variable setting. $4000.5 is to turn on the down count not another redundant squarewave switch.
But I don't think I'm permanently damaged from it. I just came back from reading these two post
http://nesdev.com/bbs/viewtopic.php?t=3605
http://nesdev.com/bbs/viewtopic.php?t=3751
So I'm switching to asm6 and looking for the nes101 tut.
My main concerns are how messed up my code is and what emu to use. I tested about 10 emulators and not a one of them ran my code the same way. I used FCEMU for 90 of the code and it was fine. Then I tested in JNES and the pallet didn't even resemble the intended one.
Also my code looks like hell. It feels really toomuch like patchwork/copypase. I have to setup small stupid loops because of the branching and I have to check every number I want since I don't have or haven't found greater then and less then.
Ignore comments since they are from gbaguy tut.
Also, I know running loops sprite switching is screwed. I'm just lazy.
I would attach the code and rom if I had an upload feature...
Thanks all!
Code:
.inesprg 1
.inesmap 0
.inesmir 1
.ineschr 1
.bank 1
.org $FFFA
.dw VBlank_Routine ; address to execute on VBlank
.dw Start ; address to execute on reset
.dw 0 ; no whatever
.bank 0
.org $0000
falling .db 0
face_left .db 0
running .db 0
running_sprite .db 0
sprite_change .db 0
VBlankOrNo .db 0
.org $0300 ; OAM Copy location $0300
Sprite0_Y: .db 20 ; sprite #1's Y value
Sprite0_T: .db 0 ; sprite #1's Tile Number
Sprite0_S: .db 0 ; sprite #1's special byte
Sprite0_X: .db 20 ; sprite #1's X value
Sprite1_Y: .db 20 ; sprite #1's Y value
Sprite1_T: .db 0 ; sprite #1's Tile Number
Sprite1_S: .db 0 ; sprite #1's special byte
Sprite1_X: .db 20 ; sprite #1's X value
Sprite2_Y: .db 20 ; sprite #1's Y value
Sprite2_T: .db 0 ; sprite #1's Tile Number
Sprite2_S: .db 0 ; sprite #1's special byte
Sprite2_X: .db 20 ; sprite #1's X value
Sprite3_Y: .db 20 ; sprite #1's Y value
Sprite3_T: .db 0 ; sprite #1's Tile Number
Sprite3_S: .db 0 ; sprite #1's special byte
Sprite3_X: .db 20 ; sprite #1's X value
Sprite4_Y: .db 20 ; sprite #1's Y value
Sprite4_T: .db 0 ; sprite #1's Tile Number
Sprite4_S: .db 0 ; sprite #1's special byte
Sprite4_X: .db 20 ; sprite #1's X value
Sprite5_Y: .db 20 ; sprite #1's Y value
Sprite5_T: .db 0 ; sprite #1's Tile Number
Sprite5_S: .db 0 ; sprite #1's special byte
Sprite5_X: .db 20 ; sprite #1's X value
; this would just go on and on for however many sprites you have
.org $8000 ; code starts at $8000 or $C000
VBlank_Routine:
;start of function to execute on VBlank
inc VBlankOrNo
; add one (1) to VBlankOrNo, will be 1 if VBlank, 0 if not.
rti ; RTI is (Interrupt RETurn or ReTurn from Interrupt)
Start:
lda #%10001000 ;
sta $2000 ;
lda #%00011110 ; Our typical PPU Setup code.
sta $2001 ;
ldx #$00 ; clear X ;; start of pallete loading code
lda #$3F ; have $2006 tell
sta $2006 ; $2007 to start
lda #$00 ; at $3F00 (pallete).
sta $2006
loadpal: ; this is a freaky loop
lda tilepal, x ; that gives 32 numbers
sta $2007 ; to $2007, ending when
inx ; X is 32, meaning we
cpx #32 ; are done.
bne loadpal ; if X isn't =32, goto "loadpal:" line.
;; end of pallete loading code
lda #$20
sta $2006 ; give $2006 both parts of address $2020.
sta $2006
ldx #$00
loadNames:
lda ourMap, X ; load A with a byte from address (ourMap + X)
inx
sta $2007
cpx #255 ; map in previous section 64 bytes long
bne loadNames ; if not all 64 done, loop and do some more
addzeros:
lda 0
sta $2007
inx
cpx #255
bne addzeros
addzeros1:
lda 0
sta $2007
inx
cpx #255
bne addzeros1
addzeros2:
lda 0
sta $2007
inx
cpx #128
bne addzeros2
ldx $0
loadfloor:
lda floorMap, X ; load A with a byte from address (ourMap + X)
inx
sta $2007
cpx #32 ; map in previous section 64 bytes long
bne loadfloor ; if not all 64 done, loop and do some more
setup_Y_X:
lda #$20
sta Sprite0_Y
sta Sprite0_X
lda #00
sta Sprite0_S
sta Sprite1_S
sta Sprite2_S
sta Sprite3_S
sta Sprite4_S
sta Sprite5_S
lda #1
sta Sprite1_T
lda #2
sta Sprite2_T
lda #3
sta Sprite3_T
lda #4
sta Sprite4_T
lda #5
sta Sprite5_T
Setupteh_Sprite:
lda face_left
cmp #0
beq faceleftloop
facerightloop:
lda #%01000000
sta Sprite0_S
sta Sprite1_S
sta Sprite2_S
sta Sprite3_S
sta Sprite4_S
sta Sprite5_S
lda Sprite0_Y
sta Sprite1_Y
adc #7
sta Sprite2_Y
sta Sprite3_Y
adc #8
sta Sprite4_Y
sta Sprite5_Y
lda Sprite0_X
sta Sprite2_X
sta Sprite4_X
sbc #7
sta Sprite1_X
sta Sprite3_X
sta Sprite5_X
jmp infinite
faceleftloop:
lda #00
sta Sprite0_S
sta Sprite1_S
sta Sprite2_S
sta Sprite3_S
sta Sprite4_S
sta Sprite5_S
lda Sprite0_Y
sta Sprite1_Y
adc #7
sta Sprite2_Y
sta Sprite3_Y
adc #8
sta Sprite4_Y
sta Sprite5_Y
lda Sprite0_X
sta Sprite2_X
sta Sprite4_X
adc #8
sta Sprite1_X
sta Sprite3_X
sta Sprite5_X
infinite: ; a label to start our infinite loop
WaitForVBlank:
lda VBlankOrNo ; A = VBlankOrNO
cmp #1 ; if A == 1 then is VBlank
bne WaitForVBlank ; if not VBlank, then loop and do again
dec VBlankOrNo ; 1-- or VBlankOrNO - 1 . VBlankOrNo will be 0 again.
lda #3
sta $4014
lda #$01 ; these
sta $4016 ; lines
lda #$00 ; setup/strobe the
sta $4016 ; keypad.
lda $4016 ; load Abutton Status ; note that whatever we ain't interested
and #1 ; AND status with #1
bne jump_up
lda Sprite0_Y
cmp #200
beq un_fall
jmp set_fall
jump_up:
lda falling
cmp #1
beq fall
lda Sprite0_Y
cmp #150
beq set_fall
cmp #200
beq jump_sound
cmp #154
beq slow_jump
cmp #153
beq slow_jump
cmp #152
beq slow_jump
cmp #151
beq slow_jump
dec Sprite0_Y
dec Sprite0_Y
jmp check_b
slow_jump:
dec Sprite0_Y
jmp check_b
jump_sound:
lda #%11011111 ; #not# typical
sta $4000 ; write
lda #%10101011 ; % means binary number, remember the '#' for immediate values.
sta $4001 ; immediate means "not an address, just a number".
lda #$FF
sta $4002
lda #%01111000
sta $4003
lda #%00000001
sta $4015
dec Sprite0_Y
dec Sprite0_Y
un_fall:
lda #0
sta falling
jmp check_b
set_fall:
lda #1
sta falling
fall:
lda Sprite0_Y
cmp #200
beq un_fall
cmp #153
beq slow_fall
cmp #152
beq slow_fall
cmp #151
beq slow_fall
inc Sprite0_Y
inc Sprite0_Y
jmp check_b
slow_fall:
inc Sprite0_Y
check_b:
lda $4016 ; load Bbutton Status ; in we just load so it'll go to the next one.
and #1
bne BKEYdown
check_l_r:
lda $4016 ; load Select button status
lda $4016 ; load Start button status
lda $4016 ; load UP button status
lda $4016 ; load DOWN button status
lda $4016 ; load LEFT button status
and #1 ; AND status with #1
bne LEFTKEYdown
lda $4016 ; load RIGHT button status
and #1 ; AND status with #1
bne RIGHTKEYdown
jmp Setupteh_Sprite
BKEYdown:
lda #1
sta running
sta running_sprite
jmp check_l_r
LEFTKEYdown:
lda running
cmp #1
beq run_left
dec Sprite0_X
lda #0
sta face_left
inc sprite_change
jmp check_sprite_change
run_left:
dec Sprite0_X
lda #0
sta running
jmp LEFTKEYdown
RIGHTKEYdown:
lda running
cmp #1
beq run_right
inc Sprite0_X
lda #1
sta face_left
inc sprite_change
jmp check_sprite_change
run_right:
inc Sprite0_X
lda #0
sta running
jmp RIGHTKEYdown
check_sprite_change:
lda running_sprite
cmp #1
bne sprite_load_new
lda #0
sta running_sprite
lda sprite_change
cmp #0
beq sprite1
cmp #3
beq sprite2
cmp #6
beq sprite3
cmp #8
beq reset
jmp Setupteh_Sprite
sprite_load_new:
lda sprite_change
cmp #0
beq sprite1
cmp #6
beq sprite2
cmp #9
beq sprite3
cmp #11
beq reset
jmp Setupteh_Sprite
sprite1:
lda #2
sta Sprite2_T
lda #3
sta Sprite3_T
lda #4
sta Sprite4_T
lda #5
sta Sprite5_T
jmp Setupteh_Sprite
sprite2:
lda #8
sta Sprite2_T
lda #9
sta Sprite3_T
lda #10
sta Sprite4_T
lda #11
sta Sprite5_T
jmp Setupteh_Sprite
sprite3:
lda #14
sta Sprite2_T
lda #15
sta Sprite3_T
lda #16
sta Sprite4_T
lda #17
sta Sprite5_T
jmp Setupteh_Sprite
reset:
lda #0
sta sprite_change
jmp Setupteh_Sprite
tilepal: .incbin "agumon.pal" ; a label for our pallete data
ourMap: .incbin "agumon.map" ; assuming our.map is the binary map file.
floorMap: .incbin "floormap.map"
.bank 2
.org $0000
.incbin "alpha.bkg"
.incbin "agumon.spr"
The first thing that is seriously wrong with this code is how the PPU is configured only once at the start of the program (writes to $2000 and $2001).
The correct way to configure the PPU is to write 0's to both registers (keeping rendering disabled). Then you must wait at least 2 frames for the PPU to become usable (it needs some time to become ready). You can just wait for VBlank twice, by reading bit 7 of $2002. You are free to do other things not related to the PPU while you are waiting for it to be ready, if you'd like, such as clearing memory or setting up variables.
After those 2 frames have passed, you can use the PPU. You should keep rendering disabled while you do this. Only when you are done you can finally turn rendering on (by writing to $2001).
The bad thing about your code is that you keep rendering enabled from the start, and then you just update the palette, write to the name tables... One of the most important rules of NEs programming is that YOU CAN'T WRITE DATA TO THE PPU (USING $2006 AND $2007) WHILE THE SCREEN IS RENDERING. If you do it, you not only get garbled graphics while you're doing it, but the writes will most likely go wrong and the PPU memory will be corrupt (wrong palettes, tiles, etc).
So, the first thing to do is replace those 2 writes to $2000 and $2001 to write 0 to both locations. Then wait for 2 frames to go by... this can be done in a simple loop, where you read $2002 waiting for bit 7 to be set, twice. Then, after all the data has been written to the PPU, wait for VBlank again, so that you can enable rendering at a convenient time (instead of in the middle of a frame). THEN you write the definitive values to $2000 and $2001 (the values you have now).
But never forget that you can't write data to the PPU while it's rendering graphics. If you need to write a lot of data to it (like when you are drawing a full screen), you must disable the rendering of sprites and background. Only when you're finished you can enable rendering again.
During VBlank, you may send data to the PPU without disabling rendering, since VBlank is the time while the PPU is inactive anyway. But you must be careful not to go beyond VBlank time (about 2273 CPU cycles), or things will go pretty wrong, like I said earlier.
Another pretty bad thing is that you seem to never set the scrolling registers through $2005. If you don't do this, rendering will start from a "random" location inside the name tables, and you'll hardly see what you expect. Be sure to ALWAYS set the scrolling AFTER you are finished with $2006 and $2007, and BEFORE rendering starts.
If you are drawing tiles to the first name table, be sure to write 0 to $2005 twice (so that X and Y scroll are both 0). You also need to select which of the 4 name tables you want to show, and you do that by using the lower 2 bits of register $2000. You must do this every frame, after you are finished with writing stuff to the PPU.
Wow. Thanks for the fast reply. Again, this is taken straight from gbaguy so I thought something might be *fundamentally* wrong.
Might that be why it throws sprite 0 into the very (PAL) top left? I guess changing will answer that. It's almost 1 her so I need some sleep. I'll try to implement all that tomorrow.
Thank you!
Another thing that make me wondering if how you declare your variables. It's really weird how you do this org .$300 and those .db $20, I wonder where you got this out (probably GBA guy ?). Oh, my, .db $20 in RAM what was that guy thinking make a tutorial with THIS inside ? He probably wrote some stuff he never tried to assemble, let alone maybe testing this on an emulator.
While all of this may not be absolutely necessary, I strongly recommand setting the interupt disable flag (sei), initialise the stack pointer to $ff, and mute the sound at startup. I also recommand clearing your memory, but that isn't really necessary if you initialise each variable individually when you use them. You just cannot rely on them as having any initial value. If you initialise all variables to 0, then you are sure they have the value 0 at startup. This can also being a bad thing, because it's basically bad to rely on an initial value for newbies.
Bregalad wrote:
It's really weird how you do this org .$300 and those .db $20, I wonder where you got this out (probably GBA guy ?).
What is the correct way to declare variables in NESASM then? The other assemblers I've worked with had commands to reserve bytes, I'm not sure if NESASM has anything like this. Directly assigning values to the labels (Score = $05) is not practical at all.
Quote:
I strongly recommand setting the interupt disable flag (sei)
That's quite important actually, since there is no IRQ handler defined in that code (he uses 0 for the vector). Should an IRQ happen (and AFAIK, APU IRQ's are enabled on startup), the 6502 will try to execute code from RAM address $0000, and that would be disastrous. So yeah, "sei" should be the very first command in the program.
Quote:
initialise the stack pointer to $ff, and mute the sound at startup.
These are less important, because the effects of not doing that are much less disastrous (for most uses, the stack pointer can be anything as long as you don't touch the memory at $0100-$01ff, and if the NES happens to output any kind of sound, that won't break your program either).
Quote:
I also recommand clearing your memory, but that isn't really necessary if you initialise each variable individually when you use them.
My personal point of view is that clearing the whole memory is bad, because it induces bad programming. There is simply no need to do it if you are careful and initialize every variable. You have to be careful anyway if you want to reuse variables as you go along (for example, your bonus stage and your main game engine can use the same RAM for different purposes, since they are never executed together)... I'd rather only clear blocks of RAM in the case of arrays or other large structures, but never the whole memory.
Some newbies like that idea because it sometimes does fix whatever is wrong with their programs, but that's because they weren't careful enough with their variables in the first place, and that part should be worked on, but instead people prefer to patch the problem with a memory-clearing routine, something that doesn't teach them anything about better programming practices.
Quote:
What is the correct way to declare variables in NESASM then? The other assemblers I've worked with had commands to reserve bytes, I'm not sure if NESASM has anything like this. Directly assigning values to the labels (Score = $05) is not practical at all.
I remeber assigning values manually to all my variables back when I used NESAsm, but that wasn't for a very long time anyway. On a "normal" assembler .db $20 reserves $20 bytes or RAM, while .db alone is for one byte. .db 0 has no sense, and often they use dsb instead of db to avoid confusing with .db in ROM wich directly set bytes.
Quote:
That's quite important actually, since there is no IRQ handler defined in that code (he uses 0 for the vector). Should an IRQ happen (and AFAIK, APU IRQ's are enabled on startup), the 6502 will try to execute code from RAM address $0000, and that would be disastrous. So yeah, "sei" should be the very first command in the program.
Normally the I flag is automatically set on each CPU reset (no matter if it's hard or soft), just like it is automatically set on IRQs and NMIs. I'm not sure if many emulators does it that way. Doing a cli later withotu having a IRQ routine would, however, have the effect you mention.
Quote:
Some newbies like that idea because it sometimes does fix whatever is wrong with their programs, but that's because they weren't careful enough with their variables in the first place, and that part should be worked on, but instead people prefer to patch the problem with a memory-clearing routine, something that doesn't teach them anything about better programming practices.
You are probably right, and I usually try to have variables initialised the good way (even if it can sometimes leaves less efficient code). Howver, in my current project I still have a memory reset routine at startup, and to be honnest I tried removing it and my game crashed... (but only when gameplay begin). I don't know wich variable exactly I rely to be zero on startup, but there is at least one in this case.
Finally it's pratical for example the sound code routine have an "channel enabled" flag, and the music will be disabled on a particular channel if this variable is zero, while having it set to different value can have various effect. You'll admit that it's way easier to have the programm clearing the memory at startup and then you can call the sound programm directly without wondering anything, it won't try to play unexistant music or anything. If you don't clear the memory then you have to make a routine that proprely initialise the sound routine, and you'll admit that this is much less efficient. The same aplies to other domains as well, but sound code is the most representative.
i zero out ram because i (usually) have nothing else to do for 2 frames.
the idea of wasted cycles is frightening.
You really don't need to wait two frames at startup, where did this idea come from?
It works! Thanks everyone! Before it worked on about 6 out of 10 emus now it works on all of them
. The only problem is part of the background is missing under Jnes. I assume this will fix it --v
Quote:
If you are drawing tiles to the first name table, be sure to write 0 to $2005 twice (so that X and Y scroll are both 0). You also need to select which of the 4 name tables you want to show, and you do that by using the lower 2 bits of register $2000. You must do this every frame, after you are finished with writing stuff to the PPU.
I gave $2005 two zeros but I don't know what to feed $2000. Do I give it normal 01 and 10 or does it need something else strange?
From NES 101 --v
Quote:
We're only scrolling vertically, which means that we should use Horizontal Mirroring. Our name tables will be at $2000 (mirrored at $2400) and $2800 (mirrored at $2C00) in VRAM.
I want to add scrolling so it can at least slightly resemble a game. I guess that would be horizontal mirroring? Is it the direction or axis that they share?
Thanks allot.
On most assemblers, .db $nn defines one or more bytes of constant data. .ds n is what reserves n bytes without specifying their values. BTW, it makes no sense to assign values to RAM, since the assembler doesn't generate any initialization code, just gives names to addresses. At the very least an assembler should give a warning if you attempt to give RAM an initial value.
As for clearing all RAM, it makes a lot of sense to me now, since you can declare a variable then rely on it being initialized to zero if you haven't used it yet. This eliminates lots of pointless clearing to zero before first use and the clutter that comes with it. That alone reduces bugs.
kyuusaku wrote:
You really don't need to wait two frames at startup, where did this idea come from?
i don't know where it originated from, but i read it before and did it since it only adds 4 lines of code. the
wiki has it under init code examples.
blargg wrote:
On most assemblers, .db $nn defines one or more bytes of constant data. .ds n is what reserves n bytes without specifying their values. BTW, it makes no sense to assign values to RAM, since the assembler doesn't generate any initialization code, just gives names to addresses. At the very least an assembler should give a warning if you attempt to give RAM an initial value.
On wla-dx, the proper way to declare a signe-byte variable called "MyVariable" is :
Code:
.ramsection "MyRamSection"
MyVariable db
.ends
If you replace db per .db, it's about the same exept that the index isn't incremented, so the next db will assign the label to the same value. This is usefull to give multiple name to the same variable or if you want to declare it once as two separate bytes and once at a word.
Quote:
You really don't need to wait two frames at startup, where did this idea come from?
Of course this is needed if you want to do anything with the PPU (exept than turning it off). This has been widely doccumented even since the Nesticle days, so I don't know where you are from. However to do uniquely CPU process, and most probably joypad reading will work straight on reset. I don't know about sound, I remember it had a short warming up time too. And I don't see what clearing RAM has to do with waiting 2 frames, exept that both are things you would usually do at startup.
Quote:
I want to add scrolling so it can at least slightly resemble a game. I guess that would be horizontal mirroring? Is it the direction or axis that they share?
There is MANY scrolling methods, and the mirroring you want to use all depend on the scrolling method, how far your screen is scrolling or how you want to repeat it, if your cartridge has scanline IRQs or not, and what kind of mirroring your cartridge allows. Assuming it only allows the standard H/V mirroring, and that you have no status bar nor rasting effect, you want to use horizontal mirroring to scroll vertically and vice verse. Because to scroll vertically, you want screens to be arranged vertically, so mirrored horizontally.
Bregalad wrote:
Of course this is needed if you want to do anything with the PPU (exept than turning it off). This has been widely doccumented even since the Nesticle days, so I don't know where you are from.
It may be "documented" but is it rooted in any fact? I thought this was debunked a while ago, so where were you? I know on my Famicom I can immediately start using the PPU address register, so it's not a rule, maybe an obscure exception, but probably not even.
I have definitely found odd PPU behavior when trying to use it immediately. Same for APU. Commercial games often wait at least two frames, adding to the evidence.
What could you be waiting for though? An RC time constant in the POR circuit?
blargg wrote:
On most assemblers, .db $nn defines one or more bytes of constant data. .ds n is what reserves n bytes without specifying their values. BTW, it makes no sense to assign values to RAM, since the assembler doesn't generate any initialization code, just gives names to addresses. At the very least an assembler should give a warning if you attempt to give RAM an initial value.
True, if the RAM segment is set to a "BSS" type. But there is another type of RAM segment called "DATA" that is designed to be copied from ROM at reset time.
Quote:
As for clearing all RAM, it makes a lot of sense to me now, since you can declare a variable then rely on it being initialized to zero if you haven't used it yet.
And in fact, C guarantees that uninitialized variables at file scope are zero at the start of main().
kyuusaku wrote:
What could you be waiting for though? An RC time constant in the POR circuit?
Cap charging might be part of it, but I guessed that it had something to do with state. It's likely that the PPU's internal state isn't necessarily consistent until it has rendered a complete field, and the easiest way to detect that the PPU has been through a complete field is to wait for the vblank before it and the vblank after it.
tepples wrote:
Quote:
As for clearing all RAM, it makes a lot of sense to me now, since you can declare a variable then rely on it being initialized to zero if you haven't used it yet.
And in fact, C guarantees that uninitialized variables at file scope are zero at the start of main().
I don't know guys, I'm still not convinced! =) The "if you haven't used it yet" part can be very tricky, because this condition may be true the first time you run a piece of code, but not at subsequent times... if this was the case, a possible bug would be pretty difficult to find. But if you instead initialized the variable at a convenient time, it would be correctly set whenever that piece of code was executed.
I'm not trying to convince anyone (as I'm sure nobody is trying to convince me) of anything, I'm just trying to express my point of view on variables and how to initialize them, since I seem to be alone on this one.
My current project has a pretty complex scrolling system, a pretty complex sprite system, and I can get away just fine by initializing memory only when I need it. I think this is the case because there really isn't a value I use that much that would justify writing it to every memory location. 0 is pretty much meaningless for most of my code...
Even when I (supposedly) do need to clear a block of RAM, such as the object slots used for active objects, I can usually get away with clearing just a small part of it. In the case of the object slots, each one has a byte that indicates the type of the object loaded there, and I know that the engine will check that value before anything else, and if I use 0 as a flag to indicate that the slot is empty, what do I care about the crap stored in the other bytes? And since I will surely clear those "object type" bytes before a level (re)starts, I see no need to clear that memory before that.
But I know this is only my case, and I guess I can accept that people out there will benefit from clearing it all on start up. This is not for me though... Even when programming in high-level languages I can't seem to assume any values on things I haven't initialized. OK, shutting up now.
In fact Tokumaru you are mostly right, it's better to rely on clean programming methods. It's simply that on the NES, it's often best to put code efficiency over colde clarity, so in some cases, but not always, initialising memory can increase code efficiency. The sound code exemple I give above is typical : Having the clearing RAM routine do it instead of a custom "SoundInit" routine is more efficient, even if that's not the cleaner programming method ever. Once the game starts, if you want to stop a music you'll have to call music_stop or something, so maybe just calling music_stop on startup would also do the trick very well.
Finally, anyone is free to clear the whole RAM on startup, only a part of it or nothing at all, I guess there is nothing better or worse here. Depend on the programmer. Final Fantasy games are the only commercial games that doesn't initialise RAM at all as far I noted. Some game however only initialise zero-parge RAM or not the entiere "bss" RAM.
I got some basic scrolling and collision detection going but I'm having palette trouble. How to do use multiple palettes on the background? Say I wanted a black and white could, a green bush, a yellow sun and brown ground. How would I make some bg sprites use a different palette from other bg sprites?
Thanks
Look up the Attribute Tables. It's a bit complicated to explain everything, so you should probably read all the documentation that is already avaliable about them, then come back here and ask about what you didn't understand! =)
I have looked at lots of things on the Attribute tables but none of them tell how to use it. I found hardware docs but I didn't find any software explanations. $23C0 is attribute table 0 and every byte controls 4 bg sprites in a square. But how do I interface it? I read the documentation on the NSA program but the code was too cryptic and I didn't see that it wrote to $23c0 so it didn't really help. I did some guess and check by dumping values into $23c0 but it only seemed to offset the sprites and leak bg tiles into foreground sprites.
One thing at a time but, how big of a pain is collision detection? I've just used sprite 0 hit as a check for hitting while jumping. I don't know how to test falling or walking collision. If you could point me in the direction of some tutorials that would be greatly appreciated.
Thanks for everything.
There is plenty of docs arround that proprely doccument how attribute table works. Unlike the Name Table, the attribute table is only 8 blocks long and 8 blocks tall, taking 64 bytes (the nametable is 32 blocks long and 30 blocks tall) so converting adresses between the two can be a pain. Each byte control a block of 4x4 BG tiles, so the attribute byte at $23c0 correspond to tiles at $2000, $2001, $2002, $2003, $2020, $2021, $2022, $2023, $2040, $2041, $2042, $2043 and $2060, $2061, $2062 and $2063, this is the topmot leftmost 8x8 BG tiles corner.
The simpler way to see this is that attribute bytes at $23c0-$23c7 controls the first 4 lines of tiles ($2000-$207f), attributes byte at $23c8-$23cf constol the next 4 lines of tiles ($2080-$20ff), etc... Each line in nametable is $20 bytes long, each 4 lines are then $80 bytes long, and an attibute line, which is $8 bytes long have control over groups of 4 lines.
Each group of 2 bits in an attribute byte correspond to a smaller block of 2x2 tiles among the 4x4 block mentionned above, allowing attribute tables to control colors over groups of 2x2 tiles. Note that the attribute table allows to give attribute to 32x32 tiles, while the map is only 32x30, so there is a "ficionnal row of tiles" you give them colors, but the tiles themselves are usually never drawn on the screen. You can still draw them from the attribute table itself (wich can also be drawn as a nametable) by setting the vertical scroll values $f0-$ff, wich normally should be never used. There is hardly any good use of this, tough.
Now you should know enough about attribute tables, if this isn't enough just read some docs.
Cool. I understand name table vs. attribute table allot better now. So 1 byte in attribute is 16 tiles in name and 2 bits in attribute is 4 tiles in name. I still don't understand what to send to $23c0 though. I modified gbaguy's name table loader for attribute like so...
Code:
ldx #0
load_atribute:
lda atribute_table, x
sta $23C0
inx
cpx #64
bne load_atribute
But I assume this is totally wrong because it did absolutely nothing.
Thanks for the attribute tutorial there. Much better than the docs I've read.
This is totally wrong. You must write to $23c0 in PPU's memory and not in CPU's memory. You must write $23, $c0 to $2006 then write your stuff in $2007.
I thought something seemed wrong there. Strange that writing there (in cpu) during vblank offsets sprites. Thanks for setting me straight on that. Really nice to have that working. Now off to edit palettes and name tables.
Skidlz wrote:
Strange that writing there (in cpu) during vblank offsets sprites.
That probably happens due to mirroring. The wiki says that
$23C0 is in a erea that mirrors the PPU registers, so when you wrote stuff there you were actually affecting
register $2000, which controls a few parameters of sprites and background.
Well, I got bored and I felt assembly meant a little more than ASCII so I wrote something.
Please Download -->
http://www.mediafire.com/?71xuinnmga1
You are welcome! =) I didn't do much though...!
I feel like I'm taking advantage of you guys(and or girls) by asking so many questions. Feel free to say something if I'm being a bother.
Well, I have an uncanny skill at underestimating and overestimating varyingly. Not as bad as my friend who typed "cows" as his source code and expected a game involving cows. But I assume collision detection is somewhat difficult and I've yet to find anything that addresses it. Only some confusing source from galaxy patrol. All I can manage is sprite 0 hit and I'm sure thats not how you go about doing this. If someone could point me towards a tutorial or give me a place to start that would be wonderful.
Skidlz wrote:
But I assume collision detection is somewhat difficult and I've yet to find anything that addresses it.
First would you like to learn about collision between sprite and sprite, or would you like to learn about collision between sprite and background?
Well of course I need to know both but I think bg cd is more important. Can't exactly fight enemies if you are falling through the floor (castlevania 2).
Does your game's background scroll, or is it still (like Donkey Kong)?
Yes it has bg scroll. Vertical I guess since it goes to the right. Due to lack of originality/creativity, think mario clone basically.
To get started with collision, first define what a collision is, in English. Once you have that solid, translate it to code. This step is likely to need subroutines that do the following:
- Sprite bounding box tracing: Given an actor's displacement, velocity, and size, compute a list of the (x, y) coordinates of all metatiles that its leading edge overlaps.
- Background object retrieval: Given (x, y) in world coordinates, return the number of the metatile in that position.
- Background object attributes: Given the number of a metatile, return whether this tile is passable.
Object collision with BG can be either very easy or very hard depending on how complicated is your BG. If you have blending tiles, tiles who moves the player automatically (such as moving sand or something) or that kind of stuff, things may be pretty compliceted. Assuming you just have a basic background, and that you just want make it walkable or not walkable, however, things are really simple.
First thing to do before a sprite-BG collision routine is a pixel-BG collision routine. For a given pixel position X and Y, you call a routine, the routine will convert the coordinate from pixel to tile, effecively finding which tile on the map is below your pixel, and then if the pixel is walkable return with carry set, and carry clear otherwise (or the other way arround it doesn't matter). There is a couple of different way to do this kind of stuff, just make your own.
Then, if you want to check if your player "can" go right, you have to check if lower-right corner and upper-right corner. If the player is bigger than your collision detection basis, you'll have to also check a middle-right point, or even more than this, but this is usually not needed. If all these checks passes, then you can add one to your player position effectively moving right. If at leat one of the checks fails, then the player cannot pass. Then just adapt it for other direction as well, it's exactly the same. If you're going to have gravity, you'll want to automatically have each object accelerate down, exept if they collide with the ground of course. That should do it.
Object/Object collision is even simpler, assuming all your object are simply rectangle shaped. If you want to do different shapes, I guess it's possible but should be a major pain. There is probably a couple of different ways to do it, I'll explain the way I did it. First have a routine that calculates an object's "corner coordinates". For example if the object is at (10, 20) on the screen and that it's tall of 8 and wide of 6, the corner coordinates would be (7, 12) (13, 12) (7, 20) and (13, 20). That is if your "object hot point" is on the bottom center of the object, which is usually the case. Then the object's bottom cordinate is the same as the hot point's coordinate, the object's top coordinate is hot point less height, the left point is hot point less half of width, and right point is hot point plus half of width. Adapt if your objects work differently tough.
Now that you have each "corner" coordinates of two objects you want to collide together, simply follow this logic :
If (object1HMax > ojbect2HMin) and (object1HMin < object2HMax) then the objects collide horizontally.
If (object1VMax > ojbect2VMin) and (object1VMin < object2VMax) then the objects collide vertically.
If object collide horizontally and vertically, they are in contact.
If can be usefull to do only vertical or horizontal collision detection to check if two objects are alighed, for example to trigger an enemy to do a particular attack when the player face it or for many other reasons.
I love your mini tuts Bregalad! I was thinking of something similar for bg collision. Tepples's post got me thinking a little and something resembling a pseudo code routine has been forming in my head over the day. Something like... yposition minus 8 until negative flag set, incrementing a value every dec 8. same for x. Add y_loop_count with x_loop_count and offset nametable by new value. Get value of tile in name table, then act accordingly. If my subconscious is as good at writing loops as I trust it to be, that should give something close to collision detection. I hope that made sence.
Here is some really rough code.
Code:
lda y_pos
sta temp_y_pos
y_div_8:
lda temp_y_pos
dec #8
sta temp_y_pos
inc y_loop_count
and the flags
beq to neg flag set then jmp x_div_8
jmp y_div_8
lda x_pos
sta temp_x_pos
x_div_8:
lda temp_x_pos
dec #8
sta temp_x_pos
inc x_loop_count
and the flags
beq to neg flag set then jmp add_values
jmp x_div_8
add_values:
lda y_loop_count
adc x_loop_count
tax
lda #0
sta y_temp_pos
sta x_temp_pos
Next_loop:
lda (nametable), x
cmp tile number aka brick type
beq to unpassable brick jmp to get_tile_y
jmp to the some other code
get_tile_y:
lda y_temp_pos
adc #08
sta y_temp_pos
dec y_loop_count
bne get_tile_y
get_tile_x:
lda x_temp_pos
adc #08
sta x_temp_pos
dec x_loop_count
bne get_tile_x
compare:
lda y_pos
cmp y_temp_pos
beq collide
lda x_pos
cmp x_temp_pos
beq collide
Is this close? It sounds like it would work to me but it feels to simple.
I really don't know how to do greater than/less than. I completely understand the corner checking logic and it sounds beautiful for square collision but I don't know how to implement gt ls.
Thanks all!
For unsigned comparisons, the 'cmp' and 'sbc' instructions set up the carry flag such that 'bcc' means "branch if accumulator was less than memory", and 'bcs' means "branch if accumulator was greater than or equal to memory". In fact, the data sheet for at least one of Western Design Center's 6502 series chips suggests the synonyms 'bge' for 'bcs' and 'blt' for 'bcc'.
Skidlz: Do you know how to use arrays in C or any other programming language? If so, you might be able to prototype your collision logic in this language and then hand-compile it to 6502 assembly language.
tepples wrote:
For unsigned comparisons, the 'cmp' and 'sbc' instructions set up the carry flag such that 'bcc' means "branch if accumulator was less than memory", and 'bcs' means "branch if accumulator was greater than or equal to memory"
Thats wonderful! Why have I net heard of these?
So I can scratch the flag checking and such.
tepples wrote:
Skidlz: Do you know how to use arrays in C or any other programming language? If so, you might be able to prototype your collision logic in this language and then hand-compile it to 6502 assembly language.
Not even that familiar with C. I wrote a few things for my calc in TIGCC but thats about the extent of my practice. I just glanced at some array tuts. Sounds like its a bunch of ints/strings put end to end in one long int/string and then temporarily parsed for output? I'm not seeing how thats immediately relative so I guess I misunderstood what arrays are.
Does my code look like its on the right track?
Thanks so much for sbc, bcc, bcs.
P.S. This is totally unrelated but I'm wondering, is a good method of inserting images floating around? I know the limitations of the system and that without drastic flickering tactics I will get very few colors. I did manage to break one image down to 4 colors and feed it through bmptochr.exe, so I know what image quality I'm dealing with. I was just wondering if there is an easy way to get an image into the red blue black white format.
*edit*I just realized in the code I need to multiply y by 32 to get the proper nametable offset.*edit*
Well, for the code I guess you'll have to try it yourself, it's hard to tell if it's good or not good just by looking at it like this. Some thing like "dec #$8" make me wondering, still. You'd want to use "sbc #$8" insted or something. dec is a bit like sbc #$01, exept the carry is not checked nor positionned.
For images, I guess nothing beats converting them manually, but I guess there is a few programms arround that can limit colors usually down to 16, this is a good start, then you limit the remaining colors "manually". I guess Tokumaru made a programm to covnert bmp->chr from files to avoid too much work, and it allows to do multiple layers (so you can place sprites on a BG or sprites on sprites at the same place on the screen, with different colors so that the image have more than 4). It's called nestc.exe, I don't know where you can find it.
Now that you say it, I see that dec was wrong there. I wrote it right on the forum and I guess dec sounded right at the time. It was meant to be more of a pseudo code/English explanation than executable code. Still I based my code off of that and I would have used dec accidentally. So thanks for pointing that out. I think I'm about ready to test the it so I will post results soon.