NES to USB Keyboard interface?
-
bitcores
- Posts: 65
- Joined: Thu Apr 20, 2023 3:09 am
- Location: Japan
Re: NES to USB Keyboard interface?
So apparently I had neglected to handle the modifier keys on my interface, that has been addressed, and now you can actually use shift+; to get a colon in Keyboard Mouse Host mode (in Family Basic Keyboard mode colon is bound to apostrophe).
And it is now possible to run the Test Program from the Apple 1 manual (with the memory location of the ECHO subroutine modified) though it doesn't maintain alignment. Please note that backspace does not move the "cursor" backwards, it will insert an underscore that indicates one previous character will be ignored. There is a limit of 30 characters to a line before it auto-escapes, so you have to break up longer store inputs (every 8 bytes is best).
And it is now possible to run the Test Program from the Apple 1 manual (with the memory location of the ECHO subroutine modified) though it doesn't maintain alignment. Please note that backspace does not move the "cursor" backwards, it will insert an underscore that indicates one previous character will be ignored. There is a limit of 30 characters to a line before it auto-escapes, so you have to break up longer store inputs (every 8 bytes is best).
You do not have the required permissions to view the files attached to this post.
-
TakuikaNinja
- Posts: 427
- Joined: Mon Jan 09, 2023 6:42 pm
- Location: New Zealand
Re: NES to USB Keyboard interface?
Yes, that was me. I was initially going to port wozmon to NES/FC & FDS myself but then I remembered/rediscovered your mapper 218 port. For the record, these were the primary fixes I made in my fork:bitcores wrote: Sun Jan 05, 2025 5:33 pm Actually, I just noticed that someone forked it a couple weeks ago and fixed that along with other things I was doing wrong.
I'm going to merge what I can.
- Always perform OAM DMA - Not only does this fix the PPU OAM decay issue (as Pokun mentioned), it also allows for writes to the $0200 page to be reflected in OAM. (I figured it would be nice to be able to manipulate sprites)
- Use a flag in memory for vblank detection - The output was occasionally dropping characters due to the race condition regarding PPUSTATUS bit 7 reads. This change also allows the NMI handler to bail on lag frames.
- Move controller reads to the NMI handler - I personally prefer to read the controller in the NMI handler (and ideally skip it on lag frames). (I originally made a claim regarding reads in main v.s. interrupts here, but its accuracy was pointed out to be quite dubious.)
- Init palette RAM within vblank - Setting PPUADDR to the palette RAM area causes the entry value to be displayed on screen (even when rendering is off), which caused stripes to appear on reset. Doing it in vblank hides this.
- Alter text display position & scroll behaviour - The original positioning of the text display placed the input buffer at the bottom of the screen (i.e. in overscan), so I had to move it up to be visible more often on CRT displays & AV capture devices. I also centred the display horizontally and made the scroll behave more similarly to the original terminal as a bonus.
The only suggestion I'd like to make regarding your current version is that the NES2.0 default expansion device could be specified in the header to use the Family BASIC Keyboard. That way, emulators can automatically pick the required keyboard input.
-
bitcores
- Posts: 65
- Joined: Thu Apr 20, 2023 3:09 am
- Location: Japan
Re: NES to USB Keyboard interface?
That is a great idea. I just had to spend the last day working out why it was causing nametable corruption if I did it at the beginning of the NMI (turned out I was crossing the 2273cy limit for PPU access during NMI, but I've solved that now).TakuikaNinja wrote: Tue Jan 07, 2025 3:55 am Always perform OAM DMA - Not only does this fix the PPU OAM decay issue (as Pokun mentioned), it also allows for writes to the $0200 page to be reflected in OAM. (I figured it would be nice to be able to manipulate sprites)
I never did work out why it was dropping those characters. This is good to know.TakuikaNinja wrote: Tue Jan 07, 2025 3:55 am Use a flag in memory for vblank detection - The output was occasionally dropping characters due to the race condition regarding PPUSTATUS bit 7 reads. This change also allows the NMI handler to bail on lag frames.
Another good idea.TakuikaNinja wrote: Tue Jan 07, 2025 3:55 am The only suggestion I'd like to make regarding your current version is that the NES2.0 default expansion device could be specified in the header to use the Family BASIC Keyboard. That way, emulators can automatically pick the required keyboard input.
-
Pokun
- Posts: 3442
- Joined: Tue May 28, 2013 5:49 am
- Location: Hokkaido, Japan
Re: NES to USB Keyboard interface?
Those are all basically necessary things in about any NES program, except that one about reading the controller in NMI, that's just a personal preference.
I've heard good reasons against reading the controller in the NMI. If two NMIs occurs within a main loop (a lag frame) it would be processing two sets of inputs from different frames which may cause strange problems. But if skipping controller reading on lag frames as TakuikaNinja mentioned it should solve the problem, in that case it wouldn't matter if reading in controllers in NMI or in the main loop.
I've heard good reasons against reading the controller in the NMI. If two NMIs occurs within a main loop (a lag frame) it would be processing two sets of inputs from different frames which may cause strange problems. But if skipping controller reading on lag frames as TakuikaNinja mentioned it should solve the problem, in that case it wouldn't matter if reading in controllers in NMI or in the main loop.
-
Fiskbit
- Site Admin
- Posts: 1380
- Joined: Sat Nov 18, 2017 9:15 pm
Re: NES to USB Keyboard interface?
I don't want to get on too much of a tangent here, but I talked with TakuikaNinja about this on Discord and our conclusion is that contemporary software might be more mixed regarding when joypads are read than he had thought (but we're not sure). Reading them in the gameplay loop and NMI are effectively equivalent as long as you protect your NMI against lag frames, which you should be doing anyway because otherwise you can have corruption or even crashes from emptying an incomplete VRAM transfer buffer or doing OAM DMA on an incomplete OAM buffer. Since you should have that protection anyway, it doesn't matter where it goes. Reading input on lag frames is bad for the reason Pokun said (input can change anytime during the gameplay frame, breaking assumptions).
There are two potential benefits to reading inputs in the NMI. A minor one is that it might allow an emulator's default lag frame detection to work better, giving you a more accurate count (but if you care about this, I think there's usually a Lua hook for this to tailor it to your target game). The more significant one is that it allows you to do DMC-safe controller reads in a way that is faster than reading input repeatedly, compatible with more controllers, and avoids corrupting sample data (such as with bus conflicts).
There are two potential benefits to reading inputs in the NMI. A minor one is that it might allow an emulator's default lag frame detection to work better, giving you a more accurate count (but if you care about this, I think there's usually a Lua hook for this to tailor it to your target game). The more significant one is that it allows you to do DMC-safe controller reads in a way that is faster than reading input repeatedly, compatible with more controllers, and avoids corrupting sample data (such as with bus conflicts).
-
Pokun
- Posts: 3442
- Joined: Tue May 28, 2013 5:49 am
- Location: Hokkaido, Japan
Re: NES to USB Keyboard interface?
Yeah basically what I was trying to say.
I just want to mention that while rendering updates and controller reading in NMI should both be skipped on lag frames, if you have sound routines running in NMI (which is a good idea to get consistent 60/50 Hz music tempo) you may actually want them to run even on lag frames, or else the music will suffer from the slowdown. So you may not want to protect the whole NMI handler from lag frames, only rendering and controller reading.
I just want to mention that while rendering updates and controller reading in NMI should both be skipped on lag frames, if you have sound routines running in NMI (which is a good idea to get consistent 60/50 Hz music tempo) you may actually want them to run even on lag frames, or else the music will suffer from the slowdown. So you may not want to protect the whole NMI handler from lag frames, only rendering and controller reading.
-
bitcores
- Posts: 65
- Joined: Thu Apr 20, 2023 3:09 am
- Location: Japan
Re: NES to USB Keyboard interface?
I don't think this is too off tangent because I'm really hoping that the keyboard.s I'm building could be used as near drop-in keyboard functionality for the Keyboard Mouse Host interfaces, so it's important that I'm getting best practices and behavior right.
Part of the problem, though, is that I am still relatively inexperienced with NES programming and have mostly gotten things working so far by messing until it works, being focused more on not-games I was neglecting things that I thought were unimportant for these text based applications. And I was too focused on trying to revert to the original wozmon behavior that I wasn't taking into account platform differences.
In this case, because there is a buffer of inputs from the keyboard, having two NMI occurring before the program reads out of the buffer shouldn't be a problem because the subroutine will either find there are still keys in the buffer and skip the keyboard polling or the buffer is empty and it needs to poll the keyboard anyway.
This isn't the best behavior for a low latency environment, like a game, but I think it is more reasonable for something that is dealing with text, you would rather a key come in a couple of frames late than not at all, though under the Family Basic Keyboard handling this could still occur (but would require single frame key strokes).
I have yet to deal with the APU in any application, but again seeing how with nesmon you could write to the registers to generate sound or write a small program in memory that will output sound (which I didn't even consider, like the sprites) I want to get that right.
And it seems the nesmon name was already taken, so I should think about renaming my application.
Part of the problem, though, is that I am still relatively inexperienced with NES programming and have mostly gotten things working so far by messing until it works, being focused more on not-games I was neglecting things that I thought were unimportant for these text based applications. And I was too focused on trying to revert to the original wozmon behavior that I wasn't taking into account platform differences.
In this case, because there is a buffer of inputs from the keyboard, having two NMI occurring before the program reads out of the buffer shouldn't be a problem because the subroutine will either find there are still keys in the buffer and skip the keyboard polling or the buffer is empty and it needs to poll the keyboard anyway.
This isn't the best behavior for a low latency environment, like a game, but I think it is more reasonable for something that is dealing with text, you would rather a key come in a couple of frames late than not at all, though under the Family Basic Keyboard handling this could still occur (but would require single frame key strokes).
I have yet to deal with the APU in any application, but again seeing how with nesmon you could write to the registers to generate sound or write a small program in memory that will output sound (which I didn't even consider, like the sprites) I want to get that right.
And it seems the nesmon name was already taken, so I should think about renaming my application.
-
freem
- Posts: 184
- Joined: Mon Oct 01, 2012 3:47 pm
- Location: freemland (NTSC-U)
Re: NES to USB Keyboard interface?
If you're referring to https://github.com/freem/nesmon, don't worry about it. I never really finished it, so the actual working product (i.e. yours) should take priority. (If you're not referring to that repo, please disregard this post.)bitcores wrote: Fri Jan 10, 2025 11:24 pmAnd it seems the nesmon name was already taken, so I should think about renaming my application.
-
Pokun
- Posts: 3442
- Joined: Tue May 28, 2013 5:49 am
- Location: Hokkaido, Japan
Re: NES to USB Keyboard interface?
Famimon is the other obvious choice. 
BTW, is it possible for the user to write his own NMI and IRQ handlers and map them in, replacing Nesmon's ISRs? So the user gets full control over the hardware I mean, in that case the user could write his own sound driver in NMI or do whatever.
I don't think there is any need for Nesmon to touch the APU unless you have beeps and stuff you want to play back.
BTW, is it possible for the user to write his own NMI and IRQ handlers and map them in, replacing Nesmon's ISRs? So the user gets full control over the hardware I mean, in that case the user could write his own sound driver in NMI or do whatever.
I don't think there is any need for Nesmon to touch the APU unless you have beeps and stuff you want to play back.
-
TakuikaNinja
- Posts: 427
- Joined: Mon Jan 09, 2023 6:42 pm
- Location: New Zealand
Re: NES to USB Keyboard interface?
With the current mapper configuration placing the interrupt vectors in PRG-ROM, remapping them at runtime would require forwarding the interrupt vectors to RAM. The ISR in PRG-ROM could then just be a JMP (Indirect) instruction with the appropriate pointer.
-
bitcores
- Posts: 65
- Joined: Thu Apr 20, 2023 3:09 am
- Location: Japan
Re: NES to USB Keyboard interface?
This is actually possible with my single chip cartridge if it is built with writable rom enabled. You can write to the ROM space on the console and the changes persist until the cartridge is reset. Here's a short demonstration video (capture) where I replace the reset vector with the position of the NESMON label. https://www.youtube.com/watch?v=4pDfIkOTCB4Pokun wrote: Sat Jan 11, 2025 5:42 pm BTW, is it possible for the user to write his own NMI and IRQ handlers and map them in, replacing Nesmon's ISRs? So the user gets full control over the hardware I mean, in that case the user could write his own sound driver in NMI or do whatever.
I don't think there is any need for Nesmon to touch the APU unless you have beeps and stuff you want to play back.
Of course, writing directly into memory is cumbersome but it could be a cool party trick to hand craft an NMI handler in RAM and then redirect the ISR to it.
-
Pokun
- Posts: 3442
- Joined: Tue May 28, 2013 5:49 am
- Location: Hokkaido, Japan
Re: NES to USB Keyboard interface?
Writable ROM? Is it flash-ROM or some other type of PROM?
Letting a pointer in RAM decide where the ISR goes like TakuikaNinja suggested is maybe the easiest solution.
Letting a pointer in RAM decide where the ISR goes like TakuikaNinja suggested is maybe the easiest solution.
-
bitcores
- Posts: 65
- Joined: Thu Apr 20, 2023 3:09 am
- Location: Japan
Re: NES to USB Keyboard interface?
My single chip cartridges use a Raspberry Pi Pico to emulate a ROM chip (and even PRG-RAM concurrently).Pokun wrote: Sun Jan 12, 2025 9:45 am Writable ROM? Is it flash-ROM or some other type of PROM?
Letting a pointer in RAM decide where the ISR goes like TakuikaNinja suggested is maybe the easiest solution.
As for remapping the ISR to RAM, I'm not entirely sure how you would go about that. The first thing that comes to my mind is using a pre-filled "battery backed" RAM, like 7FF0-7FFF and then pointing the ROMs ISR there.
Or just map the NMI and IRQ interrupts to subroutines that will use memory addresses to jump, and use the RESET to set up the contents of those memory addresses on startup.
I'm open to any of these ideas, feel free to write up details in an issue ticket or fork the project and submit a pull request with the changes.
-
Pokun
- Posts: 3442
- Joined: Tue May 28, 2013 5:49 am
- Location: Hokkaido, Japan
Re: NES to USB Keyboard interface?
Oh I see so the ROM space is technically RAM, like on the Everdrive.
I guess the pointer method would work something like this:
So somewhere in the init code you set the NMI and IRQ pointers to their respective default values which is where you have the ISRs in ROM.
This way the user can just modify nmi_pointer and irq_pointer respectively in order to make them point to wherever the user wants them to jump to when an interrupt happens, which would be somewhere in RAM where the user has made his ISRs.
I guess the pointer method would work something like this:
Code: Select all
RAM:
nmi_pointer: .equ $0010 ;pointer to NMI ISR, 2 byte
irq_pointer: .equ $0012 ;pointer to IRQ ISR, 2 byte
ROM:
RESET:
;...
lda #<nmi_isr
sta nmi_pointer+0
lda #>nmi_isr
sta nmi_pointer+1 ;store default NMI address in NMI pointer
lda #<irq_isr
sta irq_pointer+0
lda #>irq_isr
sta irq_pointer+1 ;store default IRQ address in IRQ pointer
;...
main: ;main program loop
;...
jmp main
NMI:
jmp (nmi_pointer)
nmi_isr:
;...
rti
IRQ:
jmp (irq_pointer)
irq_isr:
;...
rti
;...
;Interrupt vector table:
.word NMI
.word RESET
.word IRQ
This way the user can just modify nmi_pointer and irq_pointer respectively in order to make them point to wherever the user wants them to jump to when an interrupt happens, which would be somewhere in RAM where the user has made his ISRs.
-
TakuikaNinja
- Posts: 427
- Joined: Mon Jan 09, 2023 6:42 pm
- Location: New Zealand
Re: NES to USB Keyboard interface?
It should be noted that modifying ISR pointers at runtime, especially while NMI/IRQ sources are enabled, is prone to race conditions where an interrupt occurs before both bytes of the pointer have been written to. This is probably part of why the FDS BIOS opted for a vector selection system using the upper 2 bits of a separate variable (checking with BIT on entry to preserve registers). Taking the NMI as an example, it uses the value at $0100 to select between 1 BIOS ISR and 3 disk game ISRs (4 total). The disk game ISRs that are not selected can be freely modified before being selected using the variable. Just some food for thought.