Something like this should work for Mesen:tokumaru wrote:It just occurred me that maybe a lua script can simulate this automatic switching in emulators... I might give this a try.
Code: Select all
local mirroringReg = 0xA000 local horizontalMirroring = 1 local verticalMirroring = 0 local lastAddr = 0 function switchNametables(addr) if (addr & 0x3FF) < 0x3C0 then --ignore attribute fetches if (lastAddr & 0x20) ~= (addr & 0x20) then if addr & 0x20 == 0 then emu.write(mirroringReg, horizontalMirroring, emu.memType.cpu) else emu.write(mirroringReg, verticalMirroring, emu.memType.cpu) end end lastAddr = addr end end emu.addMemoryCallback(switchNametables, emu.memCallbackType.ppuRead, 0x2000, 0x2FFF)
Very likely you'll have to have a time-compensating loop on each IRQ whose waiting time will be pre-calculated and variable each time, and you'll lose up to 25% of CPU time doing this.
I'm contemplating doing something I also consider crazy, but crazy at the software level, which makes me much more confident. I'll try dividing the rendering logic in my raycaster into fixed-time chunks of 4 or 8 scanlines (whatever works best), so I can alternate name tables in software in between logic chunks. Then, after the 3D view, comes the bottom of the screen with the status bar and such, which doesn't need timed code, so the game/object logic can run during that time.
Dividing the ray casting and texture scaling into constant-timed chunks sounds like a bit of a challenge, but they are extremely repetitive tasks, so it just might work. I do expect to lose some processing time simply making the best cases take as much time as the worst cases, but I'd also lose a lot of time with other software-only solutions, such as using DMC IRQs or generating all patterns on the fly.
You could also aim at constant-time rendering, so that the framerate even if slow will be constant. I think it's a good idea. At least you'll be really pushing the system to its limit.
If you needed to develop a new mapper anyway, you might as well have the mapper doing the ray-casting itself, so I agree it's a bad approach.
Oh, come on. That's wholly unfair. There's a huge difference between a trivial chunk of discrete logic doing something like the Oeka Kids mapper and a coprocessor.Bregalad wrote:If you needed to develop a new mapper anyway, you might as well have the mapper doing the ray-casting itself, so I agree it's a bad approach.
Well, it's just the rendering code, so I don't expect it to change at all once it's working and generating proper results.Bregalad wrote:Good luck having code that is even remotely maitainable...
The strategy I plan on using is basically to unroll the tasks and do as much as possible in 4 or 8 scanlines, and then decide whether to continue in the same block or jump to another one. For example, there'll be a block of code that just extends the ray and checks the map for collisions with walls, and this will be used over and over until a wall is found. If there's enough time to test, say, 6 wall boundaries between scroll splits, I'll have to include wait loops for when a wall is found before the 6 checks, so that the block still takes the same amount of time to finish. That could add up to a lot of lost CPU time, unfortunately.If you're already loosing a lot of times to render the screen, you don't want to loose any CPU time waiting for synchronization, even if it is just a little.
Not much worse than an Atari 2600 kernel, I suppose.but you want to make sure your rendering algorithm is set is stone and is not going to be changed once you start coding this... because maintaining it will be a complete nightmare.
With the different amounts of distances rays have to travel, the different heights of the walls, and the varying amounts of enemies and their proximity to the player, I think that'd be very hard, and wasteful. I have to believe that these things are going to compensate for each other (e.g. a longer ray that takes more time to cast will result in a smaller wall that takes less time to texture), and design the levels in ways that avoid too much heavy processing in the same spot.You could also aim at constant-time rendering, so that the framerate even if slow will be constant. I think it's a good idea. At least you'll be really pushing the system to its limit.
That doesn't sound particularly fun to me. I want to see the NES do all the work, not just have the PPU pump out the pixels.If you needed to develop a new mapper anyway, you might as well have the mapper doing the ray-casting itself, so I agree it's a bad approach.
Thanks for all the replies. I particularly liked lidnariq's idea of constructing a simple custom mapper, and I even learned a bit more about mapper design from it, so thanks for the suggestion!
I've been occasionally tempted to make a special emulator build that has all these wacky experimental hardware designs. To keep the scope sane, it'd have to be only hardware that uses a small handful of 74xx ICs.tokumaru wrote:I particularly liked lidnariq's idea of constructing a simple custom mapper, and I even learned a bit more about mapper design from it, so thanks for the suggestion!
What with Mesen having a useful debugger and running on Linux I might even get around to it.
I don't think having JIT-compiled mappers is very useful. Mapper development for emulators is easy enough by compiling offline. What lidnariq is proposing (being able to simulate the mapper hardware in an emulator using a predefined set of readily available hardware components) would provide a very simple transition path from simulation to hardware, maybe lowering barrier of entry for people who might be unsure what kind of mapper designs would be feasible on hardware.tepples wrote:The other option is to write an emulator that supports mappers written in WebAssembly or some other intermediate language that can be JIT-recompiled. Someone in the GBDev Discord server discussed making an emulator that supports exactly this.