// pad.h // Game controllers for the Voyager. // Written by Anachronaut #ifndef PAD_H #define PAD_H #include #include // ---- What a pad is, and why it is not the console ---- // // The console says WHICH KEY WENT DOWN. That is the right shape for typing and the wrong one // for playing: a game wants to know what is being held, this frame, possibly several things // at once, and a stream of presses cannot say that. A key that is down and staying down sends // nothing at all. // // So a pad reports a LEVEL rather than an event. One read gives the state of every button at // once, holding is the natural thing to express, and two directions at the same time costs // nothing. Reading it does not consume it: a game may ask twice in a frame and get the same // answer both times, which an event queue cannot promise. // // IT IS NOT AN EXTENSION OF THE CONSOLE, and that is deliberate. A terminal hands over // characters and cannot report a key coming up however it is asked, so key-up on the console // would have been a thing that worked behind a window and silently did not down a wire - and // "the same program behaves the same everywhere" is worth more than the convenience. A // separate device can honestly say it is not there. #define PORT_PAD 0x60 #define PORT_PAD_TOP 0x6F // Four, because a party is four. They cost a port each and nothing at all when unused. #define PAD_COUNT 4 // ---- The buttons, in one byte ---- // // The four directions in the low nibble, so "which way" is an AND with 0x0F and needs no // shifting. The four buttons in the high nibble for the same reason. #define PAD_RIGHT 0x01 #define PAD_LEFT 0x02 #define PAD_DOWN 0x04 #define PAD_UP 0x08 #define PAD_A 0x10 #define PAD_B 0x20 #define PAD_START 0x40 #define PAD_SELECT 0x80 // ---- Which of them are there ---- // // One bit a pad, so a game can say "this wants a controller" rather than sitting silent while // somebody presses things at it. A pad that is not there reads as nothing held, which is the // same as a pad nobody is touching - the difference matters only to whoever wants to explain // it, and that is exactly who this port is for. #define PAD_PRESENT 0x64 // Ports 0x65 to 0x6F are reserved. Analogue axes are the thing they are being kept for: a // paddle is two more bytes a pad and an argument about deadzones, and nothing wants one yet. void padReset(void); // ---- Recorded input, which is what makes this testable ---- // // Voyager reads a real pad, and its tests run headless with no window and no hands. Without a // recorded path this whole device would be exercised only by somebody playing, which is the // state the console's line editing was in when it broke twice in two days. // // A file is one byte a FRAME, not one byte a read. A pad is a level: a game that asks twice // in one frame has to be told the same thing both times, and a file that advanced per read // would answer differently depending on how the game was written. void padFromFile(int which, FILE *file); // A live pad, set by whatever is watching real hardware. Ignored for a pad that has a file, // so a recording always wins over whatever somebody happens to be holding. void padSet(int which, uint8_t held); void padTick(unsigned long now); uint8_t padRead(uint8_t port); #endif // PAD_H