It is currently Wed Nov 22, 2017 3:20 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 29 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Thu Sep 12, 2013 9:11 am 
Offline
User avatar

Joined: Fri Aug 30, 2013 10:18 pm
Posts: 13
Goal: I would like to set the NES background as a single repeating pattern.

Something like this:
Image

Problem: I can't get my pattern to display. I have the pattern I want loaded into the pattern table, I have written the colors I want to the background palette, all name tables are pointing to tile zero, all my attribute tables are zero; yet nothing is being displayed.

Logical program flow (what I think my program is doing):
  • disable interrupts / set stack pointer
  • set PPU control #1 bits to %00001000 so that:
    • D0-D1: name table address set to $2000
    • D2: PPU increment is 1
    • D3: sprite pattern address is $1000
    • D4: background pattern address is $0000
    • D5: sprite size is 8x8
    • D6: ?
    • D7: disable NMI on Vblank
  • set PPU control #2 bits to %00011110 so that:
    • D0: color display
    • D1: no background clipping
    • D2: no sprite clipping
    • D3: background is visible
    • D4: sprites are visible (no sprites in this example)
    • D5-D7: color intensity is set to none
  • clear Vblank flag
  • first wait for Vblank
  • second wait for Vblank
  • enable NMI on Vblank
  • Branch:
    • Main loop constantly sets $00 to whatever is in the Xreg
    • on NMI interrupt
      • write two bytes to the background palette (first is transparency, second is the color I want the pattern to be)
      • write one byte of $00 to name table #0 and attribute table #0 (really does nothing)

I have set up my CHR-ROM to point to the second color I loaded into the background palette. Here is my pattern table and palette according to FCEUX:
Image

Here is my name tables according to FCEUX:
Image

Yet my emulator is only displaying:
Image

Secondary/tangent questions:
  • Why is so much of the NES memory mirrored? From a newb point of view this seems wasteful.
  • From (http://wiki.nesdev.com/w/index.php/Init_code); disabling IRQs (opcode SEI) is good if one might later desire to manually call the RESET routine. This makes sense to me but I have noticed that the order in which NMI and IRQs are enabled (I'm referencing my "INIT:" label in "test.s" below) determines whether or not PPU memory is written to (i.e. calling "CLI" before setting the 7th bit of PPU control #1 to 1 causes pattern table, name table, and attribute table to be empty; it might actually be causing the whole NES to crash, I'm not sure.)
    • Here I'm thinking it might be best to not manually disable IRQs (call SEI) and let the system do it naturally on interrupt for the benefit of simplicity.

"test.s"
Code:
; TESTING
; cl65 -t none -C nes.cfg -o test.nes test.s

; Program ROM
.segment "PRG"

RESET:
   ; set up a few things and wait for PPU
   
   SEI            ; in case of manual reset call
   CLD            ; generic 6502 debugger friendly
   
   LDX #$FF       ; setting value for stack pointer
   TXS            ; setting stack pointer
   
   LDX #%00001000 ; PPU control bits
   STX $2000      ; set PPU control register #1
   
   LDX #%00011110 ; PPU control bits
   STX $2000      ; set PPU control register #2

   BIT $2002      ; clears the Vblank flag on read (BIT sets the Neg flag to bit 7)
   
VBLANKWAIT1:
   ; wait for PPU to stabilize
   
   BIT $2002       ; sets CPU Neg flag to Vblank flag
   BPL VBLANKWAIT1 ; if the Neg flag isn't set branch to VBLANKWAIT
   
VBLANKWAIT2:
   ; wait a second time
   
   BIT $2002       ; sets CPU Neg flag to Vblank flag
   BPL VBLANKWAIT2 ; if the Neg flag isn't set branch to VBLANKWAIT
   
INIT:
   ; enable interrupts
   
   LDX #%10001000 ; PPU control bits
   STX $2000      ; set PPU control register #1
   
   CLI
   
LOOP:
   ; main loop
   
   STX $00
   JMP LOOP
   
   RTI

NMI:
   ; Non-maskable interrupt
   
   LDA #$3F       ; set address pointer in VRAM to $3F00
   STA $2006
   LDA #$00
   STA $2006
   
   LDA #$17       ; write a little to background palette
   STA $2007
   LDA #$03
   STA $2007
   
   LDA #$20       ; set address pointer in VRAM to $2000
   STA $2006
   LDA #$00
   STA $2006
   
   LDA #%00000000 ; write a little to name table #0
   STA $2007
   
   LDA #$23       ; set address pointer in VRAM to $23C0
   STA $2006
   LDA #$C0
   STA $2006
   
   LDA #%00000000 ; write a little to attibute table #0
   STA $2007
   
   RTI

IRQ:
   ; Interrupt request

   RTI

; iNES header
.segment "HDR"
   .byte "NES" ; signature
   .byte $1A   ; signature
   .byte $02   ; # of 16kb PRG-ROM banks
   .byte $01   ; # of 8kb CHR-ROM banks
   .byte $00   ; ROM control byte one
   .byte $00   ; ROM control byte two
   .byte $00   ; # of 8kb RAM banks
   .byte $00   ; reserved

; Vectors
.segment "VEC"
   .addr NMI
   .addr RESET
   .addr IRQ

; Pattern tables
.segment "CHR"
   ; one tile

   ; least significant bit one
   .byte %00011000
   .byte %00100100
   .byte %01011010
   .byte %01000010
   .byte %01000010
   .byte %01011010
   .byte %00100100
   .byte %00011000
   
   ; least significant bit two
   .byte %00000000
   .byte %00000000
   .byte %00000000
   .byte %00000000
   .byte %00000000
   .byte %00000000
   .byte %00000000
   .byte %00000000



Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 11:39 am 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
I see no $2005 writes or clearing of $2006, which from what I can see looks to be the root cause (based on lines shown in the 3rd screenshot, which indicate where the X/Y scroll are at).

http://wiki.nesdev.com/w/index.php/PPU_scrolling
http://wiki.nesdev.com/w/index.php/The_ ... _scrolling

I can give out about 5 or 6 URLs of threads here on the forum discussing all of this too, and they're just as convoluted. Just search for any posts from me with $2005 or PPUSCROL mentioned, but I guess the most applicable post would be this one from tokumaru:

viewtopic.php?p=108239#p108239


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 11:57 am 
Offline

Joined: Tue Apr 05, 2005 7:30 pm
Posts: 179
I see two problems with your code.

First, there is something you need to know about the address register at $2006: it is also used by the PPU during rendering. It needs to be initialized during vertical blank after using it to update PPU RAM (tables, palette). There are two other registers that affect the internal address latch: $2000 and $2005. Ideally, you should do the following after updating whatever needs to be updated in PPU:

A. Set the scroll in $2005 to whatever horizontal and vertical scroll values you need, and
B. Set $2000 using the name table you need for bits 1 and 0 (making sure to appropriately set the NMI enable and sprite/background bits).

Second, you did not disable the 2a03 APU's frame counter by writing a 1 to bit 6 of $4017. This means that it will continue to send IRQs to the CPU until:

A. the frame interrupt is acknowledged by reading $4015, or
B. the frame interrupt is inhibited by writing a 1 to bit 6 of $4017.

The fastest way to deal with this, of course, is to simply do an SEI and be done with it, if you're not using any IRQs at all, which is the approach I would go with in this case.

Quote:
Secondary/tangent questions:

Why is so much of the NES memory mirrored? From a newb point of view this seems wasteful.
From (http://wiki.nesdev.com/w/index.php/Init_code); disabling IRQs (opcode SEI) is good if one might later desire to manually call the RESET routine. This makes sense to me but I have noticed that the order in which NMI and IRQs are enabled (I'm referencing my "INIT:" label in "test.s" below) determines whether or not PPU memory is written to (i.e. calling "CLI" before setting the 7th bit of PPU control #1 to 1 causes pattern table, name table, and attribute table to be empty; it might actually be causing the whole NES to crash, I'm not sure.)
Here I'm thinking it might be best to not manually disable IRQs (call SEI) and let the system do it naturally on interrupt for the benefit of simplicity.


NES memory is mirrored because Nintendo wanted to keep manufacturing costs low, I think.

IRQs need to be acknowledged by the programmer (the method of this depends on what hardware causes the IRQ), or they will execute continuously. This, I think, was meant to allow for multiple IRQs occurring, so that each hardware that requested the interrupt could be serviced in turn. IRQs are not supposed to interfere with NMIs ideally. The only reason why it would happen on the NES is because the IRQs are firing off continuously before the PPU hardware is given a chance to be allowed to fire off NMIs.

Interrupts only disable IRQs until the RTI instruction is executed. The RTI instruction returns the I flag to whatever it was set to before. Therefore, you still need to disable IRQs outside of interrupt code if you don't want them to happen.

I hope this hasn't been too confusing, I will attempt to clarify if need be.

_________________
Be whatever the situation demands.


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 1:24 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10117
Location: Rio de Janeiro - Brazil
StephenM wrote:
Why is so much of the NES memory mirrored? From a newb point of view this seems wasteful.

Quite the opposite! Mirroring memory requires less logic/hardware than NOT mirroring it. If you have only 2KB of memory to fill a space of 8KB, it's AUTOMATICALLY mirrored to fill the whole space. Since there are no negative side effects from the mirroring in most cases, it's cheaper/simpler to just leave the mirrors alone.


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 3:15 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
I think he meant wasteful of the address space. Like you say, though, it's a tradeoff: waste logical address space, or use more hardware. Given the lack of need for logical address space, they choose to use less hardware.


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 3:32 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
Off-topic:

I've never understood, on the NES/6502, what purpose mirrored CPU addressing ranges served. I'm specifically referring to $0800 to $1FFF and $2008 to $3FFF.

I fully understand what it provides in the case of the SNES/65816, where for example portions of bank $00 are mirrored throughout other banks -- the purpose there should be obvious.

Likewise, on the Apple IIGS (again 65816), there were some "shadowed" (mirrored) registers and memory ranges which I was told time and time again by older/senior programmers should be used because, somehow at some particular level (hardware-wise), "they were faster than using the non-shadowed versions". I still don't understand how this can be the case, since it's going to cost the same number of CPU cycles no matter what the address is; I can see the benefit if there's a separate bus that runs at a higher clock speed that the mirrored/shadowed areas ties into, and that, say, a DMA chip could utilise (for memory copying/transfer), but the IIGS had no such thing. The only thing that made sense (in this regard) was the "mirroring" of banks $00 and $01 to banks $E0 and $E1 on the IIGS, where banks $00/01 ran at a slower clock speed (to be compatible with the earlier Apple II models) while banks $E0/E1 ran at a higher clock speed (reference; search for the phrases "shadowing is the term" and "$E0 and $E1 execute at standard clock speed"). I believe what I'm remembering are people telling me to access certain registers via $E0C0xx rather than $00C0xx to benefit from the higher clock speed; but how this matters I still don't understand (for example both sta $e0c034 and sta $00c034 would take 5 cycles (assuming 8-bit accum)).

Anyway, in the case of the NES/6502, I just don't see anything advantageous from the aforementioned mirroring.


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 3:44 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
2A03 has limited pins. PPU/SRAM/etc. decoding is done externally with a '139. This generates select signals for the cartridge, internal SRAM, and PPU. It would have taken 3 more pins on the 2A03. I'm guessing that this would have pushed it into a package size that cost noticeably more at the time (economics of size aren't just the small amount of extra plastic/pins, but the quantity produced, molds, etc.) 2A03 and 2C02 also both have 40 pins, so they probably get some economy by using the same count.


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 3:48 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5827
Location: Canada
koitsu: Why would there need to be a software advantage to justify the mirrors on the NES? If it simplifies the hardware to leave it mirrored, isn't that purpose enough?


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 3:55 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
Well the 2A03 has 16 pins for A0-A15 (hence making up the full 16-bit address bus), and I understand that a 74LS139 is used to handle designation of "what speaks to what" (CPU vs. PPU vs. SRAM vs. external), but I still don't see how that justifies the $0800-1FFF and $2008-3FFF mirroring within CPU addressing space. Why is that needed? Why couldn't it just be left floating/open bus for the CPU? Or is there some kind of "commonality" (not sure what other word to use here) between the addressing spaces (as far as the 74LS139 goes) within the PPU vs. CPU vs. SRAM vs. external that justifies this need?


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 4:00 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
rainwarrior wrote:
koitsu: Why would there need to be a software advantage to justify the mirrors on the NES? If it simplifies the hardware to leave it mirrored, isn't that purpose enough?

How does it simplify the hardware design? That hasn't been explained. A reference to a 74LS139 decoder being used does not explain why said CPU addressing ranges needed to be mirrored (respectfully blargg, not arguing or being combative).


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 4:26 pm 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19245
Location: NE Indiana, USA (NTSC)
Leaving certain address ranges as open bus would have required more hardware to generate the select signals for those ranges.


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 4:41 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
The '139 has two one-of-four decoders. Each has an active-low enable, two inputs, and four active-low outputs.
Code:
A   B  /EN /Y0 /Y1 /Y2 /Y3
---------------------------
X   X   H   H   H   H   H
L   L   L   L   H   H   H
L   H   L   H   L   H   H
H   L   L   H   H   L   H
H   H   L   H   H   H   L

The NES feeds the first one A from PHI2 and B from A15, with /EN low so it's always enabled. This generates /Y1 on access to 0-$7FFF, and /Y3 on access to $8000-$FFFF. The latter goes directly to the cartridge. /Y0 and /Y2 aren't useful as they indicate the other half of a cycle.

/Y1 is tied to the second decoder's enable, so it's enabled on access to the low half of the address space. A goes to A13 and B to A14. This divides the low half into four segments, 0-$1FFF, $2000-$3FFF, $4000-$5FFF, and $6000-$7FFF. It uses /Y0 for SRAM and /Y1 for the PPU. To "fully decode" SRAM, it'd need to look at A12 and A11, only enabling then they're low. To fully decode the PPU, it'd need to additionally look at A3-A12 (10 additional address lines), only selecting the PPU when they are all low. This adds hardware and latency due to gate propagation delays.

Incomplete decoding is elegant in the context of doing enough to work and not wasting extra hardware on things that don't matter.


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 4:42 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5827
Location: Canada
Well, for example, mirroring a particular range 4 times means that there are 2 bits in the address decoding for it that don't have to do anything. The mirroring is simply a side effect of ignoring those bits.

Mirroring is a just a side effect of not using more address lines than are strictly needed. It's not a situation you would normally go out of your way to create, because mirrors aren't normally useful. (The Apple II thing you described is fairly unusual, I think.)


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 5:15 pm 
Offline
User avatar

Joined: Fri Aug 30, 2013 10:18 pm
Posts: 13
Quote:
I see no $2005 writes or clearing of $2006, which from what I can see looks to be the root cause

Quote:
A. Set the scroll in $2005 to whatever horizontal and vertical scroll values you need, and
B. Set $2000 using the name table you need for bits 1 and 0 (making sure to appropriately set the NMI enable and sprite/background bits).

Thanks! Seems like scrolling can get quite complicated! I will eventually have to master scrolling in both horizontal and vertical directions.

I've added the following ASM, it seems to put my X, Y at (0, 0) and clear $2006 which is what I'm going for.
Code:
LDA #$00       ; x position
STA $2005
LDA #$00       ; y position
STA $2005
   
LDA #$00       ; clear $2006
STA $2006
LDA #$00
STA $2006

(no reason why I'm setting the accumulator multiple times. I wouldn't do this in a real project)

It seems that the bigger problem was that PPU control byte #2 was being written to $2000 instead of $2001, that explains why nothing was being displayed. I must have copied and pasted the first PPU control line, changed the bits, but forgot to change the address it was being written to.

Success!
Image


Top
 Profile  
 
PostPosted: Thu Sep 12, 2013 5:38 pm 
Offline
Formerly 65024U

Joined: Sat Mar 27, 2010 12:57 pm
Posts: 2257
StephenM wrote:
Code:
LDA #$00       ; x position
STA $2005
LDA #$00       ; y position
STA $2005
   
LDA #$00       ; clear $2006
STA $2006
LDA #$00
STA $2006



Wrong, you have to not care about $2006, and write to $2005 two times, and then to $2000 to set the scroll. Only use $2006 to set scroll during rendering.


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 4 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