Coder Social home page Coder Social logo

Comments (8)

asiekierka avatar asiekierka commented on June 1, 2024 1

Here's some loose thoughts:

The typical shadowing routine for a MMIO register on a one-register mapper (UNROM, CNROM, etc.) would be (written with ASM this time):

tax
sta _BANK_SHADOW
sta __rom_poke_table, x

In this situation, there's only one point during which an NMI can fire where the shadowed register no longer matches the real register: the write between _BANK_SHADOW and rom_poke_table.

The easy way to resolve that is to always write the shadow to the ROM at the beginning of NMI:

lda _BANK_SHADOW
tax
sta __rom_poke_table, x

This has the nice property of unlocking two new functionalities:

  • the ability to write to the ROM without writing to _BANK_SHADOW, creating a bank change that will automatically undo itself at NMI, but invalidating getters,
  • the ability to write to _BANK_SHADOW without writing to the ROM, creating a delayed bank change that will only apply at NMI.

Combined with re-applying _BANK_SHADOW at the beginning of NMI, this allows for "change CHR bank until vertical blank" handlers, as well as "change CHR bank at vertical blank" handlers - both very useful constructs! To borrow terms from the MMC1 bank implementation, I'm going to refer to the former as a "split".

Re-applying _BANK_SHADOW at the beginning of NMI also means that getters and setters will work correctly inside an NMI. The only case where they won't work correctly is "splits" - this should be appropriately documented; however, CHR banks don't tend to be paged in/out the same way a PRG bank would across calls, which makes me believe this won't be much of a problem in real-world use.

from llvm-mos-sdk.

asiekierka avatar asiekierka commented on June 1, 2024

This will work fine enough for those discrete mappers whose register corresponds to only one value (CNROM, UNROM). For UNROM-512, though, one needs to rethink it: we don't want a situation in which, say, writing _BANK_SHADOW to the register on set_prg_bank also changes the CHR bank.

from llvm-mos-sdk.

mysterymath avatar mysterymath commented on June 1, 2024

More broadly this issue also should include IRQ handlers; I'm not sure what the current scope of behaviors are, but this case is considerably easier to handle, since interrupt state can be pushed/popped around critical sections and interrupts disabled to keep shadows and the MMIO registers observably identical.

from llvm-mos-sdk.

mysterymath avatar mysterymath commented on June 1, 2024

More broadly, from discussion in #198, if we can't identify a need for CHR shadowing, and the cost of keeping it consistent against NMI and IRQ is too high, it may be a good idea to either withdraw such functionality or mark it as possibly stale in the case of interrupts.

from llvm-mos-sdk.

mysterymath avatar mysterymath commented on June 1, 2024

I'm might be being too persnickety about IRQs; we can wrap various routines in PHP; SEI; ... ;PLP if we want things to always work, but that imposes a 9 cycle and 3 byte cost to essentially every banking function that doesn't use the even more expensive retry approach I picked for MMC1 and MMC3. I'd leave it open to someone more experienced to the Nesdev community whether this is worth it.

from llvm-mos-sdk.

asiekierka avatar asiekierka commented on June 1, 2024

I think bank changing is sufficiently uncommon for this 9 cycle/3 byte cost to be acceptable. Worst-case, set/swap/split_chr_bank_unsafe could be added which pushes the interrupt wrapping and other potential safety mechanisms on the end user.

However, I'm not experienced at NES development myself, either.

from llvm-mos-sdk.

asiekierka avatar asiekierka commented on June 1, 2024

I think it might be worth re-opening this issue (or creating a new one), as while #198 fixes the much more pressing NMI part of the issue, it does nothing about IRQs.

from llvm-mos-sdk.

mysterymath avatar mysterymath commented on June 1, 2024

Closing this one for now; I had become sufficiently far removed from the issue that I had forgotten how special handling of interrupts in C was in llvm-mos. None of the SDK banking functions are annotated with the relevant interrupt attributes, so none of them are safe to call from an IRQ or NMI. Even if they were, they'd still need the AXY calling convention to be practical. So, there may actually be too much logic in these functions to make them reentrant, at least in how they're legal to be called today (a descendant call of _start).

from llvm-mos-sdk.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.