diff --git a/.gitignore b/.gitignore index a99baa03..02b2d853 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ bin/th* bin/TH* bin/zuncom bin/ZUNCOM +Research/*.exe +Research/*.obj +RESEARCH/*.EXE +RESEARCH/*.OBJ diff --git a/Research/HOLDKEY.C b/Research/HOLDKEY.C new file mode 100644 index 00000000..38d13e76 --- /dev/null +++ b/Research/HOLDKEY.C @@ -0,0 +1,97 @@ +/* ReC98 Research + * -------------- + * PC-98 keyboard key hold behavior test + */ + +#pragma option -ms + +#include "../libs/master.lib/master.h" + +#if defined(__TURBOC__) && defined(__MSDOS__) +// Remove C++ exception handler bloat on Borland compilers +// (see https://community.embarcadero.com/article/technical-articles/162/14700) +void _ExceptInit(void) +{ +} +#endif + +// Should cover the number of possible input states within a frame +// ⌈(19200 baud / 11 bit) / 56.4 fps⌉ ≈ 31, but let's rather be extra safe... +#define STATES_MAX 64 + +void hr(int len) +{ + int i; + dos_puts("\r\n"); + for(i = 0; i < len; i++) { + dos_puts("\x86\x44"); + } + dos_puts("\r\n"); +} + +void frame(void) +{ + char states[STATES_MAX]; + char state_prev = 0; + unsigned int states_recorded = 0; + unsigned int i = 0; + int flipped = 0; + + vsync_Count1 = 0; + do { + int delayloop = 1024; + unsigned char input = *(volatile unsigned char far*)(0x531); + states[states_recorded++] = (input >> 2) & 0xF; + + while(delayloop && vsync_Count1 < 1) { + _asm out 0x5F, AL; + delayloop--; + } + } while(vsync_Count1 < 1); + + dos_puts("\r \r"); + for(i = 0; i < states_recorded; i++) { + char state = states[i]; + + if(state_prev != state && i > 0) { + flipped++; + dos_puts("\x1B[17m"); + } else { + dos_puts("\x1B[0m"); + } + + dos_putch((state < 10) ? ('0' + state) : ('A' - 10 + state)); + dos_putch(' '); + state_prev = state; + } + if(flipped >= 2) { + dos_puts("\r\n"); + } +} + +void main(void) +{ + vsync_start(); + hr(79); + dos_puts( + "Hold any of the cursor keys. The line below will show all states of the cursor\r\n" + "keys, read using the keyboard state memory bitmap at 0x52A (which is also how\r\n" + "TH02 and later read input), within a single frame.\r\n" + "The 0.6ms delay between the states roughly matches the keyboard UART delay.\r\n" + "\r\n" + "After the typematic delay, you might see the occasional wrong state popping up.\r\n" + "This will be highlighted in\x1B[17m red \x1B[0mand kept on screen when it happens.\r\n" + "Unlike on IBM PC/XT/AT where holding a key repeats only the down scancode,\r\r\n" + "PC-98 keyboards repeat up AND down scancodes, leading to these state flips.\r\n" + "So, polling the keyboard twice within a frame, ~0.6ms apart, is indeed a good\r\n" + "idea for a game engine, which is why ZUN chose to do it from TH03 onwards.\r\n" + "\r\n" + "Hold Q to quit." + ); + hr(79); + while(!(*(volatile char far*)(0x52C) & 0x01)) { + frame(); + } + hr(79); + vsync_end(); +} diff --git a/Research/Makefile.mak b/Research/Makefile.mak new file mode 100644 index 00000000..2b391065 --- /dev/null +++ b/Research/Makefile.mak @@ -0,0 +1,12 @@ +# ReC98 Research +# -------------- +# Makefile + +.autodepend + +LFLAGS = -L..\bin\ + +all: HOLDKEY.EXE + +HOLDKEY.EXE: HOLDKEY.OBJ + $(CC) $(LFLAGS) masters.lib $** diff --git a/Research/README.md b/Research/README.md new file mode 100644 index 00000000..7fa6f54f --- /dev/null +++ b/Research/README.md @@ -0,0 +1,6 @@ +This directory collects various pieces of research into PC-98 hardware details +that were made during the reverse-engineering phase, in order for us to better +understand certain details and name functions/data more adequately. + +Run `maker` to build the examples. The executables will be directly placed +into this directory. diff --git a/th03/hardware/input_sense.asm b/th03/hardware/input_sense.asm index b89b95a3..a993e754 100644 --- a/th03/hardware/input_sense.asm +++ b/th03/hardware/input_sense.asm @@ -1,5 +1,12 @@ ; Basic keyboard input function in this game, resets and updates all three ; variables according to the keyboard state. +; +; The key state is checked twice, 614.4 µs apart, to ignore the momentary "key +; released" events sent by PC-98 keyboards at the typematic rate if a key is +; held down. This ensures that the game consistently sees that specific input +; being pressed. See the HOLDKEY example in the Research/ subdirectory for +; more explanation and sample code showing off this effect. +; ; void input_reset_sense_key_held(); _input_reset_sense_key_held proc far xor ax, ax diff --git a/th05/hardware/input_held.asm b/th05/hardware/input_held.asm index 2b8640f3..a42dc3d8 100644 --- a/th05/hardware/input_held.asm +++ b/th05/hardware/input_held.asm @@ -1,3 +1,9 @@ +; The key state is checked twice, 614.4 µs apart, to ignore the momentary "key +; released" events sent by PC-98 keyboards at the typematic rate if a key is +; held down. This ensures that the game consistently sees that specific input +; being pressed. See the HOLDKEY example in the Research/ subdirectory for +; more explanation and sample code showing off this effect. +; ; int input_reset_sense_held(); _input_reset_sense_held proc far call _input_reset_sense