I tested the counter long time ago to split the screen at a specific region but now I have different need and unfortunately I cannot figure out from the doc if I want to do is something possible with this mapper or not. My guess is yes.
What I want to do is:
- top of screen (let say 50 lines) scroll to the right
- second part of screen (almost until end of screen) scroll to the left
- last part of screen stay centered and BG bank is switched to show proper CHR data
The reason for the bank switch is to show text and it doesn't fit inside the background data. This is an animation effect during a transition so this is not used during gameplay.
With the technical reference I cannot figure it out, maybe there is a tutorial on the subject? I will check again in the wiki.
While on the subject, another use-case I will have is:
- Scroll an image from top to bottom
- leave a black window a the bottom to display text while the image is scrolled
- this mean the black window will overlay the image while scrolling
In that case I guess I will need to set horizontal mirroring then put the image first nametable and maybe it will spill in second one. Then at the end of the second nametable the text windows will be there (this is all guessing but my mental image of it).
Thank you in advance for any information on the subject.
Banshaku wrote:
What I want to do is:
- top of screen (let say 50 lines) scroll to the right
- second part of screen (almost until end of screen) scroll to the left
- last part of screen stay centered and BG bank is switched to show proper CHR data
The reason for the bank switch is to show text and it doesn't fit inside the background data. This is an animation effect during a transition so this is not used during gameplay.
Of course this is possible, with 2 different IRQs per frame. Since you say this is not used during gameplay I assume your CPU is mostly unused and you don't even need IRQs in the first place, polling $2002 or and/or using timed loops will work as well.
Quote:
While on the subject, another use-case I will have is:
- Scroll an image from top to bottom
- leave a black window a the bottom to display text while the image is scrolled
- this mean the black window will overlay the image while scrolling
In that case I guess I will need to set horizontal mirroring then put the image first nametable and maybe it will spill in second one. Then at the end of the second nametable the text windows will be there (this is all guessing but my mental image of it).
That sounds fine to me. I'm not sure what you mean by "scroll an image from top to bottom", but if this means scrolling vertically by some pixels but less than two nametables high, then it's fine.
You could also use vertical mirroring, have one nametable dedicated to the image and the other nametable dedicated to text, and write portions of the image as it scrolls vertically. This is required if it's taller than two-nametables-minus-text-height.
You can have as many MMC3 IRQs as you want in a single frame. If I'm not mistaken, any subsequent IRQ is set up just like the first one: latch the counter value, reload it and enable IRQs.
Also, don't forget that in order to change the vertical scroll you need to perform a full scroll reset using the
$2006/5/5/6 trick.
tokumaru wrote:
You can have as many MMC3 IRQs as you want in a single frame.
I think there's a limit to one IRQ every two lines, I'm not sure what was the reason, but I'm fairly sure this was the case. Even if you could trigger an IRQ immediately by some configuration, this is pretty useless as this'd be equivalent to a JMP or JSR instruction.
Quote:
Also, don't forget that in order to change the vertical scroll you need to perform a full scroll reset using the
$2006/5/5/6 trick.
Nope in many cases when changing vertical scroll only (and in this case most specifically) just $2006/$2006 will be enough.
So it's possible, which was my assumption but here where I feel dumb a little bit. Even though I read the wiki, I have no idea how to set 3 of them
I see reload and latch which give me the impression that it would reset the currently active one, which I'm sure my interpretation is wrong but this is why I was asking for more detail on the subject.
(the numbers are just for asking the question, I do not know the exact question yet)
If for example I want to react from the beginning (I guess scanline 0), scanline 100 and something around 200:
- How do I set those 3 irq
- when one is launched, how do I know which one of the 3 was triggered so I can do the proper action
Once I understand how this part work then the rest is just scrolling (scanline 0 and 100) and switching bank (scanline 200), which should not be that difficult and I should be careful about what you both mentioned.
Thank you again for the information on the subject.
Banshaku wrote:
If for example I want to react from the beginning (I guess scanline 0), scanline 100 and something around 200:
- How do I set those 3 irq
- when one is launched, how do I know which one of the 3 was triggered so I can do the proper action
I've never used MMC3's scanline counter, but I'll answer what I'd do. You don't set 3 IRQs but 2 (at line 100 and 200), knowing you already have good old NMI at line 240 as usual. You don't set multiple IRQs simultaneously (that's not possible) but each IRQ is set in the previous IRQ, or in the VBlank NMI for the 1st one. In this particular case, you need to set IRQ for line 100 in the VBlank NMI, and you need to set the IRQ for line 200 in the IRQ for line 100. Just remember the # of lines to count will still be "100" obviously (or maybe 99 or something like that); because you have to use the # of scalnine to wait and not the total # of scanline since VBlank (on MMC3 - only MMC5 has an internal scanline counter, and as such always uses absolute number for scanlines instead of relative numbers like other mappers).
When having more than one IRQ per frame you need to build a simple state machine to know what you're doing. In a typical MMC3 game I'd assume there's several kinds of IRQ anyway (title screen, cutscene, gameplay, ending, whathever...) and you'll need to differenciate from them as well, to differentiate beween IRQ #1 and IRQ #2 of the cutscene would be pretty much the same mechanic.
Also you could just as well mix IRQs with good old syncronisation methods that needs no specific hardware, although there's no reason to do that other than to avoid the state machine, which is in itself a valid reason if you feel lazy (who doesn't). Just use a fixed-time loop or a sprite-zero hit to wait between scanline 100 and scanline 200; or to wait between the end of VBlank NMI until scanline 100, or whathever you feel like doing. It's all up to you.
Bregalad wrote:
Nope in many cases when changing vertical scroll only (and in this case most specifically) just $2006/$2006 will be enough.
Nope, because the first $2006 write clears the topmost fine Y scroll bit. Just $2006 will allow you to scroll to rows 0-3 of a tile, but if you want the full range, you need the $2006/5/5/6 trick.
If you have a different IRQ system for different parts of your game it is pretty useful to set up multiple IRQ handlers in software by pointing the IRQ vector at RAM, where you place a JMP absolute (hex: $4C) instruction when initializing the console, and then the two byts after it will be used as the pointer to the IRQ handler currently needed. This provides some more flexibility at the cost of a 3 CPU cycle delay for all your IRQ handlers. Or if you need many IRQs you can keep count of your IRQs for the current frame and then use it as an index to fetch the next IRQ counter value from a table. Although I personally use the DMC IRQ, the principle is the same... all the stages have different split points, so I have an array of arrays containing screen split timings.
I'm already using custom NMI and change the address in ram when a new one is necessary. I was expecting to do the same for IRQ too.
One idea I have after reading this thread (but I'm not sure if this is appropriate or not is):
- First trigger is NMI for top scroll
- from NMI, update IRQ handler for phase 1 and request IRQ for 100
- inside phase 1 IRQ, update IRQ handler for phase 2 then request IRQ for next step
I'm not sure if changing handler inside IRQ is a common practice but usually I try to avoid issue by first putting RTI opcode at the IRQ address location then change irq_adr+1 first then irq_adr.
I guess for now the best thing would be to try it ^^;;; The only thing is since I need to scroll one from the left and the other part from right, I think that seems to imply that the screen will requires to be split in 2 and set in different nable table to achieve this effect, so they can "reconnect" to make the main screen.
Banshaku wrote:
I'm not sure if changing handler inside IRQ is a common practice but usually I try to avoid issue by first putting RTI opcode at the IRQ address location then change irq_adr+1 first then irq_adr.
The IRQ handler is indeed the best place to set the address of the next handler. You don't even have to replace the JMP with an RTI before changing the address, since the I flag (which gets set when an IRQ fires) will prevent other IRQs from firing, so there's absolutely no chance of an IRQ firing while you're changing the address.
I see. So chaining that way seems the simplest to manage states and IRQ is not triggered. I was not aware of that.
I just erased the rest of my post content because I just realized that I read "presumably at PPU cycle 260 of the current scanline" has " presumably at PPU scanline 260", which makes a hell of a difference. I hate to be sleep deprived ^^;;;;
tokumaru wrote:
Nope, because the first $2006 write clears the topmost fine Y scroll bit. Just $2006 will allow you to scroll to rows 0-3 of a tile, but if you want the full range, you need the $2006/5/5/6 trick.
I don't think he wants to scroll vertically, but just show some fixed text on the bottom so he can do that with $2006 only; the top part is what scrolls and he can do that with $2005 normally.
Also the RAM trick is cool, but you can also use JMP (indirect) as an alternative. Replacing the JMP instruciton in RAM ($4c) by an RTI is an awful idea for IRQs, as if an IRQ effectively fires and goes to a RTI instruction, it will re-fire again immediately, and so on infinitely, crashing the program, so it's the almost same effect as jumping to an invalid address basically. It's best to be sure the I flag is set when modifying the adress; either that or just rely on proper timing so we're sure no IRQ fires when changing the address (sure this is "bad practice" (tm) but so is a lot of NES coding standard practices).
@Bregalad,
the first animation is separated in 3 and requires 2 scroll (top part comes from left, middle one from right, bottom one doesn't move).
The second animation is a screen separated in 2 section:
- the top part is a viewport, let say of a height of 150 pixel, that scroll an image vertically (image height of 300 pixel)
- the bottom part is static and show some text while the viewport scroll an image
I guess without seeing a concept art it is difficult to visualise. Hopefully by saying viewport it's make it a little bit clearer.
Bregalad wrote:
I don't think he wants to scroll vertically, but just show some fixed text on the bottom so he can do that with $2006 only; the top part is what scrolls and he can do that with $2005 normally.
You're right. For some reason I thought the vertical scrolling part was in the middle of a 3-part screen, but I see now that's it's the very first part. So yeah, you can do the fixed part below using just $2006.
Quote:
you can also use JMP (indirect) as an alternative.
This looks cleaner, but is also 2 cycles slower.
Quote:
Replacing the JMP instruciton in RAM ($4c) by an RTI is an awful idea for IRQs, as if an IRQ effectively fires and goes to a RTI instruction, it will re-fire again immediately, and so on infinitely, crashing the program, so it's the almost same effect as jumping to an invalid address basically.
You're right. I've seen this technique bring used for NMIs, but for IRQs it indeed doesn't make much sense.
oh. So this old code is better be used for NMI only?
Code:
;->BEGIN----------------------------------------------------------------------
; Generate dynamic code in memory for jump to function.
;
; Note: Before updating the code, the first opcode is set to RTI prevent
; a call to an half updated address.
;
.macro setDynamicRtiFunc source, destination
lda #OPCODE_RTI ; First disable it to make sure it's not called when
sta destination ; updating address
lda #<source ; Set address
sta destination+1
lda #>source
sta destination+2
lda #OPCODE_JMP ; Then set back the jump instruction
sta destination
.endmacro
;-<END------------------------------------------------------------------------
I didn't try dynamic RTI yet but I was expecting to use the same pattern.
Indeed for NMI it's not a problem, as it is edge-triggered and not level-triggered, so NMIs will trigger once and won't necessary need to be acknowledged.
For IRQ, you could SEI while the address is being changed (unless you have an NMI handler that does CLI).
I do not use CLI inside NMI.
I think I could always try both and see how it goes but for now, the only thing I'm a little bit confused is about when to start so it goes the right scanline.
How do I start it properly to make it sure it will be run at scanline 100? For example, inside the NMI I first scroll the top part to it's proper position. Then I enable the IRQ then set the latch but when you set it, the wiki say something like:
Quote:
this value will be copied to the IRQ counter at the NEXT rising edge of the PPU address, presumably at PPU cycle 260 of the current scanline.
Since we are inside NMI and the code is executed between scanline 240~260 of vblank, wouldn't that affect where the counting would stop (unless, just guessing, vblank doesn't have a rising edge on A12)? If it was that fragile nobody would be using it so this comment in the wiki is puzzling me a little bit.
I guess I need to check more deeper the inner working to know if vlbank has a rising edge of not. I don't know why I need to know that much just to understand a counter but I don't mind knowing more about it
The scanline counter is only clocked during rendering (actually, when the PPU address lines change, so $2006/7 can also affect it). If you set up the IRQ after you're done with all PPU manipulation in vblank, the counter won't be affected until rendering starts, so the IRQ will reliably fire on the scanline of your choice even if you don't set it up at the same time every vblank.
Subsequent IRQs should be set up before the end of the next scanline though, since that's when the counter will be clocked.
The "during rendering" is the part I didn't figure out from the wiki. I'm sure I understood how it worked when I tried it a long time ago, I don't know why I'm not able to figure out by myself this time. strange.
So I will first test a IRQ defined inside the NMI. When you chain them the location of the previous one is important but let's say you only have 1 line to check. Once the IRQ is fired, you just disable the IRQ inside the handler? If not, if the IRQ is set at 50, it would fire more or less 4 time because of the 240 lines available. Is my understanding correct?
Banshaku wrote:
The "during rendering" is the part I didn't figure out from the wiki.
Yeah, the mapper is designed to be clocked by the memory fetches that happen during rendering, but you can, intentionally or not, manipulate the VRAM address manually in ways that'll also clock the scanline counter.
Quote:
So I will first test a IRQ defined inside the NMI. When you chain them the location of the previous one is important but let's say you only have 1 line to check. Once the IRQ is fired, you just disable the IRQ inside the handler? If not, if the IRQ is set at 50, it would fire more or less 4 time because of the 240 lines available. Is my understanding correct?
Well, you have to acknowledge the IRQ by disabling them (I think), otherwise the CPU would jump to the IRQ handler again as soon as you RTI'd, and since you're disabling them, no more will fire unless you enable them again.
tokumaru wrote:
Well, you have to acknowledge the IRQ by disabling them (I think), otherwise the CPU would jump to the IRQ handler again as soon as you RTI'd, and since you're disabling them, no more will fire unless you enable them again.
Oh, that's an important point that I missed and don't remember reading that on the wiki. So if you don't acknowledge and the code in the RTI finishes it will cause some find of infinite loop since the counter is now at zero and is fired indefinitely. That's brutal
I will re-read the wiki because I want to know why I missed that. So the order would be:
- you latch to a specific count ($C000)
- enable the IRQ ($E001)
- wait for the RTI
- once triggered, process task and acknowledge the IRQ ($E000)
! If you forget to acknowledge, you will end up in an infinite loop
What is left to figure out is what is reload ($C001) and how it is used. Maybe it so change the counter while a count is running?
edit:
The wiki talks about acknowledge on disable. I completely missed that part. Maybe I was either too tired of didn't understand the meaning of acknowledge in this context.
Banshaku wrote:
So if you don't acknowledge and the code in the RTI finishes it will cause some find of infinite loop since the counter is now at zero and is fired indefinitely.
It's not so much the state of the counter, but the difference between how the CPU handles NMIs and IRQs:
https://wiki.nesdev.com/w/index.php/CPU_interruptsLike Bregalad said, NMIs are edge-triggered, meaning that the CPU generates an NMI when the /NMI pin transitions from high to low. This means that even if the pin remains low, no more NMIs will fire, because there's no transition, it's permanently low.
IRQs however, are level triggered, so as long as that line remains asserted, the CPU will keep generating IRQs. Most IRQ sources provide a way to acknowledge the interrupt and turn off the IRQ signal. For the MMC3, that's the IRQ disable register ($E000).
Quote:
- you latch to a specific count ($C000)
- enable the IRQ ($E001)
- wait for the RTI
I think it's LATCH, RELOAD, ENABLE (you forgot "reload"). Also, you mean wait for the IRQ, right?
Quote:
- once triggered, process task and acknowledge the IRQ ($E000)
! If you forget to acknowledge, you will end up in an infinite loop
Yup.
Quote:
What is left to figure out is what is reload ($C001) and how it is used. Maybe it so change the counter while a count is running?
Reload is what effectively puts the value you latched into the counter. Latching won't do anything unless you reload.
Sorry regarding RTI, it was IRQ. I'm sleep deprived today and I make twice as much mistake as usual (and I usually do a lot of them! ^^;;)
Now that I have the right order to execute it, I should be able to try it properly. I think I should write a small document later that explain how to go from A to B without all the details from the wiki. Often the "recipe" is more than enough without understanding why the "ingredients" where used that way.
You mention this:
Quote:
Reload is what effectively puts the value you latched into the counter. Latching won't do anything unless you reload.
This is why the wiki confuse me. If I read the wiki under the latch section:
Quote:
This register specifies the IRQ counter reload value. When the IRQ counter is zero (or a reload is requested through $C001), this value will be copied to the IRQ counter at the NEXT rising edge of the PPU address, presumably at PPU cycle 260 of the current scanline.
and you look at the part I highlighted, they say that it will be copied to the IRQ counter but their is no mention that you have to reload to activate it. From that phrase, I'm making a wrong assumption. Maybe it should be clarified at that specific place (maybe I should just add it to the wiki).
There is some mention later that it will not be activated until reloaded or counter reach zero but that doesn't explain what you are supposed to do when you are in the initializing phase. Basically, all the information is actually there but not in away to understand in which order to use it unless you actually know about it.
Thank you again for the details, I should be able to do a quick test today.
Banshaku wrote:
Sorry regarding RTI, it was IRQ. I'm sleep deprived today and I make twice as much mistake as usual (and I usually do a lot of them! ^^;;)
Don't worry!
Quote:
Often the "recipe" is more than enough without understanding why the "ingredients" where used that way.
Yeah, the actual operation of MMC3 IRQs is fairly simple, you don't really need to know all the intricate details of the hardware... I sure don't!
Quote:
Quote:
This register specifies the IRQ counter reload value. When the IRQ counter is zero (or a reload is requested through $C001), this value will be copied to the IRQ counter at the NEXT rising edge of the PPU address, presumably at PPU cycle 260 of the current scanline.
and you look at the part I highlighted, they say that it will be copied to the IRQ counter but their is no mention that you have to reload to activate it.
It does say that the value is only copied to the counter when the counter reaches zero or when a reload is requested, which implies that a reload is necessary. It doesn't seem very relevant to us programmers that the copy only happens at cycle 260 though.
Quote:
There is some mention later that it will not be activated until reloaded or counter reach zero but that doesn't explain what you are supposed to do when you are in the initializing phase. Basically, all the information is actually there but not in away to understand in which order to use it unless you actually know about it.
I agree, the information is all there, but the actual steps you have to take in order to just use the IRQ are not very clear. Maybe a quick example of a single IRQ being set up and acknowledged would help.
I did a quick test during lunch and I was able to make it work at a specific line. Even with code mixed in C/asm it worked quite well.
On thing that may be obvious (but not when you are half asleep at the wheel):
you have to CLI even though you enable the interrupt. So even enabled, if CLI is not set then nothing happens. oppsie.
Beginners will forget that, I'm pretty sure. I guess I should write a basic sample later in the wiki then. Can't wait to test the real target, triple counter with scroll from left and right and static at bottom with bankswitching.
edit:
As for latch/reload, for me I read it the opposite way. When I latch I "load" it. To me, reload means to load it again so it idea get stuck in the back of my head even though it is written you have to reload it. From a vocabulary point of view, it latch loads it, why do I need to "reload" it to load the value?
Yeah, you absolutely need the CLI. Unlike NMIs, IRQs can be masked by the CPU, and they will be ignored if the I flag is set. One mistake I made when I first experimented with the MMC3 was to CLI inside the NMI handler, which obviously didn't work, because once the handler returned via RTI, the status flags were restored from the stack, and the I flag was set when the NMI fired. It took me a while to realize I had to CLI in the main thread, not inside an interrupt handler.
Banshaku wrote:
I did a quick test during lunch and I was able to make it work at a specific line. Even with code mixed in C/asm it worked quite well.
Cool.
Quote:
As for latch/reload, for me I read it the opposite way. When I latch I "load" it. To me, reload means to load it again so it idea get stuck in the back of my head even though it is written you have to reload it. From a vocabulary point of view, it latch loads it, why do I need to "reload" it to load the value?
I can understand the confusion, but reading it carefully (while not sleep-deprived, preferably!), you can tell that the counter can't be directly modified, it can only be copied from the latch, and there are 2 cases where that can happen - when the counter reaches 0, or when you manually request a reload. I guess that the word "reload" is used because the mapper will automatically restore the counter every time the counter reaches 0, kinda like you reload a gun when you've fired all the bullets. Yeah, not a great analogy, I know...
When I started learning NES programming, I was very puzzled by the word "latch"... I don't think we have a portuguese translation for it in the context of electronics, so it took me a while to really grasp the concept. I don't know if this is a problem that happens in English too... maybe most people just think of the metal things you use to close doors/gates when they think of the word "latch", which IMO doesn't really help understand what a latch is in electronics.
So this is why I had all kind of weird jitters by setting CLI in the nmi
I just made a quick method that call it from C in the main thread and it started to works fine.
Explained that way, that you cannot update the counter directly, because of the way you phrased it, it is actually more complete and easier to read than the wiki one. Now it is perfectly clear. I guess the low level technical speech and the explanation for a programmer is completely different.
still today, I have the same problem with the meaning of latch too . I remember seeing that in the DOS days for VGA X-Mode where you have to unchain the latch or something like that (maybe I'm mixing wordds now ^^;;) to access all the 256 k or memory but that word always stumbled me since I never found an equivalent in French and the only word I found is the metallic thing, like you mentioned, which make little sense. I guess I need to understand the electronic concept and it will make a lot more sense.
Maybe @Bregalad has a simple explanation on the subject since being French native, he may have had the same issue as me with this word?
I always had huge problems for the word "latch" (usually used untranslated when speaking French). Sometimes it's used as an equivalent to "D flip-flop" (for instance, the CNROM/UNROM "latch" retaining the bank number is a series of D flip-flops). On the other hand when doign CPLD/FPGA developement I was taught to "avoid latches and use registers instead", which means that I had to avoid design that ordered the programmable logic to have one transition condition being clocked mixed up with another transition being asynchronous.
For instance if I wanted to have a flip-flop that sets to "1" immediately when a button is pressed, and reset to "0" on a clock cycle when some condition is met, this would synthetise a "latch" that should be avoided at all costs. Either both conditions must be clocked; or (more rarely) both shouldn't.
I guess latch is not easy to understand without an electronic background. Which kind of latch is the one on MMC3 then?
As for counter, I did some extra testing while being half alseep yesteday before going to bed. This time on line 200 I was changing the bank so the text would be shown properly then in VBLANK I would bring back the original bank so the top part would not be affected. It was "kinda" working except the part after 200 was going back and fort between was was shown. Either there is a bug in the code (most probable cause) ir there is something I don't know about switching banks at specific location.
Right now CHR is in 2K mode for background and a little bit of sprites are used for cosmetics reason (adding color to a few part of the background).
It's a good thing that it's not inside the game but it's all uncharted territory for me so I'm not sure where it went wrong yet. Getting close though!
One more thing: if I need to do some color animation for a short time for the upper part but for some reason 1 color only happens to be on the lower part and should not be affected, if I change that one color only on a scanline that is black with no sprites, should I expect any artifacts? Unfortunately there is too many colors on the screen that I cannot rearrange the color to avoid that clash (#$@$@ nametable way of thing) but now that I'm thinking out loud, maybe "that" part could be switched to sprites if not more than 8 but I think it is... Hmmm...
Cannot show what I'm doing yet.. Will show it someday, when ready
Banshaku wrote:
I guess latch is not easy to understand without an electronic background. Which kind of latch is the one on MMC3 then?
I didn't check but I'm fairly sure it's used as an equivalent to "register" or "D-flip-flops".
"latch" is a generic term.
"transparent latch" is the variant that holds values while disabled, and is transparent while enabled.
a "register" is the variant that gets a new value on one direction of clock edge
a "D latch" is underspecified, and could mean either
an "S-R latch" is one that has just "set" and "reset" inputs
a "J-K latch" is like an S-R latch but has a clock input, which is to say it's a kind of register.
As to how it's implemented inside the MMC3? Could be either, really. Inside a computer, a latch often will look like a register.
Banshaku wrote:
I remember seeing that in the DOS days for VGA X-Mode where you have to unchain the latch or something like that (maybe I'm mixing wordds now ^^;;
To the best of my memory, there wasn't a latch
specifically related of unchaining the planed memory. And what latches I remember there being were definitely the generic "it holds a thing" kind of latch rather than any narrower definition.
lidnariq wrote:
a "D latch" is underspecified, and could mean either
What you said is probably correct except for this. A D latch is NOT a SR nor a JK, it is a very specific type probably equivalent to what you name "register". For some reason "registers" seems more often used when there's either more than 1 bit or when it's in a programmable logic development context. When it's outside of programmable logic and is a single bit, it's called a D flip-flop, even though it's the same thing as a register. Don't ask me why.
a "D latch" could mean either a transparent latch or a register.
Is $2002 a "register" in this sense? Why or why not? Is the same true of $2000 or $2007?
In
this post, koitsu was wondering why I often refer to memory-mapped I/O (MMIO) addresses as "ports". Your answer informs how I proceed with the explanation.
$2002 is neither?
When the CPU reads from $2002, it's just using a tristatable buffer:
if both /r2002 and _io_ce are low, read_2002_output_spr_overflow is high.
if read_2002_output_spr_overflow is high, 3753 is low.
if 3753 is low, either node 522 pulls _io_db5 high, or node 3743 pulls _io_db5 low, depending on the current contents of spr_overflow .
Similar logic is present for spr0_hit.
Specifically the behavior of clearing the NMI flag:
if both /r2002 and _io_ce are low, read_2002_output_vblank_flag is high. This node in turn is part of an S-R latch, which is neither a transparent latch nor a register. The S-R latch is why reading from it at the exact same time as it would be turned on causes it to stay off.
Writing to $2000 and $2001 uses transparent latches, more or less. (It's implemented with transmission gates, so it's not the "normal" transparent latch. None the less, it's level-based instead of edge-based). While write_2000_reg is high, the data bus replaces the current value of the bits; while it's low, the feedback loop is closed and it retains the current value.
In general, I don't think I've seen many edge-triggered behaviors inside the NES; almost everything appears to be transparent latches using multiple nonoverlapping clock phases.
After reading all those comments I feel dizzy ^^;; I'm not ready yet to go that deep
It's great to see those details though. I enjoy reading them even though I don't understand them properly
As for the split, like talked in the IRQ thread, I'm not able to make a clean one yet. there is 2 possibility I see:
- I'm not doing it properly (duh)
- the surrounding code impede the mid-screen split because of some side effects I'm not aware yet
For now, I'm not sure which one is the cause. If there would be stub example for the MMC3, I could take that and try with some test data and see it is flicker the same way. I will try to build a basic stub but I'm not sure where I'm doing something wrong. Maybe I need to wait for the next scanline since my scroll spill in the next one, causing the jitters, not sure yet.
What does Mesen's event viewer show?
From memory, I see the "window" that shows where the nametable pointed jumping from 2000->2400, back and forth but somehow it seems un-natural (gut feeling only). After line 75, where the split occurs, some of the lines become dark like it is jumping again back and forth when it shouldn't so I'm starting to have a feeling that some of my C code may have some side effects that I'm not aware of.
Since this is realtime, it's not like I can put a break point and check the state of the code when it's runs at 60 frames per seconds. I guess I need some visual feedback which may be possible with mesen scripting but I didn't try that option yet but it may become necessary.
I still feel that first I need to test only that case without the surrounding code to see if I can make it work properly. Once I'm sure the code is right, it would become easier to debug the cause. Maybe I should test it first in asm only.
No, that's specifically why I asked about mesen's event viewer. It visually shows exactly when the code is doing raster effects.
oh, sorry about that. I completely misunderstood your question ^^;;
I didn't know about that screen so first I need to learn how it works. Once I have a better understanding I will be able to answer your question. Thank you for showing me that screen! I guess I should test a simple split first to have a better idea about what is supposed to be shown there.
edit:
woah, that screen is very interesting! So much information about registers, colored pixel where event occurs, cycle it occurred, it's great! I just need to understand the details and that will help. My current broken (remaining random testing from yesterday) code for scrolling show that it writes after IRQ on cycle before 261 so NT change would be a no go. First I need to put back the code for my first visually bugged test to make it easier to understand.
edit2:
Found a few bugs that caused the first issue and now with fine x scroll I was able to do something close that I want for the second scroll (ye!). What is left is the final one, which is not working yet since more code is required. If you change fine scroll or ctr flags while palette is animated, sometime it seems to fail (or it is just a coincidence from a another bug).
Getting really happy with the results!
Sorry for the double post.
@lidnariq
I'm starting to find the cause of my bugs. A lot of them are threads related since I'm not used to IRQ, C, ASM code all at the same time. It is pretty useful for light animation but I'm not used to the threading yet.
Another reason of the bug is I was requesting the IRQ after the music was played ^^;;; I didn't realize the impact since the first part had no music and everything was doing well.
What is left to figure out is if a split screen is running (scroll is still done mid-way to hide a part of the screen), it seems to affect the main thread when doing palette update in the NMI and I do not know why yet. I need to investigate that issue.
MMC3 counts A12 rises: palette updates involve writing to addresses where A12 is high. You need to initialize the MMC3 IRQ after all updates are done and you've set your top-of-frame scroll values.
When I saw your post it was in the middle of the night and didn't make much sense (too tired) so I waited before answering with would have been incomprehensible gibberish ^^;;;
Now if I understand well, since the count is done when A12 rise and palette affect A12 (which affect the address too), any address changing "outside" of the IRQ would affect the IRQ count in some way. And I could see that trying to change the palette mid-way would affect the counter then. There is one place that I would like to change only 1 color to fix an issue during a top screen part fade but it may be better to find another trick instead (this part is not done yet).
Right now my current issue is, my IRQ are sets so the VBLANK put top to specific position, the first IRQ change the X location mid-frame (around scanline 75) to hide a part that will be shown later. When PPU flags are set to ON, this IRQ is running, the palette is black and a fade in is requested. Unless I did not understood well, if the palette fade is done in VBLANK, the mid-screen scroll should not affect the palette or anything. Right now if I fade in in that situation, nothing is shown on the screen (fade didn't work or something else went wrong). Same thing for fade out, it doesn't occur.
I would be surprised that I cannot do palette change during vblank when IRQ is running so my first guess is to check the fade method that may be doing some direct hardware access, causing corruption along the way. Those methods have been done recently so they could be the culprit.
I checked my code and the palette fade put the data in a buffer that will be executed on the next refresh so I don't know why yet palette action in NMI seems to create issue when the mid-screen scroll inside IRQ is fired. Hmmm...
edit:
I found the cause and it was related to the fade. When disabling the IRQ everything was fine. So I checked the code and then one point got my interest: I'm using the NMI counter incremented inside the NMI to calculate frames. What if for some reason the NMI reacted differently and causing issues? I switched to wait for VBlank instead of nmi counter and it started to work properly.
Now I will test the second IRQ (second split with bankswitching) and if it works then this thread will be finished then. Sometime some "error" or way of coding manifest their side effects in unusual ways ^^;;;
edit:
All splits working! I just have to be careful about events of scrolling/fading that may overlaps. Thanks everyone!