It is currently Sat Mar 25, 2017 12:45 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 26 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sun Feb 28, 2016 7:21 am 
Offline
Formerly AlienX
User avatar

Joined: Fri Apr 18, 2014 7:41 am
Posts: 133
Location: Bulgaria
So, I decided to go back to NES programming, hoping I would make an actual game at one point. I'm experimenting, trying to figure things out and early on, I hit a wall. I want the game logic (code outside of NMI or any other IRQ) to check for button presses, save the status of the buttons as flags, then use said flags to see if the sprites should move or not. Once this is done, a loop flag is set to notify that the button checks are done and the game goes into a loop until the NMI. There all the palettes and sprites are copied from RAM, as usual and finally, the loop flag is set to 0, so that when the programs returns from interrupt, it would check for button presses again. The problem is, it never does that. The sprites are supposed to move, when Left is pressed, but they remain still. I'm not quite sure whether the problem is in the code for the loop or in the code, that would check for buttons. So, can someone take a look at this and tell me what's wrong?
Code:
NOTE: This is made in a separate .asm file from the main one, which is why you don't see the NMI label or any other code.

; Movement

;$01FA - 1st Controller flags
;$01FB - Loop Flag
;$01FC - Temporary values
;$01FD - Temporary X
;$01FE - Temporary Y

ButtonCheck:
LDA #$01
STA $4016
LDA #$00
STA $4016
; Check if buttons are pressed
LDX #$00
LDY #%00000001
STY $01FE
ButtonCheckLoop:
   LDA $4016   ;read controller information
   AND #%00000001 ;erase all other bits
   BEQ NextButton ;if no button is pressed, go to the next check
   LDA $01FA ;load the value in $001A
   CLC ;clear carry flag
   ADC $01FE ;add the value in Y
   STA $01FA ;store the value in $001A
  NextButton:
   LDA $01FE;load the value in Y register
   ASL A ;shift the bytes for next flag
   STA $01FC ;store value in Temporary Values
   LDY $01FC ;store value in Y
   STY $01FE
   INX
   CPX #$08 ;check if all buttons are read
   BNE ButtonCheckLoop
;;;;;;;;;;;;;;;;

; Moving sprites

Moving:
 LDA $001A
 AND #%01000000 ;Check if Left is pressed
 BEQ MoveDone
 ; Moving Left
 LDX #$00
 STA $01FD
 SpritesLeft:
   LDA $0203, X ;load the X position
   SEC
   SBC #$01 ;move 1 pixel to the left
   STA $0203, X ;save X position
   LDA $01FD ;load the value in X register
   CLC
   ADC #$03 ;add 3
   STA $01FC
   LDX $01FC ;save X value
   STX $01FD
   CPX #$09 ;check if all sprites are moved
   BNE SpritesLeft
;;;;;;;;;;;;;;;;;;

MoveDone:
 LDA #$01
 STA $01FB

; Main Loop   
MainLoop:
 LDA $01FB
 CMP #$00
 BEQ ButtonCheck
 JMP MainLoop

This is what happens during NMI:
Code:
NMI:
 SetPaletteAdress:
  LDA $2002 ; read PPU status to reset the high/low latch to high
  LDA #$3F
  STA $2006
  LDA #$00
  STA $2006
  LDX #$00
 LoadPalettes:
   LDA palette, X
   STA $2007
   INX
   CPX #$20
   BNE LoadPalettes
   
 SpiteDMA:
   LDA #$00
   STA $2003
   LDA #$02
   STA $4014
   
LDA #$00
STA $01FB

RTI

I wouldn't be surprised, if this is all due to my poor programming skills. :D Anyway, I hope someone can tell me why this doesn't work the way it should.

Thanks!

_________________
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.
You can also follow me on Twitter.


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 7:27 am 
Offline

Joined: Thu Jul 23, 2015 7:54 pm
Posts: 127
The first thing that immediately sticks out to me is that you aren't backing up your registers in your NMI, so the accumulator is getting clobbered when you return back to your main code.

This might not be the problem if your code is so short, but see if this helps:
Code:
NMI:
        pha
        txa
        pha
        tya
        pha

        ;nmi code

        pla
        tay
        pla
        tax
        pla
        rti


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 8:40 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9412
Location: Rio de Janeiro - Brazil
My first advice is: name your variables! It's really hard to follow the logic if we have to constantly look up what each address is supposed to be.

There appears to be something weird going on in your controller reading routine, where you read 1 button to decide whether to do an addition and then proceed to buffer the other buttons... It's like you mixed two different ways of reading the controller. But I can't easily see what you were trying to do without meaningful variable names.

Also, the specific addresses you chose for the variables are very suspicious... If you're initializing the stack pointer to $ff, as is the usual, your variables and the stack will overlap, must likely resulting in crashes. These types of variables would normally be in page 0. Since page 1 is normally dedicated to the stack, you must be careful when declaring variables there. Normally you shouldn't do it, unless all other pages are full.


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 9:23 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1415
Location: DIGDUG
I have a few thoughts...

NMI code is something that you want to happen every frame, especially things that nees to happen during V-blank.

If you are going to store palette colors to the PPU, it should happen during V-blank (or with rendering off)...but you are repeatedly storing the palette every NMI. Not necessary unless you have a color palette that changes (like SMB1 coins).

The controller read code should be a simple routine that you don't mess around with. Either it should store all controller reads in 1 variable or into an array of 8 variables.

Logic code should be separate from controller read code.
(I guess you arent doing logic, but some...kind of button checking, which I still feel like should be separate).

Try to find some source code of other homebrew projects, and see how they do things. I can't think of any except the Nerdy Nights tutorials (ASM). I learned a lot from reading the SMB disassembly, but I wouldn't recommend that...it's long and complicated.

This statement is plain wrong...
Code:
STA $01FA ;store the value in $001A

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 9:46 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9412
Location: Rio de Janeiro - Brazil
Yeah, there are weird comments all around, but I ignored those, assuming they were outdated or something. In general, comments like "save X" or "load the value at $37" are pretty useless anyway, because the instructions themselves will tell you that much. Comments are supposed to state things that are not obvious, and generally describe larger operations.


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 9:48 am 
Offline
Formerly AlienX
User avatar

Joined: Fri Apr 18, 2014 7:41 am
Posts: 133
Location: Bulgaria
Well, I do lack knowledge in certain areas, which might be why there are problems in the code.
First of all: How do you exactly define variables? Is this something, that differs from assembler to assembler (I'm using ASM6, if it does)? How do you assign a name to a certain address?

Second, where exactly does the stack store its information? Is defining a stack pointer necessary? Why do people start Shadow OAM at $0200? Where does Zero page end and what exactly is in there?

About the reading routine: Now that I think about it, I'm not sure why I tried doing this, but here's the idea:

I've reserved a byte in RAM, where every bit is a flag for each button.
The program starts by reading the A button information.
The necessary AND is done to isolate bit 0, so that the program could properly check whether the button is pressed or not.
If the A is pressed, then bit 0 of the reserved byte is set to one with an ADC #%00000001, which is stored in the Y register.
An ASL is done to the value in the Y register, so that the 1 is pushed left and if the B button is pressed, this value would be added to the reserved byte, setting bit 1 to one.

I don't know if there's a better way to do this, at least when it comes to setting an individual bit in a byte, without changing the other ones.

dougeff wrote:
If you are going to store palette colors to the PPU, it should happen during V-blank (or with rendering off)...but you are repeatedly storing the palette every NMI.

Isn't the NMI code occurring only during every V-Blank? Also, I don't think it's a bad idea to update the palette every frame, since that way the game can make sudden updates without too much additional code.

dougeff wrote:
The controller read code should be a simple routine that you don't mess around with. Either it should store all controller reads in 1 variable or into an array of 8 variables.

Well, that was kind of what I was trying to do, but it went wrong somewhere.

dougeff wrote:
Logic code should be separate from controller read code.

So, does that mean the controller read code should be done during NMI? I know I sound like an idiot, but by logic code I usually refer to whatever's going on outside of NMI. Also, my attempt here was kind of to try and separate reading the controller and calculating the different stuff that's going on. It's just that the only thing I have right now is moving the four sprites to the left.

dougeff wrote:
Try to find some source code of other homebrew projects, and see how they do things. I can't think of any except the Nerdy Nights tutorials (ASM).

The code in the Nerdy Nights tutorials isn't optimized enough, in my opinion. And, to be honest, I don't know if other people's projects would help me because everyone has such different styles, that half the time I can't understand what's going on.

dougeff wrote:
This statement is plain wrong...

Whoops! Forgot to edit the comment there.

_________________
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.
You can also follow me on Twitter.


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 10:53 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1415
Location: DIGDUG
I'm away from my computer, but my memory of asm6...

Code:
enum $0000
variable_name   .dsb 1
variable_name2   .dsb 1
etc...
ende


And the assembler will assign addresses for those variables... you don't need to worry about their value while coding, just use the variable name

Also...(again, from memory)

Code:
FOO = $30


And anytime you want to access address $30, you can

LDA FOO

And, for the NUMBER $30

LDA #FOO

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 10:56 am 
Offline

Joined: Thu Jul 23, 2015 7:54 pm
Posts: 127
BioMechanical Dude wrote:
Second, where exactly does the stack store its information? Is defining a stack pointer necessary?
The stack is at $0100-$01FF. You can technically write to it normally using sta $0100, and this is actually done often because there's hardly a chance you're ever going to be deep in 256 subroutines, but the stack pointer should be defined (Set to #$FF) at reset, because stacks are LIFO.

Quote:
Why do people start Shadow OAM at $0200?
It doesn't necessarily have to start at $0200, it can start at any page really (Minus the stack and zero-page). People probably just use $0200 because its the next page after your zero-page and stack

Quote:
Where does Zero page end and what exactly is in there
The zero-page is $0000-$00FF, the first 256 bytes of RAM. Operations on the zero-page are faster than operations to other parts of memory because with zero-page the high byte is always 0, and doesn't need to be dealt with.

For example, the instruction lda $0456 gets translated to AD 56 04, 3 bytes, whereas lda $40 gets translated to A5 40, only two bytes.

Quote:
About the reading routine: Now that I think about it, I'm not sure why I tried doing this, but here's the idea:

I've reserved a byte in RAM, where every bit is a flag for each button.
The program starts by reading the A button information.
The necessary AND is done to isolate bit 0, so that the program could properly check whether the button is pressed or not.
If the A is pressed, then bit 0 of the reserved byte is set to one with an ADC #%00000001, which is stored in the Y register.
An ASL is done to the value in the Y register, so that the 1 is pushed left and if the B button is pressed, this value would be added to the reserved byte, setting bit 1 to one.

I don't know if there's a better way to do this, at least when it comes to setting an individual bit in a byte, without changing the other ones.
The way controller reading is generally done goes about like this:
Code:
ReadControllers:
  lda #$01
  sta $4016
  lda #$00
  sta $4016  ;read from controller 1, but not controller 2

  ldx #$08
- lda $4016
  lsr
  rol buttons
  dex
  bne -
  rts

That reads the controller 8 times, and makes each button a bit in the variable buttons. So bit 7 would be A, bit 6 would be B, bit 5 Select, bit 4 Start, and then the d-pad buttons. This way you can read any button from the controller anywhere in the frame.

Quote:
So, does that mean the controller read code should be done during NMI? I know I sound like an idiot, but by logic code I usually refer to whatever's going on outside of NMI. Also, my attempt here was kind of to try and separate reading the controller and calculating the different stuff that's going on. It's just that the only thing I have right now is moving the four sprites to the left.
The controller reading routine (That I mentioned above) only needs to be called from the main loop once per frame, and should be a subroutine. The only things that should go in NMI are drawing code and a music engine, which should come after the drawing code is executed. Drawing can only happen in vblank which is why the NMI shouldn't be bogged down with other code; that's what goes in the main loop. The reason why people generally put their music engine in NMI is so that it'll play at a constant tempo, but this should happen after the drawing code so that you can draw as much as possible in the short vblank window.


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 11:04 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1415
Location: DIGDUG
Stack = RAM $100-1ff. It grows downward, which is why most people set the stack pointer to $ff (= the bottom of the stack...$1ff).

Quote:
Why do people start Shadow OAM at $0200

It needs to start at a x00 address. $200 is as good a place as any. $300 would work. $400. Etc.
Quote:
zero page?


Zero page = RAM addresses 0-$ff. A 'page' is a chunk of 256 bytes. The 1 page would be addresses $100-1ff.

Quote:
Isn't the NMI code occurring only during every V-Blank?

NMI triggers at the start of V-Blank. Yes. My issue was the repeated storing of the palette every frame.

And, controller code can happen anytime. Once a frame, usually.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 11:36 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1415
Location: DIGDUG
If you want to store buttons into 8 separate addresses...

first reserve 8 addresses with an enum...

enum $0000
BUTTONS .dsb 8
ende

Then your code can do this...
Code:
Ldx #1
Stx $4016
Dex ;X=0
STX $4016

LOOP:
  LDA $4016
  AND #1
  STA BUTTONS, X
  INX
  CPX #8
  BNE LOOP


Another option...
Code:
Ldx #1
Stx $4016
Dex ;X=0
STX $4016

LOOP:
  LDA $4016
  LSR A
  ROL BUTTONS, X
  INX
  CPX #8
  BNE LOOP


Will allow you to detect HOW LONG they've been pressing a certain button, by the value of that button's address...
Value = #1 = 1 frame
Value = #3 = 2 frames
Etc.

Silver surfer does something like this.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Last edited by dougeff on Sun Feb 28, 2016 11:40 am, edited 1 time in total.

Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 11:37 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 17974
Location: NE Indiana, USA (NTSC)
BioMechanical Dude wrote:
How do you exactly define variables? Is this something, that differs from assembler to assembler (I'm using ASM6, if it does)?

This varies. Some use .DS, others .DSB, others .RES, others different keywords. If I give answers, they'll usually be for ca65 (which uses .RES), though in many ways, ASM6 is closer to ca65 than NESASM is to ca65.

Quote:
How do you assign a name to a certain address?

Usually name = address, such as PPUADDR = $2006.

Quote:
Second, where exactly does the stack store its information?

At $0100,S, except wrapping around within $0100-$01FF instead of continuing to $0200 as indexed addressing normally does.

Quote:
Is defining a stack pointer necessary?

Setting the stack pointer is necessary if your stack and something else share page $0100-$01FF. It is my common practice to use $0100-$01BF for a VRAM transfer buffer and $01C0-$01FF for the stack. It is also necessary if you want to index into the stack using TSX and then indexing into the stack using e.g. $0103,X, because of the aforementioned difference in wraparound behavior between stack access and access through X or Y.

Quote:
Why do people start Shadow OAM at $0200?

Convention. It's not universal to put the display list at $0200; Super Mario Bros. puts it at $0700, and I think Rampart puts it at $0300, instead putting a circular buffer of VRAM updates at $0200. Some games containing extra work RAM at $6000-$7FFF (found on SLROM, SNROM, TLROM, TSROM, and other less common boards) place the display list somewhere there, which is equally valid.

Quote:
Where does Zero page end

A "page" on 6502 means 256 bytes. On 6502, zero page always starts at $0000 and always ends at $00FF. On 65816, zero page is renamed direct page and can set to start at any address from $000000 through $00FF00, though failing to align it to the start of a page (that is, having a direct page start address whose least significant byte is not $00) costs one cycle per instruction that accesses the direct page, and putting it in ROM isn't nearly as useful.

Quote:
and what exactly is in there?

Mostly addresses intended for quick access, plus pointers for indirect addressing into large arrays or arrays with variable starting points using the (dd),Y addressing mode.

Quote:
I don't know if there's a better way to do this, at least when it comes to setting an individual bit in a byte, without changing the other ones.

Usually it's done with ROL, as in "Gamepad code" on the wiki.

Quote:
Also, I don't think it's a bad idea to update the palette every frame, since that way the game can make sudden updates without too much additional code.

The more you update every frame, the less time you have for occasional large updates. However, large updates should be less common with CHR ROM.

Quote:
dougeff wrote:
Logic code should be separate from controller read code.

So, does that mean the controller read code should be done during NMI?

In the majority of cases, controller reading should be done from the same thread as game logic. This means you do it in the main thread unless your game uses a Super Mario Bros.-style loop with everything in NMI and a main thread of just forever: jmp forever. If you do it in the NMI, and your game logic runs in the main thread, and your game logic slows down due to exceeding the 27,393 cycles of active picture and post-render, then the controller bits would change behind the back of the program. This is especially disastrous if you're calculating transition bitmasks (which buttons were just pressed and which buttons were just released) during NMI, as they could cause your main thread to miss presses or releases.

The biggest exception is if you're using the DMC IRQ as a makeshift scanline counter, for which reading the controller during the IRQ handler is useful to avoid bit deletions.

Quote:
I know I sound like an idiot, but by logic code I usually refer to whatever's going on outside of NMI.

For which pages like "NMI thread" on the wiki more often use the term "main thread".


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 11:46 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 4901
Location: Canada
We have example controller read code on the wiki, BTW: http://wiki.nesdev.com/w/index.php/Controller_Reading

One big omission here is that if you want to support the Famicom, you should read both bits 0 and 1 from $4016, in case the controller is on the expansion port. Famicom had hard-wired controllers with short cables, so this is actually quite a common thing.

Here's one way that can go down:
Code:
    lda $4016
    and #3 ; keep bits 0 and 1
    cmp #1 ; set carry if bit 0 or 1 is set
    rol buttons ; shift carry into buttons


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 12:11 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9412
Location: Rio de Janeiro - Brazil
BioMechanical Dude wrote:
Is this something, that differs from assembler to assembler (I'm using ASM6, if it does)? How do you assign a name to a certain address?

All assemblers I'm aware of support the VariableName = $03ab syntax, but having to manually select addresses still isn't the most reliable thing to do. You could accidentally define overlapping variables, and you'd need to retype a bunch of addresses if you decided to rearrange them. For ASM6, dougeff's first suggestion is the best: create an .enum block where you specify the starting address and then declare all variables and their lengths inside it, so the assembler will automatically assign the correct address to each one.

Quote:
Second, where exactly does the stack store its information? Is defining a stack pointer necessary?

The stack is always at $0100-$01ff, and if you're going to use that space exclusively for the stack, you don't really need to initialize the stack pointer. It wraps around, so the stack will still be 256 bytes long no matter where it starts. However, it's good practice to always initialize the stack pointer... This will allow you to use other parts of that memory page for other things you might need, such as a VRAM update buffer. Also, knowing exactly where the stack starts makes debugging easier.

Quote:
Why do people start Shadow OAM at $0200?

Since page 0 and page 1 have are used by the 6502 for special purposes, NES programmers find it natural to use the page after that for a special purpose too. In practice, any other page, from $00 to $ff, can be copied to OAM. Yes, even ROM pages can be used as shadow OAM, but there's hardly any use for that besides title screens and the like (and even then it's usually more compact to decompress the sprite data to RAM instead of DMA'ing it directly from ROM).

Quote:
Where does Zero page end and what exactly is in there?

It's the very first page of memory, from $0000 to $00ff. It's special because several instructions have optimized versions to access data there using less ROM (the high byte of the address is omitted from the instructions, since it's known to be 0) and 1 cycle faster (since the CPU doesn't have to read the high byte of the address, which is known to be 0). There are also some operation that can only be done through ZP, such as accessing data through pointers (e.g. lda (Pointer), y).

Quote:
I don't know if there's a better way to do this, at least when it comes to setting an individual bit in a byte, without changing the other ones.

Honestly, I can't make any sense out of the controller reading loop since it's using addresses instead of labeled variables. From your description it sounds like it could work, but it looks a little convoluted with all the loading and storing that are going on. It can definitely be reworked to be much smaller and easier to read. The loop itself could probably be just this (this will ignore Famicom controllers that plug into the expansion port though... the code rainwarrior posted supports those):

Code:
   ldx #$08
ButtonLoop:
   lda $4016
   lsr
   rol Buttons
   dex
   bne ButtonLoop

With time you'll learn that a lot of things can be done by simply moving bits and flags (such as the carry) around, without the need for decisions and branching. Another thing you'll learn is that counting down is usually faster than counting up, because the CPU automatically changes its status flags for results like 0 ($00) and -1 ($ff), so you can decide whether to break out of the loop with an explicit CMP/CPX/CPY instruction. Whenever you can count down instead of up, do it.

In this case in particular you can actually not count at all, and put a flag in the button buffer to indicate when all buttons have been read:

Code:
   lda #$01
   sta Buttons
ButtonLoop:
   lda $4016
   lsr
   rol Buttons
   bcc ButtonLoop

Since we initialized the "Button" buffer with all zeroes, except for the least significant bit, we can be sure that only zeroes will be shifted out after each rotation. That "1" bit we put in the least significant position will only be shifted out after 8 bits have been shifted in, so when the carry is set we'll know it's time to end the loop. Combine that with rainwarrior's suggestion of taking the Famicom expansion controller into consideration and you get this:

Code:
   lda #$01
   sta Buttons
ButtonLoop:
   lda $4016
   and #$03
   cmp #$01
   rol Buttons
   bcc ButtonLoop

Which is fairly compact and very reliable.

dougeff wrote:
Also, I don't think it's a bad idea to update the palette every frame, since that way the game can make sudden updates without too much additional code.

Yes, some people like to do it that way, there's nothing terribly wrong about that. It's still a little bit of CPU time wasted nearly every frame, but if your game doesn't need to make a lot of VRAM updates and the gameplay itself isn't too hungry for CPU cycles, there shouldn't be any problems with updating the palette every frame.

dougeff wrote:
So, does that mean the controller read code should be done during NMI?

I wouldn't do that. If your game doesn't have lag frames, it shouldn't make any difference, but if by any chance the game logic is not done by the time the NMI is called, it will overwrite the controller state with a new one, and when the game logic resumes, whatever of it that still has to run will use the new state. This means that the same logic frame will end up using two different sets of controller states, which could cause inconsistencies.


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 12:40 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 9412
Location: Rio de Janeiro - Brazil
Sogona wrote:
there's hardly a chance you're ever going to be deep in 256 subroutines

That would actually be 128 subroutines, since each return address is 2 bytes. And there are also interrupts, which consume 3 stack bytes because in addition to the return address they also save the status flags. But you're right, there's absolutely no reason for a game to go this deep in subroutines, so you can safely use a good chunk of page 1 for things other than the stack IF you initialize the stack pointer properly.

Quote:
It doesn't necessarily have to start at $0200, it can start at any page really (Minus the stack and zero-page).

Well, you can technically use pages 0 and 1 when DMA'ing sprites just fine, but nobody would seriously consider that. If you want to use part of your OAM shadow page for variables you can do that too, you just need to set the Y coordinates of all unused sprites to a value larger than 239 to make sure they're off screen, and then you can use the remaining 3 bytes (attributes, tile index and X coordinate) for whatever you want, since these sprites will never be picked up for display. So if you really wanted to make page 0 your shadow OAM, you could, but you'd lose 4 bytes of it for any sprites you did use, and 25% of whatever is left. Page 1 would be more complicated to use, because you can't tell the 6502 to skip over the Y coordinate byte, so you'd end up not being able to use subroutines or something. :mrgreen:


Top
 Profile  
 
PostPosted: Sun Feb 28, 2016 1:03 pm 
Offline
Formerly AlienX
User avatar

Joined: Fri Apr 18, 2014 7:41 am
Posts: 133
Location: Bulgaria
Wow! Thanks for all of the information! It was really helpful!
I have one question left, and it's just make sure of something. I see that both the Shift and Rotate functions move the bits in the desired direction. But from what I can tell, the Rotate functions push either the leftmost or rightmost bit into the carry flag. (Is that right?) Does that mean that, if I have bit 7 set to one and I do a ROL, the carry flag will be equal to one and all of the bits will be set to zero? What happens if I do another ROL?
Also, given that I've seen the BEQ and BNQ functions being used without the CMP function, does this mean that BEQ compares the Carry flag to 0?

tokumaru wrote:
it looks a little convoluted with all the loading and storing that are going on.

Well, originally, the code was much simpler, but then I found out that there's no other way to transfer values between the Accumulator, X and Y registers (...or is there?), and that you can't do any mathematical functions on the X and Y registers.

_________________
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.
You can also follow me on Twitter.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 26 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group