Couple of questions from a 6502 newb.

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

tepples
Posts: 21749
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Couple of questions from a 6502 newb.

Post by tepples » Wed Dec 30, 2015 1:50 pm

Friendly_Noob wrote:GetInput2 is a loop checking $4016 8 times, because there are 8 buttons. Then depending on if the button is pressed or not 00000000 or 00000001 is ANDed with 00000011 ($03) so it's 00000000 or 00000001 again?
When you read controller port $4016 or $4017, bit 0 represents whether a button is pressed on the plug-in controller (NES) or hardwired controller (Famicom), bit 1 represents whether the corresponding button is pressed on the expansion controller (Famicom), and bits 7-2 are unrelated to button presses. A 4-player game for Famicom will treat bit 0 and bit 1 separately by shifting each into carry (lsr a) and then shifting it out into that player's variable:

Code: Select all

lda $4016
lsr a
rol player1_buttons
lsr a
rol player2_buttons
But for a 1- or 2-player game, you usually want to have each button on the Famicom expansion controller do the same thing as the corresponding button on the hardwired controller.
Also, why should I store oldbutton?
To detect the difference between pressing a button and holding a button:

Code: Select all

lda oldbutton  ; A = buttons pressed last frame
eor #$FF       ; A = buttons NOT pressed last frame
and button     ; A = buttons pressed this frame but not last frame
sta newbutton
Here's another tip: If you prefill button with $01, you don't need to use Y as the loop counter. Carry after rotating a bit into place will be clear until the 1 bit gets rotated out, and you can use this change in carry to detect the end of the loop. The code used in my games is similar to the last snippet on Gamepad code, reproduced here:

Code: Select all

JOYPAD1 = $4016
JOYPAD2 = $4017

.zeropage
buttons1: .res 1
buttons2: .res 1

.code
readjoy:
  lda #$01
  sta JOYPAD1
  sta buttons2  ; player 2's buttons double as a ring counter
  lsr a         ; now A is 0
  sta JOYPAD1
@loop:
  lda JOYPAD1
  and #$03      ; ignore bits other than controller
  cmp #$01      ; Set carry if and only if nonzero
  rol buttons1  ; Carry -> bit0; bit 7 -> Carry
  lda JOYPAD2   ; Repeat 
  and #$03
  cmp #$01
  rol buttons2  ; Carry -> bit0; bit 7 -> Carry
  bcc @loop     ; Loop is done when the last bit of $01 is shifted out
  rts

User avatar
tokumaru
Posts: 11465
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Couple of questions from a 6502 newb.

Post by tokumaru » Wed Dec 30, 2015 2:24 pm

Friendly_Noob wrote:Also, why should I store oldbutton?
Like memblers and tepples said, so that you can compare the new buttons against the old ones and tell which ones have been pressed or released since last time. Some actions, such as jumping, shooting, or moving a cursor, are normally only triggered when the button is pressed, not when it's held down. Moving left and right, on the other hand, normally happens when the respective buttons are held down. Since there are these two types of actions, its customary for games to keep one variable containing the bits directly read from the controller, and another containing only the new buttons.

Friendly_Noob
Posts: 20
Joined: Mon Dec 14, 2015 2:58 am

Re: Couple of questions from a 6502 newb.

Post by Friendly_Noob » Fri Nov 25, 2016 1:25 am

Here's my question after almost a year of absence. In this loop I don't want to mess with any of the sprite's attributes like position on the screen, it's just to
store tile numbers in the right places to form a 16x16 metasprite. How do I make a loop that stores a value every 4 memory locations? I did something like this, but 4xINY doesn't seem to be a smart solution.

Code: Select all

sprite1: .db $00,$00,$10,$11

Code: Select all

LoadFrame:
  LDX #$00
  LDY #$00
LoadFrameLoop:
  LDA sprite1, x
  STA $201, y
  INX
  INY
  INY
  INY
  INY
  CPX #$04
  BNE LoadFrameLoop

User avatar
rainwarrior
Posts: 7669
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Couple of questions from a 6502 newb.

Post by rainwarrior » Fri Nov 25, 2016 2:01 am

A small burst of INX or INY instructions is a common thing to do on the 6502. 3 or 4 in a row is usually better than something like TYA / CLC / ADC #4 / TAY.

If speed is more important than size you can also unroll the loop:

Code: Select all

; original:
;   15 bytes
;   (3 * 24) + 23 = 95 cycles
:
	lda sprite1, x
	sta $201, y
	inx
	iny
	iny
	iny
	iny 
	cpx #4
	bne :-

; unrolled loop:
;   6 * 4 = 24 bytes
;   4 * 9 = 36 cycles
.repeat 4, I
	lda sprite1+I
	sta $201+(i*4), y
.endrepeat

User avatar
koitsu
Posts: 4215
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Couple of questions from a 6502 newb.

Post by koitsu » Fri Nov 25, 2016 2:09 am

I wouldn't bother with a loop in this case, assuming you have the extra space for the code (it's not much), and the cycle savings are worth it (factor of about 3, I'd think). All the addresses are calculable at assemble-time, so I'd just do this:

Code: Select all

sprite1: .db $00,$00,$10,$11

LoadFrame:
  lda sprite1
  sta $0201
  lda sprite1+1
  sta $0201+4
  lda sprite1+2
  sta $0201+8
  lda sprite1+3
  sta $0201+12
If you need this code to be a bit more dynamic, such as handling multiple entries (e.g. sprite2, sprite3, etc.) then the situation becomes a bit more complicated but not too bad. Use a combination of a 16-bit pointer in ZP and indirect indexed addressing:

Code: Select all

spriteptr = $00               ; Change this to whatever ZP location is available: your problem not mine! :P

sprite1: .db $00,$00,$10,$11
sprite2: .db $20,$20,$30,$31  ; I just made up this data
sprite3: .db $40,$40,$50,$51  ; I just made up this data

  lda #.LOBYTE(sprite2)
  sta spriteptr
  lda #.HIBYTE(sprite2)
  sta spriteptr+1
  jsr LoadFrame
  ...
  ...

LoadFrame:
  ldy #0
  lda (spriteptr),y
  sta $0201
  iny
  lda (spriteptr),y
  sta $0201+4
  iny
  lda (spriteptr),y
  sta $0201+8
  iny
  lda (spriteptr),y
  sta $0201+12
  rts
You get the idea, I hope.

Edit: rainwarrior beat me to it using a .repeat loop that unrolls the existing loop + keeps the use of Y indexing. Also, sorry for all the edits -- it's post-US-Thanksgiving and I'm in a kind of "OMG I ate too much, gonna burst" state where my brain is all over the place (what else is new).

Friendly_Noob
Posts: 20
Joined: Mon Dec 14, 2015 2:58 am

Re: Couple of questions from a 6502 newb.

Post by Friendly_Noob » Sat Nov 26, 2016 4:18 am

Does .repeat work on NESASM3? Maybe it's time to move to ca65.

User avatar
koitsu
Posts: 4215
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Couple of questions from a 6502 newb.

Post by koitsu » Sat Nov 26, 2016 5:05 am

Friendly_Noob wrote:Does .repeat work on NESASM3? Maybe it's time to move to ca65.
nesasm 3.x assembler directives are fully documented (any user of an assembler should refer to and read their assembler documentation as needed ;-) ). The short answer is no it doesn't, but it does support macros, so you could "repeat generation of code" very easily with a macro + repeated use of the macro.

I really don't think something like .repeat is reason to change assemblers, but that's my own opinion. And if you *had* to switch assemblers for simple projects, asm6 might be a better choice (the directive there is REPT/ENDR; here's a random copy of the documentation), as there isn't as much setup/complication involved compared to ca65/ld65.

bogax
Posts: 34
Joined: Wed Jul 30, 2008 12:03 am

Re: Couple of questions from a 6502 newb.

Post by bogax » Sat Nov 26, 2016 10:39 am

or something in between
use an LUT

Code: Select all

 ldx #03
loop
 ldy ytable,x
 lda sprite1,x
 sta $201,y
 dex
 bpl loop

ytable
 $00
 $04
 $08
 $0C 

User avatar
rainwarrior
Posts: 7669
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Couple of questions from a 6502 newb.

Post by rainwarrior » Sat Nov 26, 2016 12:27 pm

koitsu wrote:
Friendly_Noob wrote:Does .repeat work on NESASM3? Maybe it's time to move to ca65.
I really don't think something like .repeat is reason to change assemblers, but that's my own opinion.
I think NESASM silently failing to generate valid code when you overflow a bank is a good reason to change assemblers. :P The repeat directive is just one of a hundred advantages.

tomaitheous
Posts: 592
Joined: Thu Aug 28, 2008 1:17 am
Contact:

Re: Couple of questions from a 6502 newb.

Post by tomaitheous » Sat Nov 26, 2016 6:54 pm

rainwarrior wrote:
koitsu wrote:
Friendly_Noob wrote:Does .repeat work on NESASM3? Maybe it's time to move to ca65.
I really don't think something like .repeat is reason to change assemblers, but that's my own opinion.
I think NESASM silently failing to generate valid code when you overflow a bank is a good reason to change assemblers. :P The repeat directive is just one of a hundred advantages.
Failing to generate code or failing to provide a warning? Was this not fixed with updated versions of nesasm? If not, why not? The source is pretty easy to modify (I added some stuff at one point).
__________________________
http://pcedev.wordpress.com

tepples
Posts: 21749
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Couple of questions from a 6502 newb.

Post by tepples » Sat Nov 26, 2016 8:47 pm

"Silently" implied lack of a warning to me. Which forks fix this?

Friendly_Noob
Posts: 20
Joined: Mon Dec 14, 2015 2:58 am

Re: Couple of questions from a 6502 newb.

Post by Friendly_Noob » Sun Dec 04, 2016 12:39 pm

That's my loop after moving to ASM6. For some reason all the sprites loaded are tiles $00, or $00, $00, $00, $11 when I put INY in a different place (after STA $201 + i for example)

Code: Select all

LoadYacaFrontFrame1:
  LDA  #%01000000
  STA  $206
  LDA  #%00000000
  STA  $202
  STA  $20A
  STA  $20E
  LDY  #$00
  i=0
  REPT 4
  LDA yacafront1, y
  STA $201 + i
  DB i
  i=i+4
  INY
  ENDR
  RTS

yacafront1:
  .db $00,$00,$10,$11

User avatar
tokumaru
Posts: 11465
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Couple of questions from a 6502 newb.

Post by tokumaru » Sun Dec 04, 2016 1:12 pm

Why do you have a DB in the middle of your code? The CPU will try to execute those bytes as if they were instructions, and things will very likely derail from there.

Friendly_Noob
Posts: 20
Joined: Mon Dec 14, 2015 2:58 am

Re: Couple of questions from a 6502 newb.

Post by Friendly_Noob » Sun Dec 04, 2016 1:24 pm

It's not in the actual code, I just wanted you to know how yacafront1 looks like.

User avatar
tokumaru
Posts: 11465
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Couple of questions from a 6502 newb.

Post by tokumaru » Sun Dec 04, 2016 6:03 pm

Not that, I'm taking about the DB i inside the REPT block. You're inserting bytes $00, $04, $08 and $0c in the middle of the program, that the CPU will try to execute.

Post Reply