Lightgun game

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

WhoaMan
Posts: 167
Joined: Sat Oct 02, 2004 12:07 pm

Post by WhoaMan »

random light gun thought:

i was thinking a while ago about making a zapper demo, that showed a 16pixel grid on the screen, and when you shoot, it would show where you shot at, though at the expense of lower frame rates. what i was thinking, is that when the player pressed the trigger, it would check a dark screen as usual, then make 1/2 the screen white and the other half black vertically. after checking this, you would know what half of the screen the player was pointing at.... then you make 1/2 of the screen white and black once more, though this time horizontally. you know know what quadrant of the screen the user is pointing at, you then divide this further and further until you know the grid location. this would make for a crappy frame rate though.....
atari2600a
Posts: 324
Joined: Fri Jun 29, 2007 10:25 pm
Location: Earth, Milkyway Galaxy, The Universe, M-Theory
Contact:

Post by atari2600a »

wouldn't you have to wait an extra vblank for the original field to be wiped out from the interlaced video first? That would bring a single shot to 2-6FPS, & therefore very noticable & annoying in my opinion.

STILL wondering if the Zapper has to be strobed! :P

Code: Select all

          *=$0000
loop      JMP loop
          .eof
User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Post by kyuusaku »

"Strobing" latches bits on the controller's shift register, that's not necessary since there isn't a shift register in the Zapper. You just need to poll the Zapper bits: http://nesdevwiki.org/wiki/index.php/Zapper
atari2600a
Posts: 324
Joined: Fri Jun 29, 2007 10:25 pm
Location: Earth, Milkyway Galaxy, The Universe, M-Theory
Contact:

Post by atari2600a »

Okay, I'm trying to get this sprite to move, so I got this simple incrementing code in my vblank routine.

Well, either attachments are disabled or I'm overlooking the attachment button, so I'm just gonna copy/paste my entire far-from-finished code here:

Code: Select all

	*=$C000
	!cpu 6502
	!to "gameprg.bin", plain

;=============================init==================================

	CLC
	
vb1	LDA $2002
	BPL vb1
vb2	LDA $2002
	BPL vb2
	;
	LDA #$00
	LDX #$00
clearm	STA $00,X
	STA $0100,X
	STA $0200,X
	STA $0300,X
	STA $0400,X
	STA $0500,X
	STA $0600,X
	STA $0700,X
	INX
	BNE clearm
	
	
reset	SEI
	LDX #$ff
	TXS
	LDA #0
	STA $2000		; kill PPU
	STA $2001

	LDA #$20
	LDX #0
	LDY #0
nt1wrt	STA $2006
	STX $2006
	STY $2007
	INX
	CPY #00
	BNE nt1wrt
	CMP #$24
	ADC #1
	BNE nt1wrt

	LDA #$3F
	LDX #$00
pal1wrt	STA $2006
	STX $2006
	LDY bgpal,x
	STY $2007
	INX
	CPX #$10
	BNE pal1wrt
	
	LDA #$3F
	LDX #$10
pal2wrt	STA $2006
	STX $2006
	LDY sprpal,x
	STY $2007
	INX
	CPX #$10
	BNE pal2wrt
	
	LDA #00
spr1wrt	STA $2003
	LDY spr1,x
	STY $2004
	INX
	CPX #4
	BNE spr1wrt
	
	
	LDA #%10001000		;Enable graphics
	STA $2000
	LDA #%10011110
	STA $2001
	CLI
	
	
loop	JMP loop


;==========================vblank=======================================

vblank
	LDA scrstat
	CMP #1
	BNE skp1
	JSR blkchk
skp1	
	
	
	LDA #00
	STA $2003
	LDA $2004
	ADC #1
	STA $2004

	LDA $4016
	AND #%11101111		;check for trigger status
	CMP #%00000000
	BNE noshoot
	JSR shoot
noshoot
	RTI


shoot	LDA $2001
	AND #%00001000
	STA $2001
	INC scrstat
	RTS
	
blkchk	LDA $4016
	AND #%11110111		;check for black
	CMP #%00000000
	BNE usuck
	INC scrstat
	RTS
	
	
usuck	RTI			;temporary, need ASCII set first.
	
	
;==========================DATA=========================================

bgpal	!byte $30,$31,$32,$33,$34,$35,$36,$37
	!byte $38,$39,$3A,$3B,$3C,$3D,$3E,$3F

sprpal	!byte $00,$01,$02,$03,$04,$05,$06,$07
	!byte $08,$09,$0A,$0B,$0C,$0D,$0E,$0F

spr1	!byte %00001000,%00000000,%00000011,%00001000


	!align $FFFA, $FFFA, $00
	!word vblank		;
	!word reset		;
	!byte $00,$00		;

;==========================Labels=======================================

scrstat=$00

Also CHR:

Code: Select all

	*=$0000
	!CPU 6502
	!to "gamechr.bin", plain
	;
	!byte %00000010	;2222223.
	!byte %00000010	;2222223.
	!byte %00000010	;2222223.
	!byte %00000010	;2222223.
	!byte %00000010	;2222223.
	!byte %00000010	;2222223.
	!byte %11111110	;1111113.
	!byte %00000000	;........
	;
	!byte %11111110	;
	!byte %11111110	;
	!byte %11111110	;
	!byte %11111110	;
	!byte %11111110	;
	!byte %11111110	;
	!byte %00000010	;
	!byte %00000000	;
	;
	;
	!align $1000, $1000, $00
	;
	;
	!byte %00000000	;........
	!byte %00011000	;...33...
	!byte %00111100	;..3331..
	!byte %01110010	;.333221.
	!byte %01100010	;.332221.
	!byte %00000100	;..2221..
	!byte %00011000	;...11...
	!byte %00000000	;........
	;
	!byte %00000000	;
	!byte %00011000	;
	!byte %00111000	;
	!byte %01111100	;
	!byte %01111100	;
	!byte %00111000	;
	!byte %00000000	;
	!byte %00000000	;
	;
	;
	!align $2000,$2000, $00
	!eof
Using mapper 0. Can anyone tell me why I can't get the sprite to move?

Code: Select all

          *=$0000
loop      JMP loop
          .eof
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

I think the problem is with the way you handle $2003 and $2004. First, you shouldn't use them. I don't know the details, but they have been proven unreliable before. Just use a page of RAM as a mirror of the OAM memory, and update OAM with sprite DMA's (read about $4014).

Anyway, I haven't used $2003/$2004 in ages, but if it behaves anything like $2006/$2007, after each access to $2004 the address will auto increment, meaning that you are reading from a location, modifying the value, and storing it in a different location, because the pointer has moved.

Anyway, you shouldn't read-modify-write from these locations that are only accessible through registers, because some locations have a pointer that auto-increments, and some aren't readable at all ($2000 and $2001, for example). You should keep a copy of that data in regular CPU-addressable RAM, modify it there, and then upload using the registers.

In the case of sprites, they even made it easy for us by providing the DMA, that copies a full 256-byte page to OAM, in just 513 CPU cycles.

EDIT: I did some reading and it seems that reads do not increment the address. I don't know exactly what's wrong then, since I'm too lazy to look at the whole code! I think you should still have things such as coordinates in separate variables, though. And I think you should use sprite DMA's.

EDIT: I see some problems with the vectors. The IRQ vector points to RAM. You should point to a place with a RTI instruction (like "usuck"), even if you don't plan on using IRQ's, just to be safe. Also, when do you plan on executing the code before the "reset" label? Program execution will start at the location pointed by "reset", and I don't see how the code before it will ever be executed. I don't know if that could be the problem.
atari2600a
Posts: 324
Joined: Fri Jun 29, 2007 10:25 pm
Location: Earth, Milkyway Galaxy, The Universe, M-Theory
Contact:

Post by atari2600a »

The useless memory clearing routine I took from NES101? (I write to memory before I read it anyways so it's pretty useless right now)

I was unaware the NES starts at the reset vector. Then do games like Kirby's Adventure (skips the intro on reset) set a variable in memory to determine if it's a hard or soft reset?

Anyways, the whole fact that $2000/$2001 are unreadable clears things up for me a little! That probably explains why the screen-black-er-ing(?) subroutine I wrote didn't work!

I'll be sure to look into Sprite DMA. Luckily the wiki isn't blocked from my school's proxy server. (Oh & I'm too lazy to set up one of my own to bypass it)

Code: Select all

          *=$0000
loop      JMP loop
          .eof
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

atari2600a wrote:The useless memory clearing routine I took from NES101? (I write to memory before I read it anyways so it's pretty useless right now)
Yeah, I do that too. I kinda find that clearing the memory is a bad programming practice (no offense to anyone out there that does it, this is just a personal opinion) because it can induce errors... I don't think anyone should ever assume values, and everything should be initialized before use.
I was unaware the NES starts at the reset vector.
I think that every 6502-based machine does.
Then do games like Kirby's Adventure (skips the intro on reset) set a variable in memory to determine if it's a hard or soft reset?
I haven't looked, but probably yes. =)
Anyways, the whole fact that $2000/$2001 are unreadable clears things up for me a little!
I think the wiki indicates what can be done to each register (inside the parenthesis).
That probably explains why the screen-black-er-ing(?) subroutine I wrote didn't work!
The screen-black-a-what? O_o
I'll be sure to look into Sprite DMA.
It's simpler than using $2003/$2004 if you ask me. The only downside is that you have to waste a full page of RAM (256 bytes) even if you'll only use a few sprites. Just pretend that this page is the OAM memory and you have direct access to do whatever you want with it. Then, during VBlank, write the number of the page you used to $4014, and the whole page will be copied to OAM. Notice that you only write one byte $4014, and it works like this: if you write $02, all bytes from $0200 to $02FF will be copied.
atari2600a
Posts: 324
Joined: Fri Jun 29, 2007 10:25 pm
Location: Earth, Milkyway Galaxy, The Universe, M-Theory
Contact:

Post by atari2600a »

Can I save some cycles by using the zero page? For copying an entire page it seems like a good sacrifice...

Oh, & the screen-black-er-ing subroutine is what I call my subroutine that disables the background & sprites to check if someone's cheating.

BTW, is there anyone here that has experience w/ lightgun games? Should I wait an extra frame for the interlaced video from the previous frame to clear before I check for nohit after blanking the screen?

Code: Select all

          *=$0000
loop      JMP loop
          .eof
User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Post by kyuusaku »

It's DMA, it must always use absolute addresses but it's still twice as fast as zero-page instructions.

The NES display is progressive so there's only ever one field (NES frame) on the screen at a time. If it were interlaced I still don't understand what you mean. If you waited another frame you'd be in the same position.
atari2600a
Posts: 324
Joined: Fri Jun 29, 2007 10:25 pm
Location: Earth, Milkyway Galaxy, The Universe, M-Theory
Contact:

Post by atari2600a »

On an interlaced CRT, doesn't the beam for the next field hit directly under the previous, thus causing the scrolling affect you see when you look at a CRT from the side by moving your eyes upward or downward?

Since the NES is essentially 320x240 (clipped on borders) X 2, it's obvious how it can display 60fps progressive, but wouldn't the previous field still show under interlaced video?

Code: Select all

          *=$0000
loop      JMP loop
          .eof
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

atari2600a wrote:Can I save some cycles by using the zero page? For copying an entire page it seems like a good sacrifice...
I don't know if the DMA completes faster if you use zero page (I would guess it doesn't), but I don't think this is a good idea anyway, because since the whole page must be used as mirror of the OAM, you will not be able to have anything but sprites in zero page.

One of the biggest uses of zero page is indirect indexed addressing (LDA (ZP), Y), where the address to access must be stored in zero page. Well, you actually can use zero page for other things (the space not used for sprites), as long as you keep the Y coordinate of every sprite below the bottom of the screen (you should do it anyway), in which case you can use the other 3 bytes of each unused sprite. The data will still be copied to OAM, but since the sprites will be off-screen there is no problem. This is a little awkward, though.

513 cycles is not that much, and there will be enough time in Vblank to do many other things besides the DMA. There are about 2273 CPU cycles in VBlank (NTSC).
but wouldn't the previous field still show under interlaced video?
I'm, no expert when it comes to TV's and such, but AFAIK the NES tricks the TV into showing progressive frames. But I think you get interlaved video if you record that with a VCR or other types of digital capture. someone who know what they are talking should say something about this.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

In NTSC interlaced video, there are "odd fields" and there are "even fields", depending on where the synchronization pulses fall at the start of the vertical blanking period prior to the frame. One of the field types is drawn half a scanline down, and a signal that alternates odd and even fields produces a 480i display. But if a video signal is all odd fields or all even fields, most TVs will interpret it as a 240p signal.
atari2600a
Posts: 324
Joined: Fri Jun 29, 2007 10:25 pm
Location: Earth, Milkyway Galaxy, The Universe, M-Theory
Contact:

Post by atari2600a »

Ah, so it IS overwritten? Okay, then one field it is.

Code: Select all

          *=$0000
loop      JMP loop
          .eof
User avatar
Laserbeak43
Posts: 188
Joined: Fri Sep 21, 2007 4:31 pm
Contact:

Post by Laserbeak43 »

commodorejohn wrote:Well, as long as you're not selling it, it would fall under fair use and you shouldn't get in trouble even if the BBC did care.
wtf does BBC own europe???
(sorry for the OT)
atari2600a
Posts: 324
Joined: Fri Jun 29, 2007 10:25 pm
Location: Earth, Milkyway Galaxy, The Universe, M-Theory
Contact:

Post by atari2600a »

Umm.....noes?

Code: Select all

          *=$0000
loop      JMP loop
          .eof
Post Reply