Fake John Wick nes game on real nes

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Fake John Wick nes game on real nes
by on (#239434)
Danilo of JoyMasher (Oniken, Odallus) released ~2 weeks ago a small tribute to a movie he love, John Wick, for #screenshotsaturday, in a form of a fake nes game video (original tweet).

Since the concept was interesting, I decided to try to port it, since it's a very small 1 level thing. After receiving some adapted assets based on the limitation of the nes and testing how it would work in my current engine, I have a very early prototype of it that I shared yesterday a video on twitter (as a reply instead of a new post by accident ^^;;; link here) .

Since it's a nes game it makes sense to share it on nesdev so for now I will share an animated gif of the current look of it.

Attachment:
john5.gif
john5.gif [ 708.49 KiB | Viewed 6844 times ]

My time is limited, as always, so I don't know when I will finish it but once I have something stable I will start to share a rom of it. For now, only the running assets have been inserted and the rest is a glitch fest so it not usable yet :lol: the artifcats is where I load the palette since the tribute used more colors than the nes allows, same thing for background chr data, so I have to switch midframe a few things here and there. It's not in in hblank yet since my goal yesterday was to have a sample video to share and hopefully bring a smile to Danilo ;)
Re: Fake John Wick nes game on real nes
by on (#239542)
Sorry for the double post, I think I will ask the question here since it shows one part of the symptom in the screenshot.

For this quick project, the source uses more color than the nes can display at the same time. For this, I'm changing mid-frame the palette to try to reproduce as much as possible the same color that was used (if not, then the complete stage color needs to be adapted for the limitation).

It looks quite close to the original but you can see that artifacts are shown since I didn't time the code yet (it was a quick hack to make a video on the subject and all colors are changed 1 one shot at 2 points, status bar, 3 of them, and middle of screen, 4 of them).

When changing color mid-frame, it affect the address of the ppu since you change the address to the palette and you have to go back to the original address. I tried it in to change 1 color per hblank and it reduced the trailing artifacts but it caused the data shown the be not what I expected. I will need to take a screenshot later to show what I'm talking about but is it possible that if for 3 colors, 3 rasters, just resetting the address is maybe not enough? Is it possible the fine scroll must be re-adjusted, if not, it will not show what expected on that raster?
Re: Fake John Wick nes game on real nes
by on (#239543)
Like I always say, mid-screen palette changes are notoriously hard to pull off on the NES. You need at least one blank line when doing it, because one hblank simply doesn't give you enough time to 1) disable rendering; 2) set the palette address; 3) write the new color; 4) restore the scroll; 5) re-enable rendering. But even with rendering disabled, the PPU will render the color pointed by the VRAM address register, so in order to avoid visual glitches you have to change colors only during hblank, meaning you need several blank lines in order to update several colors. And during those blank lines, you can't display sprites either, which really limits where on the screen you can do it without disrupting gameplay.

And don't forget that disabling rendering mid-frame can corrupt OAM once rendering is eventually re-enabled (I personally can never remember the rules for avoiding this problem - what I remember is that you have to disable rendering as close to the end of the scanline as possible, but still before hblank starts).

I'm of the opinion that changing the palette mid-frame has waaaaaay too many catches to be feasible, so I basically don't consider this a viable technique on the NES. Whenever I'm designing something for the console, mid-frame palette changes are never on the table. I know that this doesn't help you with your project, so sorry for that, but I wanted to point out the specific reasons why this is hard to achieve so you can keep in mind all the issues you need to overcome when thinking of a solution.

BTW, have you tested this on hardware? Are the glitches the same or does something behave differently?
Re: Fake John Wick nes game on real nes
by on (#239544)
This project is a "holiday" to my current one I'm working on, allowing me to test and experiment things that I would usually not do with my current code base. I hope it will help me find new ideas to my current code block I have.

The goal is to try to get as close that I can from the original with the current assets and color is one of the many issue with it :lol: If I can't do it then:

- colors will have to be reduced in some way
- only one part of color may be updated (between status bar/ main game)
- leave the glitches as-is and say that was not possible without glitches

So success/failure is different in this project since it's a tentative re-creation of what Danilo's did. If it fails then oh well, it fails and will figure out something.

As for testing on hardware, I just made that quick port based on my current engine and it proved that it's possible to adapt it to something else, which is good, so I didn't test it yet. I guess I should try it soon.

As for the sprites, I wasn't aware of that so that may become a reason that I will have to drop it in the end, if it glitches too much :lol: For glitching OAM, wasn't aware of that too so I should be careful then.

Thanks for your feedback, that will help me on trying to port it as close as I can to the original material.
Re: Fake John Wick nes game on real nes
by on (#239546)
Banshaku wrote:
The goal is to try to get as close that I can from the original with the current assets and color is one of the many issue with it :lol:

I guess that the parallax in the buildings was the first thing to go!

Quote:
So success/failure is different in this project since it's a tentative re-creation of what Danilo's did. If it fails then oh well, it fails and will figure out something.

Well, he obviously didn't care for the actual limitations of the system when making that, he just went with the typical "NES feel" pixel artists seem to love so much (which is basically "4-color areas", but everything else is game), so it's no surprise that the conversion process isn't exactly going smoothly.

Quote:
As for testing on hardware, I just made that quick port based on my current engine and it proved that it's possible to adapt it to something else, which is good, so I didn't test it yet. I guess I should try it soon.

These things that mess with the PPU during rendering are always problematic, and emulated with varying levels of accuracy, so it's best to check on hardware ASAP so you don't end up making decisions based on misleading test results.

Quote:
As for the sprites, I wasn't aware of that so that may become a reason that I will have to drop it in the end, if it glitches too much :lol:

Yeah, the PPU is always processing sprites and backgrounds in parallel, and it evaluates sprites one scanline in advance, so if you interrupt that evaluation, at the very least you'll end up with no sprites for one scanline.

Quote:
For glitching OAM, wasn't aware of that too so I should be careful then.

The type of RAM used for OAM is DRAM (Dynamic RAM), which needs to be refreshed (read and written back) constantly or it loses its contents, and turning rendering off can interrupt the refresh process that the PPU is constantly doing, so when you enable rendering again this process resumes and may end up writing back values to the wrong memory positions. This is particularly nasty because it can even corrupt sprites on the *next* frame, if you happen to disable rendering near the end of the frame for some extra VRAM access time, even if you do an OAM DMA during vblank, since the corruption happens when rendering resumes, which's after the DMA, in the beginning of the next frame.

Anyway, feel free to keep trying to come up with something usable, I was just pointing some of the things you have to look out for.
Re: Fake John Wick nes game on real nes
by on (#241073)
Does this game work on Retropie?
Re: Fake John Wick nes game on real nes
by on (#241080)
Well, it's still a work in progress and there is a long way to go but it should work on any emulator that support mmc3 so my guess is yes, it should work on the retropie: I don't see why it shouldn't ;)
Re: Fake John Wick nes game on real nes
by on (#241108)
You can use the straight horizontal line of the barrier right below the buildings as an entire scanline for palette updates. Turn on the greyscale bit and all colour emphasis bits, and do the remainder of your palette updates. This at least masks the palette colours a little bit as they streak across the screen.
Re: Fake John Wick nes game on real nes
by on (#241124)
tokumaru wrote:
Like I always say, mid-screen palette changes are notoriously hard to pull off on the NES. You need at least one blank line when doing it, because one hblank simply doesn't give you enough time to 1) disable rendering; 2) set the palette address; 3) write the new color; 4) restore the scroll; 5) re-enable rendering. But even with rendering disabled, the PPU will render the color pointed by the VRAM address register, so in order to avoid visual glitches you have to change colors only during hblank, meaning you need several blank lines in order to update several colors. And during those blank lines, you can't display sprites either, which really limits where on the screen you can do it without disrupting gameplay.


In what ways would PPUADDR be disturbed between the end of one line's name table fetches and the start of the next line's? If background rendering is disabled, could one--near the end of a scan line--set PPUADDR to the pallete area, write one or two palette entries, and then store an arbitrary value to PPUADDR to kick it out of the palette area (with rendering disabled, it shouldn't matter what address one uses)? By my understanding, if background rendering is disabled and one does a sequence like:
Code:
    lda #$3F
    sta PPUADDR ; Store MSB at any time
    lda #paletteIndex (value $00-$1F)
    ldx color1
    ldy color2
    sta PPUADDR ; Write LSB and set address --must hit at hblank
    stx PPUDAT
    stx PPUDAT
    sta PPUADDR ; Store some arbitrary number as address MSB
    sta PPUADDR ; Write arbitrary address that isn't $3Fxx

sprites would be shown cleanly if PPUADDR is left holding any address that isn't of the form $3Fxx. Obviously PPUADDR would need to be loaded with a valid nametable address before background rendering is re-enabled. Am I missing anything?
Re: Fake John Wick nes game on real nes
by on (#241125)
During horizontal blanking, the PPU is fetching sprite tiles. To prevent the PPU from hogging the VRAM bus, you have to disable both background and sprite rendering.

In addition, your write sequence is 17 cycles from the last cycle of line 6 to the last cycle of line 10, and the window you have to fit them into is 64/3 = 21.3 cycles on NTSC or 64/3.2 = 20 cycles on PAL. The 6502 allows the current instruction to finish before servicing an interrupt, which can delay NMI and IRQ by up to 6 cycles.[1] So how are you planning to hit that window?


[1] Or 7 cycles for a few unofficial RMW+ALU opcodes.
Re: Fake John Wick nes game on real nes
by on (#241126)
tepples wrote:
During horizontal blanking, the PPU is fetching sprite tiles. To prevent the PPU from hogging the VRAM bus, you have to disable both background and sprite rendering.

In addition, your write sequence is 17 cycles from the last cycle of line 6 to the last cycle of line 10, and the window you have to fit them into is 64/3 = 21.3 cycles on NTSC or 64/3.2 = 20 cycles on PAL. The 6502 allows the current instruction to finish before servicing an interrupt, which can delay NMI and IRQ by up to 6 cycles.[1] So how are you planning to hit that window?


[1] Or 7 cycles for a few unofficial RMW+ALU opcodes.


By my understanding, PPUADDR is used as the external PPU bus address except when anything else needs to be placed upon it, which would suggest that the address multiplexer may be agnostic to operations by the CPU. Further, I don't think operations on the $3Fxx range trigger external PPU bus actions. I wouldn't be particularly surprised if they clobber any sprite access that's happening at that exact moment, but perhaps one could be okay if one didn't need all the sprites.

If the data bus is reliably either low or holding during a read of PPUADDR, then `INC PPUADDR` could save a cycle, setting PPUADDR to $0001 or $2021. As for hitting the timing window, ensure that the CPU is executing two-cycle instructions when the interrupt hits. If using DMC interrupts, one could have an interrupt set to fire 432 cycles before the interrupt one really wants and set the interrupt vector to a routine which sets the vector to the real routine, runs a loop for about 400 cycles, and then does a sequence of nops. If the real ISR can use a flag to insert or omit an extra delay cycle, I think total timing uncertainty can be reduced to three cycles. CPU-cycle waste could be minimized, if needed, by finding something useful for the first ISR to do before it enters its stream of NOPs.

PS--If I design an advanced mapper, one thing I'd like to do is make accesses to $FFFE-$FFFF map to a location which depends upon the three bottom bits of a timer, allowing an IRQ which is preceded by a nop slide to have a consistent precise 9-cycle delay. If palette effects could be achieved even using DMC interrupts, though, that would seem even better.
Re: Fake John Wick nes game on real nes
by on (#241128)
The PPU keeps its rendering pipeline active if either sprite or background rendering is on - disabling only one of them will prevent the respective pixels from being output, but the PPU will still be doing all the memory fetches underneath. If you want to touch VRAM during rendering, you have to stop the PPU completely.

As for the narrow window you have to hit in order to change colors during hblank, you may be able to take advantage of "increment 32" mode if any of the registers holds a value with bit 2 set - what you have to do is write the new colors to the last palette mirror ($3FE0-$3FFF) and set increment 32 mode (bit 2 of $2000) before writing the last color, so that PPUADDR gets kicked out of the palette area on that last write. This reduces the crucial cycle count to 13, plus the 7 cycles of possible interrupt latency, that's 20, exactly the maximum you can have on PAL.
Re: Fake John Wick nes game on real nes
by on (#241129)
supercat wrote:
By my understanding, PPUADDR is used as the external PPU bus address except when anything else needs to be placed upon it, which would suggest that the address multiplexer may be agnostic to operations by the CPU.
You've got the causality backwards. There is no concept of "anything else needs"; only "rendering" when it's completely busy and "idle" when the CPU can safely use the PPU's interface to access memory attached to the PPU.

Quote:
Further, I don't think operations on the $3Fxx range trigger external PPU bus actions.
It is impossible to write to palette RAM while rendering is enabled; the hardware inside the PPU that handles palette reads and writes cares about the output of the address bus multiplexer, and no part of the normal fetch cadence can ever place $3Fxx on the address bus.

Quote:
I wouldn't be particularly surprised if they clobber any sprite access that's happening at that exact moment, but perhaps one could be okay if one didn't need all the sprites.
If the CPU tries to write (or read) while rendering is enabled, there are two separate finite state machines inside that PPU that will run simultaneously. The multiplexed address/data bus signal (ALE) will be asserted both due to the normal PPU fetch cadence, as well as the "write" FSM, and the /RD and /WR signals will similarly be superimposed. Analog feedback paths will probably exist at this moment, as the PPU's multiplexed bus are outputs except while /RD is asserted. Writes during rendering will probably scribble over multiple memory locations. Reads during rendering will interfere with one or two of the fetches, producing one or two bad slivers (as well as bumping the scroll value)

None of that matters, because you can't address the palette memory while rendering is enabled anyway.

It's remotely conceivable that a perfectly timed raster effect might be able to disable rendering at the exact moment that the current sliver and nametable entry corresponds to the palette entry that one wants to update. This is an 7 pixel window, or 2 cycles, and it will correspond to the 3rd or 7th row of some tile in the bottom fifth of the lower-right nametable.

Quote:
If the data bus is reliably either low or holding during a read of PPUADDR, then `INC PPUADDR`
PPUADDR is a write-only register. An attempt to read from it will yield the special "PPU internal open bus". This value can be seeded by writing to PPUSTATUS.
Re: Fake John Wick nes game on real nes
by on (#241133)
lidnariq wrote:
It is impossible to write to palette RAM while rendering is enabled; the hardware inside the PPU that handles palette reads and writes cares about the output of the address bus multiplexer, and no part of the normal fetch cadence can ever place $3Fxx on the address bus.


Ah, that would be a problem. Grr... Maybe I'm spoiled by the Atari 2600's color capabilities.
Re: Fake John Wick nes game on real nes
by on (#241157)
Sorry for not answering sooner ^-^;;; I was busy on that business trip that I didn't have time to check the internet much these days.

@ccovel

That's an interesting trick ;) Right now, since I have been too busy these days, the current version removed completely the change of colors mid-screen and adapted the palette accordingly. One reason is that I don't have the hardware to test it efficiently and I was told that it was causing sprite artifacts on the real thing so I decided to go for a simpler version until I can finish it. I may try it again someday since I would love to keep as much as possible color fidelity but for now this is not on my list of priority.

Still, I appreciate everyone's answer and read all of them so if someday that would be possible to do it without any glitch in the sprites then I would be more than happy to try again.

For now, I need to finish my engine so that this project, and my currently secret one can move forward. Still, this small project was a good test to my current progress of the engine regarding how easy it is to adapt to another game so it give me hope that it will be useful to make other game, someday ^^;;