├── firmware ├── build_opt.h ├── .clang-format ├── keymap.h ├── keyboardinterface.h ├── firmware.ino ├── hidlcd.h └── rawkeyboard.h ├── client ├── sample.png ├── print.sh ├── Makefile └── main.cc ├── binaries ├── keyboard-rom.img └── keyboard-rom.decompilation.txt ├── LICENSE ├── .clang-format ├── README.md └── KeyboardProtocol.md /firmware/build_opt.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /firmware/.clang-format: -------------------------------------------------------------------------------- 1 | ../.clang-format -------------------------------------------------------------------------------- /client/sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgiven/ibm6770keyboard/HEAD/client/sample.png -------------------------------------------------------------------------------- /binaries/keyboard-rom.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgiven/ibm6770keyboard/HEAD/binaries/keyboard-rom.img -------------------------------------------------------------------------------- /client/print.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | pbmtext -width=480 "$*" | pamcut -pad -width 480 -height 32 | pamtopng | sudo ./kbdtool /dev/stdin 3 | -------------------------------------------------------------------------------- /client/Makefile: -------------------------------------------------------------------------------- 1 | kbdtool: main.cc Makefile 2 | g++ -g -o $@ main.cc $$(pkg-config --libs --cflags hidapi-hidraw stb) 3 | 4 | clean: 5 | rm -f kbdtool 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 David Given 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: -4 3 | AlignAfterOpenBracket: DontAlign 4 | AlignArrayOfStructures: Left 5 | AlignEscapedNewlines: Left 6 | AllowAllArgumentsOnNextLine: 'true' 7 | AllowAllConstructorInitializersOnNextLine: 'false' 8 | AllowAllParametersOfDeclarationOnNextLine: 'true' 9 | AllowShortBlocksOnASingleLine: 'true' 10 | AllowShortCaseLabelsOnASingleLine: 'false' 11 | AllowShortFunctionsOnASingleLine: Empty 12 | AllowShortIfStatementsOnASingleLine: Never 13 | AllowShortLambdasOnASingleLine: None 14 | AllowShortLoopsOnASingleLine: 'false' 15 | AlwaysBreakAfterDefinitionReturnType: None 16 | AlwaysBreakAfterReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: 'true' 18 | AlwaysBreakTemplateDeclarations: 'Yes' 19 | BinPackArguments: 'false' 20 | BinPackParameters: 'false' 21 | BreakBeforeBraces: Allman 22 | BreakConstructorInitializers: 'AfterColon' 23 | BreakInheritanceList: AfterColon 24 | BreakStringLiterals: 'true' 25 | ColumnLimit: '80' 26 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' 27 | FixNamespaceComments: 'false' 28 | IncludeBlocks: Preserve 29 | IndentCaseLabels: 'true' 30 | IndentWidth: '4' 31 | IndentWrappedFunctionNames: 'false' 32 | KeepEmptyLinesAtTheStartOfBlocks: 'true' 33 | NamespaceIndentation: All 34 | PointerAlignment: Left 35 | ReflowComments: 'true' 36 | SortIncludes: 'false' 37 | SortUsingDeclarations: 'true' 38 | SpaceAfterTemplateKeyword: 'true' 39 | SpaceBeforeAssignmentOperators: 'true' 40 | SpaceBeforeCtorInitializerColon: 'false' 41 | SpaceBeforeInheritanceColon: 'true' 42 | SpaceBeforeParens: ControlStatements 43 | SpaceInEmptyParentheses: 'false' 44 | ... 45 | -------------------------------------------------------------------------------- /client/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static constexpr uint8_t DISPLAY_ATTRIBUTES_REPORT_ID = 21; 9 | static constexpr uint8_t BLIT_REPORT_ID = 22; 10 | 11 | typedef struct 12 | { 13 | uint8_t reportId; 14 | uint16_t width; 15 | uint16_t height; 16 | uint16_t depth; 17 | } __attribute__((__packed__)) DisplayAttributesReport; 18 | 19 | typedef struct 20 | { 21 | uint8_t reportId; 22 | uint16_t x1, y1; 23 | uint16_t x2, y2; 24 | uint8_t data[50]; 25 | } __attribute__((__packed__)) BlitReport; 26 | 27 | static void syntaxError() 28 | { 29 | fprintf(stderr, "Syntax error\n"); 30 | exit(1); 31 | } 32 | 33 | int main(int argc, char* argv[]) 34 | { 35 | hid_init(); 36 | 37 | hid_device* dev = hid_open(0x1209, 0x6e01, nullptr); 38 | if (!dev) 39 | { 40 | printf("Unable to open device\n"); 41 | hid_exit(); 42 | return 1; 43 | } 44 | 45 | DisplayAttributesReport report = {DISPLAY_ATTRIBUTES_REPORT_ID}; 46 | if (hid_get_feature_report(dev, (uint8_t*)&report, sizeof(report)) < 0) 47 | { 48 | perror("failed to query device"); 49 | exit(1); 50 | } 51 | 52 | if (argc != 2) 53 | syntaxError(); 54 | 55 | if ((argv[1] == std::string("-q"))) 56 | { 57 | printf("Size: %d x %d, depth: %d\n", 58 | report.width + 1, 59 | report.height + 1, 60 | report.depth); 61 | exit(0); 62 | } 63 | 64 | int w, h, n; 65 | uint8_t* imageData = stbi_load(argv[1], &w, &h, &n, 1); 66 | if (!imageData) 67 | perror("cannot open file"); 68 | 69 | if ((w != (report.width + 1)) || (h != (report.height + 1))) 70 | { 71 | fprintf(stderr, 72 | "Image must be %d x %d\n", 73 | report.width + 1, 74 | report.height + 1); 75 | exit(1); 76 | } 77 | 78 | for (int x = 0; x < w; x += 8) 79 | { 80 | BlitReport br = { 81 | BLIT_REPORT_ID, (uint16_t)x, 0, (uint16_t)(x + 8), (uint16_t)h}; 82 | for (int y = 0; y < h; y++) 83 | { 84 | uint8_t* ptr = &imageData[x + y * w]; 85 | br.data[y] = (!ptr[0] << 7) | (!ptr[1] << 6) | (!ptr[2] << 5) | 86 | (!ptr[3] << 4) | (!ptr[4] << 3) | (!ptr[5] << 2) | 87 | (!ptr[6] << 1) | (!ptr[7] << 0); 88 | } 89 | 90 | for (;;) 91 | { 92 | int r = hid_write(dev, (uint8_t*)&br, sizeof(br)); 93 | if (r >= 0) 94 | break; 95 | if (errno != EPIPE) 96 | { 97 | perror("failed to send image data"); 98 | exit(1); 99 | } 100 | } 101 | } 102 | 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Reverse engineering the IBM 6770 keyboard 2 | ========================================= 3 | 4 | What? 5 | ----- 6 | 7 | This repository contains information on how the IBM 6770 and 6780 keyboards 8 | work, plus how to convert them to work with USB. These keyboards are amazingly 9 | cool proto-model-Ms dating from 1985, and have a 480x32 LCD screen on each one. 10 | 11 | There's also some Arduino firmware for the keyboard controller and a very 12 | simple client which will draw an image on the LCD. 13 | 14 | - [Read all the juicy protocol details.](KeyboardProtocol.md) 15 | 16 | 17 | How? 18 | ---- 19 | 20 | The `firmware` directory contains an Arduino sketch suitable for programming a 21 | generic Blue Pill STM32F103 based microcontroller. This provides a HID Keyboard 22 | and HID LCD interface which will talk to the keyboard microcontroller itself. 23 | 24 | To use, you'll want to wire things up as following: 25 | 26 | | Keyboard pin | Blue Pill pin | Meaning | 27 | |:-------------|:--------------|:--------| 28 | | 1 | | -5V bias voltage for the LCD | 29 | | 2 | 5V | +5V power supply | 30 | | 3 | | (unknown) | 31 | | 4 | | not connected | 32 | | 5 | GND | 0V | 33 | | 6 | | not connected | 34 | | 7 | PA9, PA10 | data | 35 | | 8 | PB3 | reset | 36 | 37 | Note that the data line needs to be connected to both PA9 _and_ PA10. Also, if 38 | you want to use the LCD, you'll need to source a -5V supply from somewhere. I 39 | used a cheap Banggood negative voltage generator module. Almost no current is 40 | required. 41 | 42 | **Important**: you will need [Roger Clark Melbourne's STM32 Arduino 43 | core](https://github.com/rogerclarkmelbourne/Arduino_STM32/) and you will 44 | _also_ need to patch it with the up-to-date version of [the USBComposite 45 | library](https://github.com/arpruss/USBComposite_stm32f1). 46 | 47 | Once programmed and hooked up, plug in the Blue Pill. There'll be a short pause 48 | as the keyboard is initialised and it'll make a short beep. If you have the LCD 49 | working, you'll see it go black and then clear. It's now ready to use, 50 | presenting itself as a keyboard. 51 | 52 | To use the client, just run `make` in the `client` directory. Then do `kbdtool 53 | `. The image must be a 1bpp image which is 480x32. 54 | 55 | (The client _should_ work on anything which conforms to the HID Auxiliary 56 | Display bitmap protocol, but I haven't even heard of any other software or 57 | hardware which uses this, so I've no idea if I've gotten the protocol right.) 58 | 59 | 60 | Why? 61 | ---- 62 | 63 | I have one. And it's the nicest keyboard I've ever used. 64 | 65 | 66 | Who? 67 | ---- 68 | 69 | Everything _except_ the `binaries` directory was designed, built and written by 70 | me, David Given. You may contact me at dg@cowlark.com, or visit my website at 71 | http://www.cowlark.com. There may or may not be anything interesting there. 72 | 73 | 74 | License 75 | ------- 76 | 77 | Everything here _except_ the `binaries` directory is © 2023 David Given and is 78 | licensed under the MIT open source license. Please see [LICENSE](LICENSE) for 79 | the full text. The tl;dr is: you can do what you like with it provided you 80 | don't claim you wrote it. 81 | 82 | -------------------------------------------------------------------------------- /firmware/keymap.h: -------------------------------------------------------------------------------- 1 | #include "USBHID.h" 2 | #pragma once 3 | 4 | static const uint8_t keyboardMap[] = { 5 | // The scancode layout is as follows: 6 | // 7 | // 40 48 00 01 09 11 19 18 20 21 29 31 39 38 28 50 51 71 79 61 8 | // 41 42 4a 02 0a 12 1a 1b 23 22 2a 32 3a 3b 2b 52 72 7a 62 9 | // 43 4b 4e 04 0c 14 1c 1d 25 24 2c 34 3c 3d 3e 56 54 74 7c 64 10 | // 44 4c 5f 07 06 0e 16 1e 1f 27 26 2e 36 3f 5e 46 76 7e fe 11 | // 4d 45 65 67 47 4f 57 77 7f 6e 12 | 13 | /* 00 */ HID_KEY_Grave, 14 | /* 01 */ HID_KEY_1, 15 | /* 02 */ HID_KEY_Q, 16 | /* 03 */ 0, 17 | /* 04 */ HID_KEY_A, 18 | /* 05 */ 0, 19 | /* 06 */ HID_KEY_Z, 20 | /* 07 */ HID_KEY_NonUSBackslash, 21 | /* 08 */ 0, 22 | /* 09 */ HID_KEY_2, 23 | /* 0a */ HID_KEY_W, 24 | /* 0b */ 0, 25 | /* 0c */ HID_KEY_S, 26 | /* 0d */ 0, 27 | /* 0e */ HID_KEY_X, 28 | /* 0f */ 0, 29 | /* 10 */ 0, 30 | /* 11 */ HID_KEY_3, 31 | /* 12 */ HID_KEY_E, 32 | /* 13 */ 0, 33 | /* 14 */ HID_KEY_D, 34 | /* 15 */ 0, 35 | /* 16 */ HID_KEY_C, 36 | /* 17 */ 0, 37 | /* 18 */ HID_KEY_5, 38 | /* 19 */ HID_KEY_4, 39 | /* 1a */ HID_KEY_R, 40 | /* 1b */ HID_KEY_T, 41 | /* 1c */ HID_KEY_F, 42 | /* 1d */ HID_KEY_G, 43 | /* 1e */ HID_KEY_V, 44 | /* 1f */ HID_KEY_B, 45 | /* 20 */ HID_KEY_6, 46 | /* 21 */ HID_KEY_7, 47 | /* 22 */ HID_KEY_U, 48 | /* 23 */ HID_KEY_Y, 49 | /* 24 */ HID_KEY_J, 50 | /* 25 */ HID_KEY_H, 51 | /* 26 */ HID_KEY_M, 52 | /* 27 */ HID_KEY_N, 53 | /* 28 */ HID_KEY_Equals, 54 | /* 29 */ HID_KEY_8, 55 | /* 2a */ HID_KEY_I, 56 | /* 2b */ HID_KEY_RightBracket, 57 | /* 2c */ HID_KEY_K, 58 | /* 2d */ 0, 59 | /* 2e */ HID_KEY_Comma, 60 | /* 2f */ 0, 61 | /* 30 */ 0, 62 | /* 31 */ HID_KEY_9, 63 | /* 32 */ HID_KEY_O, 64 | /* 33 */ 0, 65 | /* 34 */ HID_KEY_L, 66 | /* 35 */ 0, 67 | /* 36 */ HID_KEY_Period, 68 | /* 37 */ 0, 69 | /* 38 */ HID_KEY_Minus, 70 | /* 39 */ HID_KEY_0, 71 | /* 3a */ HID_KEY_P, 72 | /* 3b */ HID_KEY_LeftBracket, 73 | /* 3c */ HID_KEY_Semicolon, 74 | /* 3d */ HID_KEY_Quote, 75 | /* 3e */ HID_KEY_Backslash, 76 | /* 3f */ HID_KEY_Slash, 77 | /* 40 */ HID_KEY_F1, 78 | /* 41 */ HID_KEY_F2, 79 | /* 42 */ HID_KEY_F6, 80 | /* 43 */ HID_KEY_F3, 81 | /* 44 */ HID_KEY_F4, 82 | /* 45 */ HID_KEY_F9, 83 | /* 46 */ HID_KP_1, 84 | /* 47 */ HID_KEY_Space, 85 | /* 48 */ HID_KEY_Escape, 86 | /* 49 */ 0, 87 | /* 4a */ HID_KEY_Tab, 88 | /* 4b */ HID_KEY_F7, 89 | /* 4c */ HID_KEY_F8, 90 | /* 4d */ HID_KEY_F5, 91 | /* 4e */ HID_KEY_LeftControl, 92 | /* 4f */ HID_KEY_RightAlt, 93 | /* 50 */ HID_KEY_Delete, 94 | /* 51 */ HID_KP_NumLock, 95 | /* 52 */ HID_KP_7, 96 | /* 53 */ 0, 97 | /* 54 */ HID_KP_4, 98 | /* 55 */ 0, 99 | /* 56 */ HID_KEY_Return, 100 | /* 57 */ HID_KP_0, 101 | /* 58 */ 0, 102 | /* 59 */ 0, 103 | /* 5a */ 0, 104 | /* 5b */ 0, 105 | /* 5c */ 0, 106 | /* 5d */ 0, 107 | /* 5e */ HID_KEY_RightShift, 108 | /* 5f */ HID_KEY_LeftShift, 109 | /* 60 */ 0, 110 | /* 61 */ HID_KP_Subtract, 111 | /* 62 */ 0, 112 | /* 63 */ 0, 113 | /* 64 */ 0, 114 | /* 65 */ HID_KEY_LeftAlt, 115 | /* 66 */ 0, 116 | /* 67 */ HID_KEY_LeftGUI, 117 | /* 68 */ 0, 118 | /* 69 */ 0, 119 | /* 6a */ 0, 120 | /* 6b */ 0, 121 | /* 6c */ 0, 122 | /* 6d */ 0, 123 | /* 6e */ HID_KP_Enter, 124 | /* 6f */ 0, 125 | /* 70 */ 0, 126 | /* 71 */ HID_KP_Divide, 127 | /* 72 */ HID_KP_8, 128 | /* 73 */ 0, 129 | /* 74 */ HID_KP_5, 130 | /* 75 */ 0, 131 | /* 76 */ HID_KP_2, 132 | /* 77 */ 0, 133 | /* 78 */ 0, 134 | /* 79 */ HID_KP_Multiply, 135 | /* 7a */ HID_KP_9, 136 | /* 7b */ 0, 137 | /* 7c */ HID_KP_6, 138 | /* 7d */ 0, 139 | /* 7e */ HID_KP_3, 140 | /* 7f */ HID_KP_Point, 141 | }; 142 | -------------------------------------------------------------------------------- /firmware/keyboardinterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class KeyboardInterface 6 | { 7 | private: 8 | static constexpr int BAUDRATE = 186453; 9 | 10 | public: 11 | KeyboardInterface( 12 | usart_dev* usart, uint8_t rxPin, uint8_t txPin, uint8_t resetPin): 13 | _usart(usart), 14 | _serial(usart, txPin, rxPin), 15 | _rxPin(rxPin), 16 | _txPin(txPin), 17 | _resetPin(resetPin) 18 | { 19 | } 20 | 21 | void begin() 22 | { 23 | _serial.begin(BAUDRATE); 24 | // pinMode(_txPin, OUTPUT_OPEN_DRAIN); 25 | // pinMode(_rxPin, INPUT); 26 | 27 | pinMode(_resetPin, OUTPUT_OPEN_DRAIN); 28 | digitalWrite(_resetPin, 1); 29 | delay(200); 30 | digitalWrite(_resetPin, 0); 31 | delay(200); 32 | } 33 | 34 | void txc(uint8_t b) 35 | { 36 | setUsartFlags(SERIAL_8N1); 37 | rawTx(b); 38 | } 39 | 40 | void tx(uint8_t b) 41 | { 42 | rawTx(b); 43 | } 44 | 45 | private: 46 | void rawTx(uint8_t b) 47 | { 48 | _serial.write(b); 49 | _serial.flush(); 50 | 51 | /* As the rx and tx lines are wired together, when the byte is 52 | * transmitted it'll get picked up by the receiver. Read and discard 53 | * this ready for the real response. */ 54 | 55 | rx(); 56 | setUsartFlags(SERIAL_8E1); 57 | } 58 | 59 | public: 60 | uint8_t txrxc(uint8_t b) 61 | { 62 | txc(b); 63 | return rx(); 64 | } 65 | 66 | uint8_t txrx(uint8_t b) 67 | { 68 | tx(b); 69 | return rx(); 70 | } 71 | 72 | uint8_t txrxw(uint16_t b) 73 | { 74 | txrx(b >> 8); 75 | return txrx(b & 0xff); 76 | } 77 | 78 | uint8_t rx() 79 | { 80 | while (!_serial.available()) 81 | ; 82 | return _serial.read(); 83 | } 84 | 85 | public: 86 | void initHardware() 87 | { 88 | while (cmdPoll() != 0x88) 89 | ; 90 | 91 | cmdInit(); 92 | } 93 | 94 | bool keysAvailable() const 95 | { 96 | return !(_flags & 0x80); 97 | } 98 | 99 | bool gpuBusy() const 100 | { 101 | return (_flags & 0x20); 102 | } 103 | 104 | uint8_t getFlags() const 105 | { 106 | return _flags; 107 | } 108 | 109 | void waitForGpu() 110 | { 111 | do 112 | cmdPoll(); 113 | while (gpuBusy()); 114 | } 115 | 116 | public: 117 | uint8_t cmdInit() 118 | { 119 | txrxc(0x11); 120 | txrx(0); 121 | return txrx(0); 122 | } 123 | 124 | uint8_t cmdPoll() 125 | { 126 | txrxc(0x22); 127 | _flags = txrx(0); 128 | txrx(0); 129 | return _flags; 130 | } 131 | 132 | uint8_t cmdGetKey() 133 | { 134 | txrxc(0x33); 135 | uint8_t k = txrx(0); 136 | txrx(0); 137 | return k; 138 | } 139 | 140 | void cmdSoundControl(uint8_t op) 141 | { 142 | txrxc(0x88); 143 | txrx(op); 144 | } 145 | 146 | void cmdSetGpuAddress(uint16_t address) 147 | { 148 | waitForGpu(); 149 | 150 | txrxc(0x99); 151 | txrxw(address); 152 | } 153 | 154 | void cmdSendGpuData(uint8_t count, const uint8_t data[12]) 155 | { 156 | waitForGpu(); 157 | 158 | txrxc(0xbb); 159 | txrx(count); 160 | for (int i = 0; i < count; i++) 161 | txrx(data[i]); 162 | } 163 | 164 | void cmdClear(int row, int width) 165 | { 166 | waitForGpu(); 167 | 168 | txrxc(0xdd); 169 | txrxw(0x200 * row + 1); 170 | txrxw(width); 171 | } 172 | 173 | private: 174 | void setUsartFlags(uint8_t flags) 175 | { 176 | _usart->regs->CR1 = (_usart->regs->CR1 & 0B1110000111111111) | 177 | ((uint32_t)(flags & 0x0F) << 9); 178 | _usart->regs->CR2 = (_usart->regs->CR2 & 0B1100111111111111) | 179 | ((uint32_t)(flags & 0x30) << 8); 180 | } 181 | 182 | private: 183 | usart_dev* _usart; 184 | HardwareSerial _serial; 185 | uint8_t _rxPin; 186 | uint8_t _txPin; 187 | uint8_t _resetPin; 188 | uint8_t _flags; 189 | }; 190 | -------------------------------------------------------------------------------- /firmware/firmware.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define LED_PIN PC13 5 | #define SERIALRX_PIN PA10 6 | #define SERIALTX_PIN PA9 7 | #define RESET_PIN PB3 8 | 9 | #include "rawkeyboard.h" 10 | #include "keymap.h" 11 | 12 | #include "keyboardinterface.h" 13 | #include "hidlcd.h" 14 | 15 | class IBM6770Screen : public HIDLCD 16 | { 17 | public: 18 | static constexpr int width = 480; 19 | static constexpr int height = 32; 20 | 21 | public: 22 | IBM6770Screen(USBHID& HID, KeyboardInterface& keyboardInterface): 23 | HIDLCD(HID, width, height), 24 | _currentAddress(0xffff), 25 | _keyboardInterface(keyboardInterface) 26 | { 27 | memset(_frontBuffer, 0x00, sizeof(_frontBuffer)); 28 | memset(_backBuffer, 0x00, sizeof(_backBuffer)); 29 | } 30 | 31 | void begin() 32 | { 33 | HIDLCD::begin(); 34 | 35 | for (int i = 0; i < height / 8; i++) 36 | _keyboardInterface.cmdClear(i, width); 37 | } 38 | 39 | void setPixel(int x, int y, bool value) override 40 | { 41 | /* The buffer layout mirrors the device video memory layout, of 42 | * 4*480*8bits, each byte representing a column. This makes it efficient 43 | * to sync to the device. */ 44 | 45 | uint8_t* ptr = &_frontBuffer[(y / 8) * width + x]; 46 | 47 | uint8_t b = 1 << (y & 7); 48 | if (value) 49 | *ptr |= b; 50 | else 51 | *ptr &= ~b; 52 | } 53 | 54 | void sync() 55 | { 56 | /* Look for changed data and send it to the screen --- but only one 57 | * packet at a time to avoid blocking the main loop too long (as this 58 | * will cause keypresses to be delayed). */ 59 | 60 | const uint8_t* fptr = _frontBuffer; 61 | uint8_t* bptr = _backBuffer; 62 | for (;;) 63 | { 64 | if (fptr == &_frontBuffer[sizeof(_frontBuffer)]) 65 | return; 66 | if (*fptr != *bptr) 67 | break; 68 | fptr++; 69 | bptr++; 70 | } 71 | 72 | /* We found a difference. Determine the location and video memory 73 | * address. */ 74 | 75 | int delta = fptr - _frontBuffer; 76 | int row = delta / width; 77 | int column = delta % width; 78 | uint16_t address = 0x200 * row + column + 1; 79 | 80 | if (address != _currentAddress) 81 | _keyboardInterface.cmdSetGpuAddress(address); 82 | 83 | uint8_t sendBuffer[12]; 84 | int sendCount = 0; 85 | while ((column != width) && (*fptr != *bptr) && 86 | (sendCount != sizeof(sendBuffer))) 87 | { 88 | uint8_t b = *fptr++; 89 | sendBuffer[sendCount++] = b; 90 | *bptr++ = b; 91 | address++; 92 | column++; 93 | } 94 | 95 | _keyboardInterface.cmdSendGpuData(sendCount, sendBuffer); 96 | _currentAddress = address; 97 | } 98 | 99 | private: 100 | KeyboardInterface& _keyboardInterface; 101 | uint16_t _currentAddress; 102 | uint8_t _frontBuffer[width * height / 8]; 103 | uint8_t _backBuffer[sizeof(_frontBuffer)]; 104 | }; 105 | 106 | KeyboardInterface KeyboardInterface( 107 | USART1, SERIALRX_PIN, SERIALTX_PIN, RESET_PIN); 108 | 109 | USBHID HID; 110 | HIDRawKeyboard USBKeyboard(HID); 111 | IBM6770Screen Screen(HID, KeyboardInterface); 112 | 113 | void setup() 114 | { 115 | disableDebugPorts(); 116 | 117 | pinMode(LED_PIN, OUTPUT); 118 | digitalWrite(LED_PIN, false); 119 | 120 | USBComposite.setProductId(0x6e01); 121 | USBComposite.setVendorId(0x1209); 122 | USBComposite.setManufacturerString("Cowlark Technologies"); 123 | USBComposite.setProductString("IBM 6770 Keyboard Interface"); 124 | 125 | HID.begin(); 126 | while (!USBComposite) 127 | ; 128 | 129 | USBKeyboard.begin(); 130 | 131 | KeyboardInterface.begin(); 132 | KeyboardInterface.initHardware(); 133 | KeyboardInterface.cmdSoundControl('A'); /* keyclick off */ 134 | KeyboardInterface.cmdSoundControl('4'); /* make click */ 135 | Screen.begin(); 136 | 137 | digitalWrite(LED_PIN, true); 138 | } 139 | 140 | void loop() 141 | { 142 | Screen.process(); 143 | 144 | uint8_t flags = KeyboardInterface.cmdPoll(); 145 | if (KeyboardInterface.keysAvailable()) 146 | { 147 | uint8_t key = KeyboardInterface.cmdGetKey(); 148 | uint8_t ascii = keyboardMap[key & 0x7f]; 149 | if (ascii) 150 | { 151 | if (!(key & 0x80)) 152 | USBKeyboard.press(ascii); 153 | else 154 | USBKeyboard.release(ascii); 155 | } 156 | } 157 | 158 | if (!(flags & 0x20)) 159 | Screen.sync(); 160 | } 161 | -------------------------------------------------------------------------------- /KeyboardProtocol.md: -------------------------------------------------------------------------------- 1 | The keyboard protocol 2 | ===================== 3 | 4 | The keyboard uses a half-duplex bidirectional single-wire serial link running 5 | at 186453 baud 8E1 (yes, really). It's strictly controlled by the master. The 6 | slave only replies after the master sends a byte, and the master has to wait 7 | until this byte is received before sending the next byte. 8 | 9 | At a higher level, commands are sent in packets. The first byte of each command 10 | is always sent with the parity bit forced on (sending it as 8N1 works, as the 11 | stop bit will be treated as the parity bit). 12 | 13 | Example: the Poll command sends `22, 00, 00` and receives `00, nn, 00` where 14 | `nn` is the response.The master sends `22`, and receives `00`. Then it sends 15 | `00`, and receives `nn`. Then it sends `00`, and receives `00`. The keyboard is 16 | then ready for the next command. 17 | 18 | So far I haven't seen any cases where the master sends a non-zero bytes and the 19 | slave replies with a non-zero byte. If the slave replies with an `ff`, that 20 | usually means a protocol error. 21 | 22 | The pinout is as follows: 23 | 24 | | Keyboard pin | Meaning | 25 | |:-------------|:--------| 26 | | 1 | -5V bias voltage for the LCD | 27 | | 2 | +5V power supply | 28 | | 3 | (unknown) | 29 | | 4 | not connected | 30 | | 5 | 0V | 31 | | 6 | not connected | 32 | | 7 | data | 33 | | 8 | reset | 34 | 35 | Keyboard modes 36 | -------------- 37 | 38 | When the keyboard starts up, it's in _demo mode_. In this state, typing on the 39 | keyboard will cause the scancodes to be displayed on the screen. To get it into 40 | _normal mode_, you need to send the `11 Reset` command. 41 | 42 | The Poll command returns a flags byte describing the current keyboard state. 43 | 44 | - `80`: Key event buffer is empty. 45 | - `40`: Key event buffer is full and any additional keypresses will be lost. 46 | - `20`: GPU is busy --- don't send any graphics commands. 47 | - `10`: Sound is playing. 48 | - `08`: The keyboard is in demo mode. 49 | 50 | The others are unknown (so far). 51 | 52 | The keyboard has about 1kB of video memory. The main bitmap is divided into 53 | four rows of 480 8-bit columns at addresses 0x001, 0x201, 0x401 and 0x601. Each 54 | byte represents eight _vertical_ bits. 55 | 56 | There's also a character overlay which is written to with `77`, which I haven't 57 | investigated yet. 58 | 59 | Commands 60 | -------- 61 | 62 | (If the keyboard replies with anything useful, the byte marked with a `!` is 63 | the one with the reply attached to it. No, it's not necessarily the last...) 64 | 65 | ### `11` 66 | 67 | Reset. 68 | 69 | Restarts the keyboard firmware in _normal mode_. 70 | 71 | ### `22 00! 00` 72 | 73 | Poll. 74 | 75 | Replies with the current keyboard status flags. 76 | 77 | ### `33!` 78 | 79 | Fetches the next key from the event buffer. This has the scancode in the 80 | bottom seven bytes. If this is a keyup, the top bit will be set. If the event 81 | buffer is empty, this returns 0 --- which _is_ a valid scancode! (It's `\``.) 82 | 83 | ### `44 x` 84 | 85 | Sets the cursor X position. Characters are six pixels wide. 86 | 87 | ### `55 f1 f2` 88 | 89 | Controls the cursor. 90 | 91 | For `f1`: the bottom four bits control which line the cursor is on --- 0, 1, 2 92 | or 4. Add `40` if you want 8-pixel high characters; the default is 16. 93 | 94 | For `f2`: 95 | 96 | `10` 97 | : Cursor is flashing. 98 | 99 | `40` 100 | : Cursor is an underscore only; the default is an underscore and an 101 | overscore. 102 | 103 | ### `66 r x w` 104 | 105 | Controls the overlay window. 106 | 107 | `r`: right margin, in characters. 108 | 109 | `x`: unknown --- maybe a command for scrolling? 110 | 111 | `w`: width of the window. 112 | 113 | ### `77 x bytes...` 114 | 115 | Draws a character on the overlay window. 116 | 117 | `x` is the character position. This is then followed by either 6 bytes or 12 118 | bytes, for 8-pixel and 16-pixel high characters respectively. See `55`. 119 | 120 | ### `88 nn` 121 | 122 | Sound control. 123 | 124 | `nn` can be: 125 | 126 | `30`, `31` 127 | : Plays a low beep; long, short. 128 | 129 | `32`, `33` 130 | : Plays a high beep: long, short. 131 | 132 | `34` 133 | : Produces a click. 134 | 135 | `40` 136 | : Turns keyclicks on. 137 | 138 | `41` 139 | : Turns keyclicks off. 140 | 141 | ### `99 hi lo` 142 | 143 | Sets the current video memory address. 144 | 145 | Used for writing with `bb`. 146 | 147 | ### `aa` 148 | 149 | Switches to _demo mode_. 150 | 151 | ### `bb n bytes...` 152 | 153 | Writes to video memory. 154 | 155 | Takes `n` bytes and writes them consecutively to video memory, incrementing 156 | the write address each time. `n` can go up to 12 before the firmware 157 | command buffer overflows and the keyboard crashes. 158 | 159 | ### `cc x` 160 | 161 | Unknown. 162 | 163 | ### `dd hi1 lo1 hi2 lo2` 164 | 165 | Clears video memory. 166 | 167 | Writes `{hi2,lo2}` zero bytes at address `{hi1,lo1}`. `lo1` seems to have 168 | to be 1 or odd things happen. 169 | 170 | ### `ee x` 171 | 172 | Unknown. 173 | 174 | ### `ff` 175 | 176 | Unknown. 177 | 178 | -------------------------------------------------------------------------------- /firmware/hidlcd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | static constexpr uint8_t DISPLAY_ATTRIBUTES_REPORT_ID = 21; 4 | static constexpr uint8_t BLIT_REPORT_ID = 22; 5 | 6 | static const uint8_t graphicsReportDescriptorBytes[] = { 7 | /* See page 204 of https://usb.org/sites/default/files/hut1_2.pdf. 8 | * There's also a really useful example here: 9 | * https://usb.org/sites/default/files/hutrr29b_0.pdf */ 10 | // clang-format off 11 | 0x05, 0x14, // Usage Page (Auxiliary Display Page) 12 | 0x09, 0x02, // Usage: Auxiliary Display 13 | 0xA1, 0x01, // Collection (Application) 14 | 0x09, 0x20, // Usage (Display Attributes report) 15 | 0xA1, 0x02, // Collection (Logical) 16 | 0x85, DISPLAY_ATTRIBUTES_REPORT_ID, // Report ID 17 | 0x09, 0x80, // Usage: bitmap size X 18 | 0x09, 0x81, // Usage: bitmap size Y 19 | 0x09, 0x83, // Usage: bitmap depth 20 | 0x15, 0x00, // Logical minimum: 0 21 | 0x26, LSB(0x7fff), MSB(0x7fff), // Logical maximum: 0x7fff 22 | 0x75, 0x10, // Report size: 16 23 | 0x95, 0x03, // Report size: 3 24 | 0xb1, 0x03, // Feature: Cnst, Var, Abs 25 | 0xc0, // end 26 | 0x09, 0x8a, // Usage: blit report 27 | 0xA1, 0x02, // Collection (Logical) 28 | 0x85, BLIT_REPORT_ID, // Report ID 29 | 0x09, 0x8b, // Usage: blit rectange X1 30 | 0x09, 0x8c, // Usage: blit rectange Y1 31 | 0x09, 0x8d, // Usage: blit rectange Y2 32 | 0x09, 0x8e, // Usage: blit rectange Y2 33 | 0x15, 0x00, // Logical minimum: 0 34 | 0x26, LSB(0x7fff), MSB(0x7fff), // Logical maximum: 0x7fff 35 | 0x75, 0x10, // Report size: 16 36 | 0x95, 0x03, // Report size: 4 37 | 0x91, 0x02, // Output: Data, Var, Abs 38 | 0x09, 0x8f, // Usage: blit data buffer 39 | 0x75, 0x08, // Report size: 8 40 | 0x95, 50, // Report size: 50 41 | 0x92, 0x02, 0x01, // Output: Data, Var, Abs, Buf 42 | 0xc0, 43 | 0xc0 44 | // clang-format on 45 | }; 46 | 47 | static const HIDReportDescriptor graphicsReportDescriptor = { 48 | graphicsReportDescriptorBytes, sizeof(graphicsReportDescriptorBytes)}; 49 | 50 | typedef struct 51 | { 52 | uint16_t width; 53 | uint16_t height; 54 | uint16_t depth; 55 | } __packed DisplayAttributesReport; 56 | 57 | typedef struct 58 | { 59 | uint16_t x1, y1; 60 | uint16_t x2, y2; 61 | uint8_t data[50]; 62 | } __packed BlitReport; 63 | 64 | typedef struct 65 | { 66 | uint16_t x1, y1; 67 | uint16_t x2, y2; 68 | } Bounds; 69 | 70 | class HIDLCD : public HIDReporter 71 | { 72 | public: 73 | HIDLCD(USBHID& HID, int width, int height): 74 | HIDReporter(HID, &graphicsReportDescriptor, nullptr, 0), 75 | width(width), 76 | height(height), 77 | _attributesBuffer(_attributesBufferBytes, 78 | sizeof(_attributesBufferBytes), 79 | DISPLAY_ATTRIBUTES_REPORT_ID), 80 | _displayAttributesReport({width - 1, height - 1, 1}), 81 | _blitBuffer(_blitBufferBytes, 82 | sizeof(_blitBufferBytes), 83 | BLIT_REPORT_ID, 84 | HID_BUFFER_MODE_OUTPUT) 85 | { 86 | } 87 | 88 | void begin() 89 | { 90 | HID.addFeatureBuffer(&_attributesBuffer); 91 | HID.addOutputBuffer(&_blitBuffer); 92 | 93 | usb_hid_set_feature( 94 | DISPLAY_ATTRIBUTES_REPORT_ID, (uint8_t*)&_displayAttributesReport); 95 | } 96 | 97 | void process() 98 | { 99 | BlitReport blitReport; 100 | if (usb_hid_get_data(HID_REPORT_TYPE_OUTPUT, 101 | BLIT_REPORT_ID, 102 | (uint8_t*)&blitReport, 103 | true)) 104 | { 105 | uint8_t* p = &blitReport.data[0]; 106 | uint8_t buffer; 107 | int count = 0; 108 | for (int y = blitReport.y1; y < blitReport.y2; y++) 109 | { 110 | for (int x = blitReport.x1; x < blitReport.x2; x++) 111 | { 112 | if (p == &blitReport.data[sizeof(blitReport.data)]) 113 | break; 114 | 115 | if (!count) 116 | { 117 | buffer = *p++; 118 | count = 8; 119 | } 120 | 121 | setPixel(x, y, buffer & 0x80); 122 | buffer <<= 1; 123 | count--; 124 | } 125 | } 126 | } 127 | } 128 | 129 | virtual void setPixel(int x, int y, bool value) = 0; 130 | 131 | public: 132 | int width; 133 | int height; 134 | 135 | private: 136 | uint8_t _attributesBufferBytes[HID_BUFFER_ALLOCATE_SIZE( 137 | sizeof(DisplayAttributesReport), DISPLAY_ATTRIBUTES_REPORT_ID)]; 138 | HIDBuffer_t _attributesBuffer; 139 | DisplayAttributesReport _displayAttributesReport; 140 | 141 | uint8_t 142 | _blitBufferBytes[HID_BUFFER_ALLOCATE_SIZE(sizeof(BlitReport), BLIT_REPORT_ID)]; 143 | HIDBuffer_t _blitBuffer; 144 | }; 145 | -------------------------------------------------------------------------------- /firmware/rawkeyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum 4 | { 5 | /* Zero, does not correspond to any key. */ 6 | HID_KEY_None = 0, 7 | 8 | /* Keycode definitions. */ 9 | HID_KEY_A = 4, 10 | HID_KEY_B = 5, 11 | HID_KEY_C = 6, 12 | HID_KEY_D = 7, 13 | HID_KEY_E = 8, 14 | HID_KEY_F = 9, 15 | HID_KEY_G = 10, 16 | HID_KEY_H = 11, 17 | HID_KEY_I = 12, 18 | HID_KEY_J = 13, 19 | HID_KEY_K = 14, 20 | HID_KEY_L = 15, 21 | HID_KEY_M = 16, 22 | HID_KEY_N = 17, 23 | HID_KEY_O = 18, 24 | HID_KEY_P = 19, 25 | HID_KEY_Q = 20, 26 | HID_KEY_R = 21, 27 | HID_KEY_S = 22, 28 | HID_KEY_T = 23, 29 | HID_KEY_U = 24, 30 | HID_KEY_V = 25, 31 | HID_KEY_W = 26, 32 | HID_KEY_X = 27, 33 | HID_KEY_Y = 28, 34 | HID_KEY_Z = 29, 35 | HID_KEY_1 = 30, 36 | HID_KEY_2 = 31, 37 | HID_KEY_3 = 32, 38 | HID_KEY_4 = 33, 39 | HID_KEY_5 = 34, 40 | HID_KEY_6 = 35, 41 | HID_KEY_7 = 36, 42 | HID_KEY_8 = 37, 43 | HID_KEY_9 = 38, 44 | HID_KEY_0 = 39, 45 | HID_KEY_Return = 40, 46 | HID_KEY_Escape = 41, 47 | HID_KEY_Delete = 42, 48 | HID_KEY_Tab = 43, 49 | HID_KEY_Space = 44, 50 | HID_KEY_Minus = 45, 51 | HID_KEY_Equals = 46, 52 | HID_KEY_LeftBracket = 47, 53 | HID_KEY_RightBracket = 48, 54 | HID_KEY_Backslash = 49, 55 | HID_KEY_Semicolon = 51, 56 | HID_KEY_Quote = 52, 57 | HID_KEY_Grave = 53, 58 | HID_KEY_Comma = 54, 59 | HID_KEY_Period = 55, 60 | HID_KEY_Slash = 56, 61 | HID_KEY_CapsLock = 57, 62 | HID_KEY_F1 = 58, 63 | HID_KEY_F2 = 59, 64 | HID_KEY_F3 = 60, 65 | HID_KEY_F4 = 61, 66 | HID_KEY_F5 = 62, 67 | HID_KEY_F6 = 63, 68 | HID_KEY_F7 = 64, 69 | HID_KEY_F8 = 65, 70 | HID_KEY_F9 = 66, 71 | HID_KEY_F10 = 67, 72 | HID_KEY_F11 = 68, 73 | HID_KEY_F12 = 69, 74 | HID_KEY_PrintScreen = 70, 75 | HID_KEY_ScrollLock = 71, 76 | HID_KEY_Pause = 72, 77 | HID_KEY_Insert = 73, 78 | HID_KEY_Home = 74, 79 | HID_KEY_PageUp = 75, 80 | HID_KEY_DeleteForward = 76, 81 | HID_KEY_End = 77, 82 | HID_KEY_PageDown = 78, 83 | HID_KEY_Right = 79, 84 | HID_KEY_Left = 80, 85 | HID_KEY_Down = 81, 86 | HID_KEY_Up = 82, 87 | HID_KP_NumLock = 83, 88 | HID_KP_Divide = 84, 89 | HID_KP_Multiply = 85, 90 | HID_KP_Subtract = 86, 91 | HID_KP_Add = 87, 92 | HID_KP_Enter = 88, 93 | HID_KP_1 = 89, 94 | HID_KP_2 = 90, 95 | HID_KP_3 = 91, 96 | HID_KP_4 = 92, 97 | HID_KP_5 = 93, 98 | HID_KP_6 = 94, 99 | HID_KP_7 = 95, 100 | HID_KP_8 = 96, 101 | HID_KP_9 = 97, 102 | HID_KP_0 = 98, 103 | HID_KP_Point = 99, 104 | HID_KEY_NonUSBackslash = 100, 105 | HID_KP_Equals = 103, 106 | HID_KEY_F13 = 104, 107 | HID_KEY_F14 = 105, 108 | HID_KEY_F15 = 106, 109 | HID_KEY_F16 = 107, 110 | HID_KEY_F17 = 108, 111 | HID_KEY_F18 = 109, 112 | HID_KEY_F19 = 110, 113 | HID_KEY_F20 = 111, 114 | HID_KEY_F21 = 112, 115 | HID_KEY_F22 = 113, 116 | HID_KEY_F23 = 114, 117 | HID_KEY_F24 = 115, 118 | HID_KEY_Help = 117, 119 | HID_KEY_Menu = 118, 120 | HID_KEY_Mute = 127, 121 | HID_KEY_SysReq = 154, 122 | HID_KP_Clear = 216, 123 | HID_KP_Decimal = 220, 124 | HID_KEY_LeftControl = 224, 125 | HID_KEY_LeftShift = 225, 126 | HID_KEY_LeftAlt = 226, 127 | HID_KEY_LeftGUI = 227, 128 | HID_KEY_RightControl = 228, 129 | HID_KEY_RightShift = 229, 130 | HID_KEY_RightAlt = 230, 131 | HID_KEY_RightGUI = 231 132 | }; 133 | 134 | #define HID_KEYMOD_LeftControl 0b00000001 135 | #define HID_KEYMOD_LeftShift 0b00000010 136 | #define HID_KEYMOD_LeftAlt 0b00000100 137 | #define HID_KEYMOD_LeftGUI 0b00001000 138 | #define HID_KEYMOD_RightControl 0b00010000 139 | #define HID_KEYMOD_RightShift 0b00100000 140 | #define HID_KEYMOD_RightAlt 0b01000000 141 | #define HID_KEYMOD_RightGUI 0b10000000 142 | 143 | class HIDRawKeyboard : public HIDReporter 144 | { 145 | public: 146 | KeyReport_t keyReport; 147 | 148 | protected: 149 | uint8_t leds[HID_BUFFER_ALLOCATE_SIZE(1, 1)]; 150 | HIDBuffer_t ledData; 151 | uint8_t reportID; 152 | uint8_t rollover; 153 | bool adjustForHostCapsLock = true; 154 | 155 | public: 156 | HIDRawKeyboard(USBHID& HID, 157 | uint8_t _reportID = HID_KEYBOARD_REPORT_ID, 158 | uint8_t _rollover = HID_KEYBOARD_ROLLOVER): 159 | HIDReporter(HID, 160 | hidReportKeyboard, 161 | (uint8*)&keyReport, 162 | sizeof(KeyReport_t) + _rollover - HID_KEYBOARD_MAX_ROLLOVER, 163 | _reportID), 164 | ledData(leds, 165 | HID_BUFFER_SIZE(1, _reportID), 166 | _reportID, 167 | HID_BUFFER_MODE_NO_WAIT), 168 | reportID(_reportID), 169 | rollover(_rollover) 170 | { 171 | } 172 | 173 | void begin(void) 174 | { 175 | HID.addOutputBuffer(&ledData); 176 | } 177 | 178 | void end(void) {} 179 | 180 | void setAdjustForHostCapsLock(bool state) 181 | { 182 | adjustForHostCapsLock = state; 183 | } 184 | 185 | uint8 getLEDs(void) 186 | { 187 | return leds[reportID != 0 ? 1 : 0]; 188 | } 189 | 190 | private: 191 | uint8_t getModifiers(uint8_t k) 192 | { 193 | switch (k) 194 | { 195 | case HID_KEY_LeftShift: 196 | return HID_KEYMOD_LeftShift; 197 | case HID_KEY_LeftControl: 198 | return HID_KEYMOD_LeftControl; 199 | case HID_KEY_LeftAlt: 200 | return HID_KEYMOD_LeftAlt; 201 | case HID_KEY_LeftGUI: 202 | return HID_KEYMOD_LeftGUI; 203 | case HID_KEY_RightShift: 204 | return HID_KEYMOD_RightShift; 205 | case HID_KEY_RightAlt: 206 | return HID_KEYMOD_RightAlt; 207 | default: 208 | return 0; 209 | } 210 | } 211 | 212 | public: 213 | size_t press(uint8_t k) 214 | { 215 | keyReport.modifiers |= getModifiers(k); 216 | 217 | for (unsigned i = 0; i < rollover; i++) 218 | { 219 | if (keyReport.keys[i] == k) 220 | { 221 | goto SEND; 222 | } 223 | } 224 | for (unsigned i = 0; i < rollover; i++) 225 | { 226 | if (keyReport.keys[i] == 0) 227 | { 228 | keyReport.keys[i] = k; 229 | goto SEND; 230 | } 231 | } 232 | 233 | 234 | SEND: 235 | sendReport(); 236 | return 1; 237 | } 238 | 239 | size_t release(uint8_t k) 240 | { 241 | keyReport.modifiers &= ~getModifiers(k); 242 | 243 | for (unsigned i = 0; i < rollover; i++) 244 | { 245 | if (keyReport.keys[i] == k) 246 | { 247 | keyReport.keys[i] = 0; 248 | break; 249 | } 250 | } 251 | 252 | sendReport(); 253 | return 1; 254 | } 255 | 256 | void releaseAll(void) 257 | { 258 | memset(keyReport.keys, 0, rollover); 259 | keyReport.modifiers = 0; 260 | 261 | sendReport(); 262 | } 263 | }; 264 | -------------------------------------------------------------------------------- /binaries/keyboard-rom.decompilation.txt: -------------------------------------------------------------------------------- 1 | typedef unsigned char undefined; 2 | 3 | typedef unsigned char bool; 4 | typedef unsigned char byte; 5 | typedef unsigned char undefined1; 6 | typedef unsigned short undefined2; 7 | typedef unsigned short word; 8 | 9 | 10 | 11 | void main(void) 12 | 13 | { 14 | bool bVar1; 15 | undefined *puVar2; 16 | byte *pbVar3; 17 | byte bVar4; 18 | 19 | puVar2 = &DAT_INTMEM_7f; 20 | do { 21 | *puVar2 = 0; 22 | puVar2 = puVar2 + -1; 23 | } while (puVar2 != (undefined *)0x0); 24 | SP = 0x67; 25 | 2b_6_keyclick_flag = true; 26 | timer0_psw = 8; 27 | serial_psw = 0x10; 28 | BANK3_R5 = 0x18; 29 | BYTE_INTMEM_59 = 3; 30 | tick_counter = 0x10; 31 | BYTE_INTMEM_54 = 0x7d; 32 | keyboard_write_ptr = keyboard_buffer_start; 33 | keyboard_read_ptr = keyboard_buffer_start; 34 | pbVar3 = keyboard_bitmap; 35 | do { 36 | *pbVar3 = 0xff; 37 | pbVar3 = pbVar3 + '\x01'; 38 | } while (pbVar3 != command_buffer_start); 39 | P2_6_activate_keyboard = false; 40 | RD = 0; 41 | RD = 1; 42 | P2_4 = false; 43 | P2_5 = false; 44 | P2_7_display_strobe = true; 45 | // Probe screen type? 46 | P0_screen_out = 0x39; 47 | P2_7_display_strobe = false; 48 | bVar4 = P1_screen_in_; 49 | bVar4 = ((bVar4 >> 7) << 1 | (bVar4 << 1) >> 7) + 1; 50 | if (bVar4 >> 2 != 0) { 51 | bVar4 = 1; 52 | } 53 | screen_height = 2; 54 | do { 55 | screen_height = screen_height << 1 | screen_height >> 7; 56 | bVar4 = bVar4 - 1; 57 | } while (bVar4 != 0); 58 | screen_type = screen_height - 1; 59 | P2_7_display_strobe = true; 60 | P0_screen_out = 0x3e; 61 | P2_7_display_strobe = false; 62 | 2b_5_cursor_8_pixels_high_flag = true; 63 | cursor_flags = 1; 64 | TMOD = 2; 65 | TH0 = 6; 66 | TR0 = 1; 67 | SCON = 0x90; 68 | PS = 1; 69 | ES = 1; 70 | ET0 = 1; 71 | WR = 0; 72 | bVar1 = F0_reading_command_flag; 73 | if (bVar1 == false) { 74 | poweron_reset(); 75 | } 76 | else { 77 | WR = 1; 78 | last_response_byte = P1_screen_in_; 79 | WR = 0; 80 | bVar4 = P; 81 | TB8 = bVar4 & 1; 82 | SBUF = last_response_byte; 83 | 2a_1 = true; 84 | DAT_INTMEM_5a = 0xff; 85 | serial_psw = 0x30; 86 | } 87 | EA = true; 88 | main_loop(); 89 | return; 90 | } 91 | 92 | 93 | 94 | // WARNING: Unknown calling convention -- yet parameter storage is locked 95 | 96 | void main_loop(void) 97 | 98 | { 99 | byte current_command_byte; 100 | 101 | do { 102 | if (((2f_0_main_thread_command_flag != false) && 103 | (-1 < (char)((2b_3 & 1U & 2b_7_cursor_flash_pending) << 7))) && 104 | ((char)((2b_3 & 1U | 2b_7_cursor_flash_pending) << 7) < '\0')) { 105 | flash_cursor(); 106 | } 107 | } while (::current_command_byte == 0); 108 | if (2b_7_cursor_flash_pending != false) { 109 | flash_cursor(); 110 | } 111 | if (current_command_byte == 0x55) { 112 | cmd_55_cursorcontrol(command_buffer_start); 113 | return; 114 | } 115 | if (current_command_byte == 0x44) { 116 | cursor_x_position = command_buffer_start[0]; 117 | exit_normal_command(); 118 | return; 119 | } 120 | if (current_command_byte == 0x66) { 121 | cmd_66_overlaycontrol(); 122 | return; 123 | } 124 | if (current_command_byte == 0x77) { 125 | cmd_77_drawcharacter(); 126 | return; 127 | } 128 | if (current_command_byte == 0x99) { 129 | cmd_99_setvideoaddress(); 130 | return; 131 | } 132 | if (current_command_byte == 0xaa) { 133 | cmd_aa(); 134 | return; 135 | } 136 | if (current_command_byte == 0xbb) { 137 | cmd_bb_senddata(command_buffer_start); 138 | return; 139 | } 140 | if (current_command_byte == 0xdd) { 141 | cmd_dd_clear(); 142 | return; 143 | } 144 | FUN_CODE_05cc(); 145 | return; 146 | } 147 | 148 | 149 | 150 | void exit_normal_command(void) 151 | 152 | { 153 | T0 = true; 154 | current_command_byte = 0; 155 | if (2f_0_main_thread_command_flag) { 156 | BYTE_INTMEM_54 = 0x7d; 157 | 2b_3 = true; 158 | } 159 | else { 160 | flash_cursor(); 161 | } 162 | main_loop(); 163 | return; 164 | } 165 | 166 | 167 | 168 | void cmd_55_cursorcontrol(byte *command_ptr) 169 | 170 | { 171 | 2b_5_cursor_8_pixels_high_flag = (bool)(*command_ptr >> 6 & 1); 172 | cursor_y = *command_ptr & 0xf; 173 | if (-1 < (cursor_y < screen_height) << 7) { 174 | cursor_y = screen_height - 1; 175 | } 176 | cursor_flags = cursor_flags & 0xf0 | command_ptr['\x01'] >> 4 & 7; 177 | exit_normal_command(); 178 | return; 179 | } 180 | 181 | 182 | 183 | // WARNING: Unknown calling convention -- yet parameter storage is locked 184 | 185 | void flash_cursor(void) 186 | 187 | { 188 | byte *pbVar1; 189 | undefined1 *puVar2; 190 | byte bVar3; 191 | char cVar4; 192 | byte bVar5; 193 | 194 | if (cursor_x_position != 0) { 195 | compute_cursor_position(cursor_x_position); 196 | if (((cursor_flags >> 2 & 1) == 1) || ((cursor_flags >> 1 & 1) != 1)) { 197 | FUN_CODE_0656(); 198 | P0_screen_out = 0xff; 199 | nop(); 200 | EA = false; 201 | bVar3 = P2; 202 | P2 = bVar3 | 0x30; 203 | P2_7_display_strobe = true; 204 | P2_7_display_strobe = false; 205 | EA = true; 206 | pbVar1 = &DAT_INTMEM_4d; 207 | cVar4 = '\x06'; 208 | nop(); 209 | nop(); 210 | nop(); 211 | do { 212 | EA = false; 213 | P2_7_display_strobe = true; 214 | bVar3 = P0_screen_out; 215 | P2_7_display_strobe = false; 216 | EA = true; 217 | *pbVar1 = bVar3 ^ 1; 218 | pbVar1 = pbVar1 + '\x01'; 219 | cVar4 = cVar4 + -1; 220 | } while (cVar4 != '\0'); 221 | puVar2 = &DAT_INTMEM_4d; 222 | cVar4 = '\x06'; 223 | FUN_CODE_0656(); 224 | nop(); 225 | P2_4 = false; 226 | P2_5 = true; 227 | do { 228 | EA = false; 229 | P0_screen_out = *puVar2; 230 | P2_7_display_strobe = true; 231 | P2_7_display_strobe = false; 232 | EA = true; 233 | puVar2 = puVar2 + '\x01'; 234 | nop(); 235 | cVar4 = cVar4 + -1; 236 | } while (cVar4 != '\0'); 237 | } 238 | if (((cursor_flags >> 2 & 1) == 0) || ((cursor_flags >> 1 & 1) != 0)) { 239 | if (2b_5_cursor_8_pixels_high_flag != false) { 240 | DAT_INTMEM_5f = DAT_INTMEM_5f + 1 & screen_type; 241 | } 242 | FUN_CODE_0656(); 243 | P0_screen_out = 0xff; 244 | nop(); 245 | EA = false; 246 | bVar3 = P2; 247 | P2 = bVar3 | 0x30; 248 | P2_7_display_strobe = true; 249 | P2_7_display_strobe = false; 250 | EA = true; 251 | pbVar1 = &DAT_INTMEM_4d; 252 | cVar4 = '\x06'; 253 | nop(); 254 | nop(); 255 | nop(); 256 | do { 257 | EA = false; 258 | P2_7_display_strobe = true; 259 | bVar3 = P0_screen_out; 260 | P2_7_display_strobe = false; 261 | EA = true; 262 | *pbVar1 = bVar3 ^ 0x80; 263 | pbVar1 = pbVar1 + '\x01'; 264 | cVar4 = cVar4 + -1; 265 | } while (cVar4 != '\0'); 266 | puVar2 = &DAT_INTMEM_4d; 267 | cVar4 = '\x06'; 268 | FUN_CODE_0656(); 269 | nop(); 270 | P2_4 = false; 271 | P2_5 = true; 272 | do { 273 | EA = false; 274 | P0_screen_out = *puVar2; 275 | P2_7_display_strobe = true; 276 | P2_7_display_strobe = false; 277 | EA = true; 278 | puVar2 = puVar2 + '\x01'; 279 | nop(); 280 | cVar4 = cVar4 + -1; 281 | } while (cVar4 != '\0'); 282 | if (2b_5_cursor_8_pixels_high_flag != false) { 283 | DAT_INTMEM_5f = DAT_INTMEM_5f - 1 & screen_type; 284 | } 285 | } 286 | if (((cursor_flags >> 2 & 1) != 0) && ((cursor_flags >> 1 & 1) != 0)) { 287 | if (2b_5_cursor_8_pixels_high_flag == false) { 288 | bVar3 = 0x7e; 289 | } 290 | else { 291 | bVar3 = 0xfe; 292 | } 293 | FUN_CODE_0656(); 294 | FUN_CODE_0645(); 295 | bVar5 = FUN_CODE_0645(); 296 | bVar5 = bVar5 ^ bVar3; 297 | FUN_CODE_0656(); 298 | send_to_screen(bVar5); 299 | if (2b_5_cursor_8_pixels_high_flag != false) { 300 | DAT_INTMEM_5f = DAT_INTMEM_5f + 1 & screen_type; 301 | FUN_CODE_0656(); 302 | FUN_CODE_0645(); 303 | bVar5 = FUN_CODE_0645(); 304 | bVar5 = bVar5 ^ 0x7f; 305 | FUN_CODE_0656(); 306 | send_to_screen(bVar5); 307 | DAT_INTMEM_5f = DAT_INTMEM_5f - 1 & screen_type; 308 | } 309 | FUN_CODE_0656(); 310 | FUN_CODE_0645(); 311 | bVar5 = FUN_CODE_0645(); 312 | bVar5 = bVar5 ^ bVar3; 313 | FUN_CODE_0656(); 314 | send_to_screen(bVar5); 315 | if (2b_5_cursor_8_pixels_high_flag != false) { 316 | DAT_INTMEM_5f = DAT_INTMEM_5f + 1 & screen_type; 317 | FUN_CODE_0656(); 318 | FUN_CODE_0645(); 319 | bVar3 = FUN_CODE_0645(); 320 | bVar3 = bVar3 ^ 0x7f; 321 | FUN_CODE_0656(); 322 | send_to_screen(bVar3); 323 | } 324 | } 325 | } 326 | 2b_7_cursor_flash_pending = (bool)(2b_7_cursor_flash_pending ^ 1); 327 | return; 328 | } 329 | 330 | 331 | 332 | void cmd_66_overlaycontrol(void) 333 | 334 | { 335 | byte cVar1; 336 | byte cVar2; 337 | byte cVar3; 338 | byte cVar4; 339 | char cVar5; 340 | char cVar6; 341 | byte bVar7; 342 | 343 | if (command_buffer_start[2] != 0) { 344 | if (command_buffer_start[0] == 0) { 345 | command_buffer_start[0] = 1; 346 | } 347 | if (command_buffer_start[1] == 0) { 348 | command_buffer_start[1] = 1; 349 | } 350 | if (command_buffer_start[1] == command_buffer_start[0]) { 351 | cVar3 = 1; 352 | cVar4 = 1; 353 | } 354 | else if ((command_buffer_start[1] < command_buffer_start[0]) << 7 < '\0') { 355 | bVar7 = command_buffer_start[1] + command_buffer_start[2]; 356 | if (-1 < (command_buffer_start[0] - command_buffer_start[1] < 357 | command_buffer_start[2] - 358 | (((command_buffer_start[0] < command_buffer_start[1]) << 7) >> 7)) << 7) { 359 | cVar5 = ~bVar7 + command_buffer_start[0] + '\x02'; 360 | do { 361 | FUN_CODE_0354(bVar7); 362 | bVar7 = bVar7 + 1; 363 | command_buffer_start[1] = command_buffer_start[1] + 1; 364 | cVar5 = cVar5 + -1; 365 | } while (cVar5 != '\0'); 366 | } 367 | cVar3 = (command_buffer_start[0] + 1) - command_buffer_start[1]; 368 | cVar4 = 1; 369 | } 370 | else { 371 | cVar5 = command_buffer_start[1] - command_buffer_start[2]; 372 | if (-1 < (command_buffer_start[1] - command_buffer_start[0] < 373 | command_buffer_start[2] - 374 | (((command_buffer_start[1] < command_buffer_start[0]) << 7) >> 7)) << 7) { 375 | cVar6 = (cVar5 + '\x01') - command_buffer_start[0]; 376 | do { 377 | FUN_CODE_0354(cVar5); 378 | cVar5 = cVar5 + -1; 379 | command_buffer_start[1] = command_buffer_start[1] - 1; 380 | cVar6 = cVar6 + -1; 381 | } while (cVar6 != '\0'); 382 | } 383 | cVar3 = (command_buffer_start[1] + 1) - command_buffer_start[0]; 384 | cVar4 = 0xff; 385 | } 386 | do { 387 | compute_cursor_position(command_buffer_start[1]); 388 | FUN_CODE_0656(); 389 | cVar1 = 6; 390 | P2_4 = false; 391 | P2_5 = true; 392 | do { 393 | EA = false; 394 | P0_screen_out = 0; 395 | P2_7_display_strobe = true; 396 | P2_7_display_strobe = false; 397 | EA = true; 398 | nop(); 399 | nop(); 400 | nop(); 401 | cVar1 = cVar1 - 1; 402 | } while (cVar1 != 0); 403 | if (2b_5_cursor_8_pixels_high_flag != false) { 404 | DAT_INTMEM_5f = DAT_INTMEM_5f + 1 & screen_type; 405 | FUN_CODE_0656(); 406 | cVar2 = 6; 407 | P2_4 = false; 408 | P2_5 = true; 409 | do { 410 | EA = false; 411 | P0_screen_out = 0; 412 | P2_7_display_strobe = true; 413 | P2_7_display_strobe = false; 414 | EA = true; 415 | nop(); 416 | nop(); 417 | nop(); 418 | cVar2 = cVar2 - 1; 419 | } while (cVar2 != 0); 420 | } 421 | command_buffer_start[1] = cVar4 + command_buffer_start[1]; 422 | cVar3 = cVar3 - 1; 423 | } while (cVar3 != 0); 424 | } 425 | exit_normal_command(); 426 | return; 427 | } 428 | 429 | 430 | 431 | void FUN_CODE_0354(byte param_1) 432 | 433 | { 434 | byte bVar1; 435 | byte *pbVar2; 436 | char cVar3; 437 | 438 | compute_cursor_position(param_1); 439 | FUN_CODE_0656(); 440 | nop(); 441 | P0_screen_out = 0xff; 442 | EA = false; 443 | bVar1 = P2; 444 | P2 = bVar1 | 0x30; 445 | P2_7_display_strobe = true; 446 | P2_7_display_strobe = false; 447 | EA = true; 448 | pbVar2 = command_buffer_start + '\x03'; 449 | cVar3 = '\x05'; 450 | nop(); 451 | nop(); 452 | do { 453 | nop(); 454 | EA = false; 455 | P2_7_display_strobe = true; 456 | bVar1 = P0_screen_out; 457 | *pbVar2 = bVar1; 458 | P2_7_display_strobe = false; 459 | EA = true; 460 | pbVar2 = pbVar2 + '\x01'; 461 | nop(); 462 | cVar3 = cVar3 + -1; 463 | } while (cVar3 != '\0'); 464 | if (2b_5_cursor_8_pixels_high_flag != false) { 465 | DAT_INTMEM_5f = DAT_INTMEM_5f + 1 & screen_type; 466 | FUN_CODE_0656(); 467 | nop(); 468 | P0_screen_out = 0xff; 469 | EA = false; 470 | bVar1 = P2; 471 | P2 = bVar1 | 0x30; 472 | P2_7_display_strobe = true; 473 | P2_7_display_strobe = false; 474 | EA = true; 475 | cVar3 = '\x05'; 476 | nop(); 477 | nop(); 478 | nop(); 479 | do { 480 | nop(); 481 | EA = false; 482 | P2_7_display_strobe = true; 483 | bVar1 = P0_screen_out; 484 | *pbVar2 = bVar1; 485 | P2_7_display_strobe = false; 486 | EA = true; 487 | pbVar2 = pbVar2 + '\x01'; 488 | nop(); 489 | cVar3 = cVar3 + -1; 490 | } while (cVar3 != '\0'); 491 | DAT_INTMEM_5f = DAT_INTMEM_5f - 1 & screen_type; 492 | } 493 | compute_cursor_position(command_buffer_start[1]); 494 | FUN_CODE_0656(); 495 | pbVar2 = command_buffer_start + '\x03'; 496 | cVar3 = '\x05'; 497 | P2_4 = false; 498 | P2_5 = true; 499 | do { 500 | EA = false; 501 | P0_screen_out = *pbVar2; 502 | P2_7_display_strobe = true; 503 | P2_7_display_strobe = false; 504 | EA = true; 505 | pbVar2 = pbVar2 + '\x01'; 506 | nop(); 507 | cVar3 = cVar3 + -1; 508 | } while (cVar3 != '\0'); 509 | if (2b_5_cursor_8_pixels_high_flag != false) { 510 | DAT_INTMEM_5f = DAT_INTMEM_5f + 1 & screen_type; 511 | FUN_CODE_0656(); 512 | cVar3 = '\x05'; 513 | P2_4 = false; 514 | P2_5 = true; 515 | do { 516 | EA = false; 517 | P0_screen_out = *pbVar2; 518 | P2_7_display_strobe = true; 519 | P2_7_display_strobe = false; 520 | EA = true; 521 | pbVar2 = pbVar2 + '\x01'; 522 | nop(); 523 | cVar3 = cVar3 + -1; 524 | } while (cVar3 != '\0'); 525 | DAT_INTMEM_5f = DAT_INTMEM_5f - 1 & screen_type; 526 | } 527 | return; 528 | } 529 | 530 | 531 | 532 | // WARNING: Unknown calling convention -- yet parameter storage is locked 533 | 534 | void cmd_77_drawcharacter(void) 535 | 536 | { 537 | byte *command_buffer; 538 | byte *pbVar1; 539 | byte cVar2; 540 | char cVar5; 541 | byte cVar3; 542 | byte cVar4; 543 | byte bVar6; 544 | 545 | bVar6 = *command_buffer; 546 | if (bVar6 == 0) { 547 | bVar6 = 1; 548 | } 549 | compute_cursor_position(bVar6); 550 | FUN_CODE_0656(); 551 | nop(); 552 | P0_screen_out = 0xff; 553 | EA = false; 554 | bVar6 = P2; 555 | P2 = bVar6 | 0x30; 556 | P2_7_display_strobe = true; 557 | P2_7_display_strobe = false; 558 | EA = true; 559 | cVar2 = 5; 560 | nop(); 561 | nop(); 562 | nop(); 563 | nop(); 564 | do { 565 | EA = false; 566 | P2_7_display_strobe = true; 567 | bVar6 = P0_screen_out; 568 | P2_7_display_strobe = false; 569 | EA = true; 570 | command_buffer = command_buffer + '\x01'; 571 | *command_buffer = bVar6 | *command_buffer; 572 | cVar2 = cVar2 - 1; 573 | } while (cVar2 != 0); 574 | if (2b_5_cursor_8_pixels_high_flag != false) { 575 | DAT_INTMEM_5f = DAT_INTMEM_5f + 1 & screen_type; 576 | FUN_CODE_0656(); 577 | nop(); 578 | P0_screen_out = 0xff; 579 | EA = false; 580 | bVar6 = P2; 581 | P2 = bVar6 | 0x30; 582 | P2_7_display_strobe = true; 583 | P2_7_display_strobe = false; 584 | EA = true; 585 | cVar5 = '\x05'; 586 | nop(); 587 | nop(); 588 | nop(); 589 | nop(); 590 | do { 591 | EA = false; 592 | P2_7_display_strobe = true; 593 | bVar6 = P0_screen_out; 594 | P2_7_display_strobe = false; 595 | EA = true; 596 | command_buffer = command_buffer + '\x01'; 597 | *command_buffer = bVar6 | *command_buffer; 598 | cVar5 = cVar5 + -1; 599 | } while (cVar5 != '\0'); 600 | DAT_INTMEM_5f = DAT_INTMEM_5f - 1 & screen_type; 601 | } 602 | FUN_CODE_0656(); 603 | pbVar1 = command_buffer_start + '\x01'; 604 | P2_4 = false; 605 | P2_5 = true; 606 | cVar3 = 5; 607 | do { 608 | EA = false; 609 | P0_screen_out = *pbVar1; 610 | P2_7_display_strobe = true; 611 | P2_7_display_strobe = false; 612 | EA = true; 613 | pbVar1 = pbVar1 + '\x01'; 614 | nop(); 615 | cVar3 = cVar3 - 1; 616 | } while (cVar3 != 0); 617 | if (2b_5_cursor_8_pixels_high_flag != false) { 618 | DAT_INTMEM_5f = DAT_INTMEM_5f + 1 & screen_type; 619 | FUN_CODE_0656(); 620 | cVar4 = 5; 621 | P2_4 = false; 622 | P2_5 = true; 623 | do { 624 | EA = false; 625 | P0_screen_out = *pbVar1; 626 | P2_7_display_strobe = true; 627 | P2_7_display_strobe = false; 628 | EA = true; 629 | pbVar1 = pbVar1 + '\x01'; 630 | nop(); 631 | cVar4 = cVar4 - 1; 632 | } while (cVar4 != 0); 633 | DAT_INTMEM_5f = DAT_INTMEM_5f - 1 & screen_type; 634 | } 635 | exit_normal_command(); 636 | return; 637 | } 638 | 639 | 640 | 641 | void cmd_99_setvideoaddress(byte *param_1) 642 | 643 | { 644 | 2b_0 = (bool)(*param_1 & 1); 645 | DAT_INTMEM_63 = *param_1 >> 1 & 0xf; 646 | if (-1 < (DAT_INTMEM_63 < screen_height) << 7) { 647 | DAT_INTMEM_63 = screen_height - 1; 648 | } 649 | current_screen_address = param_1['\x01'] - 1; 650 | if (current_screen_address == -1) { 651 | if (2b_0 == false) { 652 | current_screen_address = '\0'; 653 | } 654 | else { 655 | 2b_0 = false; 656 | } 657 | } 658 | exit_normal_command(); 659 | return; 660 | } 661 | 662 | 663 | 664 | void cmd_aa(void) 665 | 666 | { 667 | char cVar1; 668 | byte bVar2; 669 | 670 | bVar2 = P2; 671 | P2 = bVar2 | 0xf; 672 | DAT_INTMEM_5f = 0; 673 | do { 674 | FUN_CODE_0656(); 675 | bVar2 = 0xff; 676 | cVar1 = '0'; 677 | do { 678 | send_to_screen(bVar2); 679 | cVar1 = cVar1 + -1; 680 | } while (cVar1 != '\0'); 681 | DAT_INTMEM_5f = DAT_INTMEM_5f + 1; 682 | } while ((DAT_INTMEM_5f < screen_height) << 7 < '\0'); 683 | cursor_x_position = 0; 684 | exit_normal_command(); 685 | return; 686 | } 687 | 688 | 689 | 690 | void cmd_bb_senddata(byte *command_buffer) 691 | 692 | { 693 | byte bVar1; 694 | byte count; 695 | byte bVar2; 696 | 697 | count = *command_buffer; 698 | do { 699 | bVar2 = current_screen_address / 0x30; 700 | if (2b_0 != false) { 701 | bVar2 = bVar2 + ('\x05' - ((char)((current_screen_address % 0x30 >> 5) << 7) >> 7)); 702 | } 703 | bVar1 = P2; 704 | P2 = bVar1 & 0xf0; 705 | bVar1 = P2; 706 | P2 = bVar1 | bVar2; 707 | DAT_INTMEM_5f = DAT_INTMEM_63; 708 | FUN_CODE_0656(); 709 | command_buffer = command_buffer + '\x01'; 710 | send_to_screen(*command_buffer); 711 | current_screen_address = current_screen_address + 1; 712 | if (current_screen_address == 0) { 713 | if (2b_0 == false) { 714 | 2b_0 = true; 715 | } 716 | else { 717 | 2b_0 = false; 718 | } 719 | } 720 | count = count - 1; 721 | } while (count != 0); 722 | exit_normal_command(); 723 | return; 724 | } 725 | 726 | 727 | 728 | void cmd_dd_clear(byte *command_buffer) 729 | 730 | { 731 | byte bVar1; 732 | byte bVar2; 733 | byte bVar3; 734 | 735 | 2b_0 = (bool)(*command_buffer & 1); 736 | DAT_INTMEM_63 = *command_buffer >> 1 & 0xf; 737 | if (-1 < (DAT_INTMEM_63 < screen_height) << 7) { 738 | DAT_INTMEM_63 = screen_height - 1; 739 | } 740 | current_screen_address = command_buffer['\x01'] - 1; 741 | if (current_screen_address == 0xff) { 742 | if (2b_0 == false) { 743 | current_screen_address = 0; 744 | } 745 | else { 746 | 2b_0 = false; 747 | } 748 | } 749 | 2c_4 = command_buffer['\x02'] & 1; 750 | bVar2 = command_buffer['\x03'] - 1; 751 | if (bVar2 == 0xff) { 752 | if (2c_4 == 0) { 753 | bVar2 = 0; 754 | } 755 | else { 756 | 2c_4 = 0; 757 | } 758 | } 759 | bVar3 = current_screen_address; 760 | if (2c_4 == 0) { 761 | if (2b_0 == true) goto LAB_CODE_05c9; 762 | LAB_CODE_057b: 763 | if ((bVar2 < current_screen_address) << 7 < '\0') goto LAB_CODE_05c9; 764 | } 765 | else if (2b_0 != false) goto LAB_CODE_057b; 766 | LAB_CODE_0580: 767 | do { 768 | current_screen_address = bVar3; 769 | bVar3 = current_screen_address / 0x30; 770 | if (2b_0 != false) { 771 | bVar3 = bVar3 + ('\x05' - ((char)((current_screen_address % 0x30 >> 5) << 7) >> 7)); 772 | } 773 | bVar1 = P2; 774 | P2 = bVar1 & 0xf0; 775 | bVar1 = P2; 776 | P2 = bVar1 | bVar3; 777 | DAT_INTMEM_5f = DAT_INTMEM_63; 778 | FUN_CODE_0656(); 779 | send_to_screen(0); 780 | bVar3 = current_screen_address + 1; 781 | if (bVar3 == 0) { 782 | 2b_0 = true; 783 | } 784 | if (2c_4 == 0) { 785 | if (2b_0 == true) break; 786 | } 787 | else if (2b_0 == false) goto LAB_CODE_0580; 788 | } while (-1 < (bVar2 < bVar3) << 7); 789 | if (current_screen_address == 0xff) { 790 | 2b_0 = false; 791 | } 792 | LAB_CODE_05c9: 793 | exit_normal_command(); 794 | return; 795 | } 796 | 797 | 798 | 799 | void FUN_CODE_05cc(byte *param_1) 800 | 801 | { 802 | byte bVar1; 803 | byte cVar2; 804 | byte bVar2; 805 | 806 | bVar2 = P3; 807 | P3 = bVar2 & 0xf3; 808 | bVar2 = P2; 809 | P2 = bVar2 | 0xf; 810 | if ((*param_1 >> 5 & 1) == 1) { 811 | cursor_flags = cursor_flags - 0x40; 812 | } 813 | else { 814 | cursor_flags = cursor_flags + 0x40; 815 | } 816 | bVar2 = P2; 817 | P2 = bVar2 & 0xcf; 818 | EA = false; 819 | P2_7_display_strobe = true; 820 | P0_screen_out = cursor_flags & 0xc0 | 0x3e; 821 | P2_7_display_strobe = false; 822 | EA = true; 823 | if ((*param_1 >> 6 & 1) != 1) { 824 | bVar2 = cursor_flags; 825 | if ((*param_1 >> 5 & 1) != 1) { 826 | bVar2 = cursor_flags - 0x40; 827 | } 828 | bVar1 = P2; 829 | P2 = bVar1 & 0xcf; 830 | EA = false; 831 | P2_7_display_strobe = true; 832 | P0_screen_out = bVar2 & 0xc0; 833 | P2_7_display_strobe = false; 834 | EA = true; 835 | cVar2 = 0x30; 836 | bVar2 = 0; 837 | do { 838 | send_to_screen(bVar2); 839 | cVar2 = cVar2 - 1; 840 | } while (cVar2 != 0); 841 | } 842 | T0 = true; 843 | current_command_byte = 0; 844 | if (2f_0_main_thread_command_flag == true) { 845 | BYTE_INTMEM_54 = 0x7d; 846 | 2b_3 = true; 847 | } 848 | else { 849 | flash_cursor(); 850 | } 851 | main_loop(); 852 | return; 853 | } 854 | 855 | 856 | 857 | byte compute_cursor_position(byte pos) 858 | 859 | { 860 | byte x; 861 | byte y; 862 | byte bVar1; 863 | 864 | y = (pos - 1) / 0x50; 865 | x = (pos - 1) % 0x50; 866 | bVar1 = cursor_y + y; 867 | if (2b_5_cursor_8_pixels_high_flag) { 868 | bVar1 = bVar1 + y; 869 | } 870 | DAT_INTMEM_5f = bVar1 & screen_type; 871 | y = P2; 872 | P2 = y & 0xf0; 873 | y = P2; 874 | P2 = y | x / 8; 875 | return (x % 8) * '\x06'; 876 | } 877 | 878 | 879 | 880 | undefined FUN_CODE_0645(void) 881 | 882 | { 883 | undefined uVar1; 884 | byte bVar2; 885 | 886 | P0_screen_out = 0xff; 887 | EA = false; 888 | bVar2 = P2; 889 | P2 = bVar2 | 0x30; 890 | P2_7_display_strobe = true; 891 | uVar1 = P0_screen_out; 892 | P2_7_display_strobe = false; 893 | EA = true; 894 | return uVar1; 895 | } 896 | 897 | 898 | 899 | // WARNING: Unknown calling convention -- yet parameter storage is locked 900 | 901 | void FUN_CODE_0656(void) 902 | 903 | { 904 | byte bVar1; 905 | byte in_B; 906 | 907 | bVar1 = P3; 908 | P3 = bVar1 & 0xf3; 909 | bVar1 = P3; 910 | P3 = bVar1 | DAT_INTMEM_5f & 0xc; 911 | bVar1 = P2; 912 | P2 = bVar1 & 0xcf; 913 | EA = false; 914 | P2_7_display_strobe = true; 915 | P0_screen_out = (DAT_INTMEM_5f << 7) >> 1 | (DAT_INTMEM_5f >> 1) << 7 | in_B; 916 | P2_7_display_strobe = false; 917 | EA = true; 918 | return; 919 | } 920 | 921 | 922 | 923 | void send_to_screen(byte b) 924 | 925 | { 926 | P2_4 = false; 927 | P2_5 = true; 928 | EA = false; 929 | P0_screen_out = b; 930 | P2_7_display_strobe = true; 931 | P2_7_display_strobe = false; 932 | EA = true; 933 | return; 934 | } 935 | 936 | 937 | 938 | // WARNING: Unable to use type for symbol cVar1 939 | // WARNING: Unknown calling convention -- yet parameter storage is locked 940 | 941 | void timer0_interrupt_handler(void) 942 | 943 | { 944 | byte bVar1; 945 | bool bVar2; 946 | byte keyboard_probe_r0; 947 | byte *pbVar3; 948 | byte count; 949 | byte keyboard_buffer_write_ptr; 950 | byte *buffer_ptr; 951 | byte bVar3; 952 | byte pbVar6; 953 | byte *ptr; 954 | byte *pbVar4; 955 | byte bVar5; 956 | byte bVar6; 957 | byte bVar10; 958 | word *in_DPTR; 959 | byte *font_ptr; 960 | undefined in_stack_000000ff; 961 | char cVar1; 962 | byte current_char; 963 | byte bVar7; 964 | 965 | tick_counter = tick_counter - 1; 966 | if (tick_counter == 0) { 967 | tick_counter = 0x10; 968 | if (BYTE_INTMEM_2e != 0) { 969 | pbVar3 = keyboard_bitmap; 970 | BANK3_R6 = 0x10; 971 | bVar3 = 0xff; 972 | do { 973 | bVar5 = timer0_psw; 974 | bVar3 = bVar3 & *pbVar3; 975 | bVar7 = ((char)bVar3 < '\0') << 5; 976 | bVar6 = (bVar3 == 0) << 1; 977 | timer0_psw = bVar5 & 0xdd | bVar7 | bVar6; 978 | pbVar3 = pbVar3 + '\x01'; 979 | BANK3_R6 = BANK3_R6 - 1; 980 | } while (BANK3_R6 != 0); 981 | BANK3_R7 = keyboard_probe_r0; 982 | if ((bVar3 == 0xff) && (BYTE_INTMEM_2e = BYTE_INTMEM_2e - 1, BYTE_INTMEM_2e == 0)) { 983 | BYTE_INTMEM_2e = 0xd; 984 | timer0_psw = bVar5 & 0x5d | bVar7 | bVar6 | ((byte)in_DPTR < 0x22) << 7; 985 | if ((byte)in_DPTR == 0x22) { 986 | in_DPTR = lookup_table_1; 987 | } 988 | pbVar6 = keyboard_buffer_write_ptr + 1; 989 | if (pbVar6 == 0x29) { 990 | pbVar6 = (byte)keyboard_buffer_start; 991 | } 992 | EA = false; 993 | nop(); 994 | nop(); 995 | BANK3_R5 = BANK3_R5 & 0x7f | (pbVar6 < keyboard_read_ptr) << 7; 996 | if ((byte *)pbVar6 != keyboard_read_ptr) { 997 | *(byte *)pbVar6 = *(byte *)in_DPTR; 998 | in_DPTR = (word *)((short)in_DPTR + 1); 999 | keyboard_buffer_write_ptr = pbVar6; 1000 | } 1001 | EA = true; 1002 | } 1003 | } 1004 | BYTE_INTMEM_54 = BYTE_INTMEM_54 - 1; 1005 | if (BYTE_INTMEM_54 == 0) { 1006 | BYTE_INTMEM_54 = 0x7d; 1007 | 2b_3 = !2b_3; 1008 | } 1009 | } 1010 | bVar3 = sound_length_tick_counter; 1011 | // 1012 | // Handles the sound length. 1013 | // 1014 | if (sound_length_tick_counter != 0) { 1015 | EA = false; 1016 | nop(); 1017 | nop(); 1018 | sound_length_tick_counter = sound_length_tick_counter - 1; 1019 | if (sound_length_tick_counter == 0) { 1020 | sound_length_seconds = sound_length_seconds - 1; 1021 | if (sound_length_seconds == 0) { 1022 | if (2b_4_play_sound_flag_) { 1023 | 2b_4_play_sound_flag_ = false; 1024 | } 1025 | else { 1026 | 2b_4_play_sound_flag_ = true; 1027 | P3_5_speaker = 1; 1028 | sound_length_tick_counter = 0x91; 1029 | sound_length_seconds = 2; 1030 | } 1031 | } 1032 | else { 1033 | sound_length_tick_counter = bVar3 - 2; 1034 | } 1035 | } 1036 | EA = true; 1037 | } 1038 | // 1039 | // Actually generates the tone. 1040 | // 1041 | if ((sound_length_tick_counter != 0) && (2b_4_play_sound_flag_ != true)) { 1042 | if (pitch_tick_counter == 0) { 1043 | pitch_tick_counter = pitch_value; 1044 | bVar3 = P3_5_speaker; 1045 | P3_5_speaker = bVar3 ^ 1; 1046 | } 1047 | else { 1048 | pitch_tick_counter = pitch_tick_counter - 1; 1049 | } 1050 | } 1051 | BYTE_INTMEM_59 = BYTE_INTMEM_59 - 1; 1052 | if (BYTE_INTMEM_59 != 0) goto LAB_CODE_06dc; 1053 | if (2d_7) { 1054 | 2d_7 = false; 1055 | DAT_INTMEM_64 = P2; 1056 | bVar3 = P2; 1057 | P2 = bVar3 & 0xf0; 1058 | bVar3 = BANK3_R5 & 0xdd; 1059 | bVar6 = P2; 1060 | P2 = bVar6 | keyboard_probe_r0; 1061 | P2_6_activate_keyboard = true; 1062 | bVar6 = P1_screen_in_; 1063 | P2_6_activate_keyboard = false; 1064 | if (bVar6 == BANK3_R2) { 1065 | ptr = (byte *)(keyboard_buffer_write_ptr + '\x01'); 1066 | bVar3 = BANK3_R5 & 0x5d; 1067 | if (ptr == &keyboard_buffer_end) { 1068 | ptr = keyboard_buffer_start; 1069 | } 1070 | if (ptr == keyboard_read_ptr) { 1071 | 2a_6_keyboard_buffer_full_flag = 1; 1072 | goto LAB_CODE_0887; 1073 | } 1074 | pbVar4 = (byte *)(keyboard_probe_r0 + 0x30); 1075 | bVar3 = *pbVar4; 1076 | *pbVar4 = BANK3_R2; 1077 | bVar3 = bVar3 ^ *pbVar4; 1078 | if ((bVar3 & 0x80) == 0) { 1079 | if ((bVar3 & 0x40) == 0) { 1080 | if ((bVar3 & 0x20) == 0) { 1081 | if ((bVar3 & 0x10) == 0) { 1082 | if ((bVar3 & 8) == 0) { 1083 | if ((bVar3 & 4) == 0) { 1084 | if ((bVar3 & 2) == 0) { 1085 | bVar3 = bVar3 & 0xfe; 1086 | bVar6 = 0; 1087 | BANK3_R2 = BANK3_R2 & 1; 1088 | } 1089 | else { 1090 | bVar3 = bVar3 & 0xfd; 1091 | bVar6 = 1; 1092 | BANK3_R2 = BANK3_R2 & 2; 1093 | } 1094 | } 1095 | else { 1096 | bVar3 = bVar3 & 0xfb; 1097 | bVar6 = 2; 1098 | BANK3_R2 = BANK3_R2 & 4; 1099 | } 1100 | } 1101 | else { 1102 | bVar3 = bVar3 & 0xf7; 1103 | bVar6 = 3; 1104 | BANK3_R2 = BANK3_R2 & 8; 1105 | } 1106 | } 1107 | else { 1108 | bVar3 = bVar3 & 0xef; 1109 | bVar6 = 4; 1110 | BANK3_R2 = BANK3_R2 & 0x10; 1111 | } 1112 | } 1113 | else { 1114 | bVar3 = bVar3 & 0xdf; 1115 | bVar6 = 5; 1116 | BANK3_R2 = BANK3_R2 & 0x20; 1117 | } 1118 | } 1119 | else { 1120 | bVar3 = bVar3 & 0xbf; 1121 | bVar6 = 6; 1122 | BANK3_R2 = BANK3_R2 & 0x40; 1123 | } 1124 | } 1125 | else { 1126 | bVar3 = bVar3 & 0x7f; 1127 | bVar6 = 7; 1128 | BANK3_R2 = BANK3_R2 & 0x80; 1129 | } 1130 | if (bVar3 != 0) { 1131 | *pbVar4 = bVar3 ^ *pbVar4; 1132 | F0_reading_command_flag = true; 1133 | } 1134 | bVar5 = (BANK3_R2 != 0) << 7; 1135 | if (((-1 < (char)(BANK3_R5 & 0x5d | bVar5 | ((char)(BANK3_R2 - 1) < '\0') << 5 | 1136 | (BANK3_R2 == 1) << 1)) && (BYTE_INTMEM_2e = 0, 2b_6_keyclick_flag)) && 1137 | (sound_length_tick_counter == 0)) { 1138 | sound_length_tick_counter = 0x11; 1139 | sound_length_seconds = 1; 1140 | pitch_tick_counter = 0; 1141 | pitch_value = 0; 1142 | } 1143 | bVar3 = BANK3_R5 & 0x5d; 1144 | EA = false; 1145 | nop(); 1146 | nop(); 1147 | keyboard_write_ptr = ptr; 1148 | *(byte *)keyboard_buffer_write_ptr = 1149 | (keyboard_probe_r0 >> 4 | keyboard_probe_r0 * '\x10') >> 1 | bVar5 | bVar6; 1150 | EA = true; 1151 | bVar2 = F0_reading_command_flag; 1152 | if (bVar2 == false) goto LAB_CODE_0887; 1153 | F0_reading_command_flag = false; 1154 | } 1155 | else { 1156 | LAB_CODE_0887: 1157 | keyboard_probe_r0 = keyboard_probe_r0 + 1; 1158 | bVar3 = bVar3 & 0x7f; 1159 | if (keyboard_probe_r0 == 0x10) { 1160 | keyboard_probe_r0 = 0; 1161 | } 1162 | } 1163 | P2 = DAT_INTMEM_64; 1164 | BANK3_R5 = bVar3; 1165 | } 1166 | DAT_INTMEM_64 = P2; 1167 | bVar3 = P2; 1168 | P2 = bVar3 & 0xf0; 1169 | bVar3 = P2; 1170 | P2 = bVar3 | keyboard_probe_r0; 1171 | P2_6_activate_keyboard = true; 1172 | BANK3_R2 = P1_screen_in_; 1173 | P2_6_activate_keyboard = false; 1174 | ptr = (byte *)(keyboard_probe_r0 + 0x30); 1175 | bVar6 = ((char)ptr < '\0') << 5; 1176 | bVar3 = (ptr == (byte *)0x0) << 1; 1177 | bVar10 = BANK3_R5 & 0x5d | (0xcf < keyboard_probe_r0) << 7 | bVar6 | bVar3; 1178 | if (*ptr == BANK3_R2) { 1179 | BYTE_INTMEM_59 = 3; 1180 | 2c_2 = false; 1181 | bVar10 = BANK3_R5 & 0x5d | bVar6 | bVar3 | (keyboard_probe_r0 + 1 < 0x10) << 7; 1182 | } 1183 | else if (2c_2) { 1184 | bVar3 = BANK3_R5 & 0x5d | bVar6 | bVar3; 1185 | bVar10 = bVar3 | (stack_base < BANK3_R2) << 7; 1186 | if (stack_base == BANK3_R2) { 1187 | BYTE_INTMEM_59 = 5; 1188 | bVar6 = keyboard_probe_r0 + 1; 1189 | if (keyboard_probe_r0 + 1 == 0x10) { 1190 | bVar6 = 0; 1191 | } 1192 | do { 1193 | current_keyboard_probe_value = bVar6; 1194 | bVar5 = P2; 1195 | bVar5 = bVar5 & 0xf0; 1196 | P2 = bVar5; 1197 | bVar6 = ((char)bVar5 < '\0') << 5; 1198 | bVar5 = (bVar5 == 0) << 1; 1199 | bVar10 = bVar3 & 0xdd | bVar6 | bVar5; 1200 | bVar1 = P2; 1201 | P2 = bVar1 | current_keyboard_probe_value; 1202 | P2_6_activate_keyboard = true; 1203 | bVar1 = P1_screen_in_; 1204 | P2_6_activate_keyboard = false; 1205 | if (~bVar1 != 0) { 1206 | bVar10 = bVar3 & 0x5d | bVar6 | bVar5; 1207 | bVar3 = ~bVar1; 1208 | do { 1209 | bVar5 = bVar3 >> 1 | bVar10 & 0x80; 1210 | bVar6 = bVar3 << 7; 1211 | bVar10 = bVar10 & 0x7f | bVar6; 1212 | bVar3 = bVar5; 1213 | } while (bVar6 == 0); 1214 | if (bVar5 != 0) { 1215 | 2b_1_keypress_detected = true; 1216 | break; 1217 | } 1218 | } 1219 | bVar6 = current_keyboard_probe_value + 1; 1220 | if (bVar6 == 0x10) { 1221 | bVar6 = 0; 1222 | } 1223 | bVar3 = bVar10 & 0x7f | (bVar6 < keyboard_probe_r0) << 7; 1224 | bVar10 = bVar3; 1225 | } while (bVar6 != keyboard_probe_r0); 1226 | if ((2b_1_keypress_detected == true) || (2c_0)) { 1227 | bVar10 = bVar10 & 0x7f | 2b_1_keypress_detected << 7; 1228 | 2c_0 = (bool)(2b_1_keypress_detected & 1); 1229 | 2b_1_keypress_detected = false; 1230 | } 1231 | else { 1232 | 2c_2 = false; 1233 | 2d_7 = true; 1234 | } 1235 | } 1236 | else { 1237 | 2c_2 = false; 1238 | BYTE_INTMEM_59 = 0x30; 1239 | } 1240 | } 1241 | else { 1242 | if (~BANK3_R2 != 0) { 1243 | bVar10 = BANK3_R5 & 0x5d | bVar6 | bVar3; 1244 | bVar3 = ~BANK3_R2; 1245 | do { 1246 | bVar5 = bVar3 >> 1 | bVar10 & 0x80; 1247 | bVar6 = bVar3 << 7; 1248 | bVar10 = bVar10 & 0x7f | bVar6; 1249 | bVar3 = bVar5; 1250 | } while (bVar6 == 0); 1251 | if (bVar5 != 0) { 1252 | 2c_0 = false; 1253 | 2c_2 = true; 1254 | BYTE_INTMEM_59 = 0x30; 1255 | goto LAB_CODE_07ba; 1256 | } 1257 | } 1258 | 2d_7 = true; 1259 | BYTE_INTMEM_59 = 5; 1260 | } 1261 | LAB_CODE_07ba: 1262 | P2 = DAT_INTMEM_64; 1263 | BANK3_R5 = bVar10; 1264 | BANK3_R7 = keyboard_probe_r0; 1265 | stack_base = BANK3_R2; 1266 | LAB_CODE_06dc: 1267 | EA = false; 1268 | if ((DAT_INTMEM_5a != '\0') && (DAT_INTMEM_5a = DAT_INTMEM_5a + -1, DAT_INTMEM_5a == '\0')) { 1269 | EA = false; 1270 | nop(); 1271 | nop(); 1272 | 2a_0 = 1; 1273 | serial_psw = serial_psw & 0xdf; 1274 | timer0_psw = timer0_psw & 0xdd | ((char)serial_psw < '\0') << 5 | (serial_psw == 0) << 1; 1275 | 2a_1 = false; 1276 | EA = true; 1277 | } 1278 | EA = true; 1279 | ptr = keyboard_read_ptr; 1280 | if ((2a_3_demomode_flag) && (current_command_byte == 0)) { 1281 | bVar3 = timer0_psw & 0x7f; 1282 | timer0_psw = bVar3 | (keyboard_read_ptr < keyboard_write_ptr) << 7; 1283 | if (keyboard_read_ptr != keyboard_write_ptr) { 1284 | ptr = keyboard_read_ptr + '\x01'; 1285 | timer0_psw = bVar3 | (ptr < &keyboard_buffer_end) << 7; 1286 | if (ptr == &keyboard_buffer_end) { 1287 | ptr = keyboard_buffer_start; 1288 | } 1289 | current_char = *ptr; 1290 | if (-1 < (char)current_char) { 1291 | if (2c_7) { 1292 | if (2c_6) { 1293 | buffer_ptr = command_buffer_start + '\x01'; 1294 | BANK1_R4 = (undefined)((ushort)in_DPTR >> 8); 1295 | bVar3 = current_char >> 4; 1296 | keyboard_read_ptr = ptr; 1297 | command_buffer_start[0] = current_char; 1298 | do { 1299 | bVar3 = (bVar3 & 0xf) * '\x05'; 1300 | cVar1 = '\r' - ((char)(timer0_psw & 0x59 | (0xdd < bVar3) << 7) >> 7); 1301 | font_ptr = (byte *)CONCAT11(cVar1,bVar3 + 0x22); 1302 | count = 5; 1303 | do { 1304 | *buffer_ptr = *font_ptr; 1305 | buffer_ptr = buffer_ptr + '\x01'; 1306 | font_ptr = font_ptr + 1; 1307 | count = count - 1; 1308 | } while (count != 0); 1309 | timer0_psw = timer0_psw & 0x59 | (cVar1 == '\0') << 1 | 1310 | (buffer_ptr < command_buffer_start + '\v') << 7; 1311 | bVar3 = command_buffer_start[0]; 1312 | } while (buffer_ptr != command_buffer_start + '\v'); 1313 | current_command_byte = 0x77; 1314 | command_buffer_start[0] = 1; 1315 | 2c_6 = false; 1316 | ptr = keyboard_read_ptr; 1317 | } 1318 | else { 1319 | current_command_byte = 0x66; 1320 | command_buffer_start[0] = 1; 1321 | command_buffer_start[1] = 0x50; 1322 | command_buffer_start[2] = 1; 1323 | 2c_6 = true; 1324 | ptr = keyboard_read_ptr; 1325 | } 1326 | } 1327 | else if (2c_5) { 1328 | current_command_byte = 0x66; 1329 | command_buffer_start[0] = 1; 1330 | command_buffer_start[1] = 0xa0; 1331 | command_buffer_start[2] = 0xa0; 1332 | 2c_6 = true; 1333 | 2c_7 = true; 1334 | ptr = keyboard_read_ptr; 1335 | } 1336 | else { 1337 | current_command_byte = 0x55; 1338 | command_buffer_start[0] = 0x40; 1339 | command_buffer_start[1] = 6; 1340 | 2c_5 = true; 1341 | ptr = keyboard_read_ptr; 1342 | } 1343 | } 1344 | } 1345 | } 1346 | keyboard_read_ptr = ptr; 1347 | unused_interrupt_handler(in_stack_000000ff); 1348 | EA = true; 1349 | return; 1350 | } 1351 | 1352 | 1353 | 1354 | // WARNING: Unknown calling convention -- yet parameter storage is locked 1355 | 1356 | void serial_interrupt_handler(void) 1357 | 1358 | { 1359 | bool bVar1; 1360 | byte *in_R0; 1361 | byte in_R2; 1362 | byte in_R3; 1363 | 1364 | bVar1 = TI; 1365 | if (bVar1 != false) { 1366 | TI = false; 1367 | RI = false; 1368 | } 1369 | bVar1 = RI; 1370 | if (bVar1 != false) { 1371 | process_command_byte(in_R0,in_R2,in_R3); 1372 | return; 1373 | } 1374 | return; 1375 | } 1376 | 1377 | 1378 | 1379 | // WARNING: Unknown calling convention -- yet parameter storage is locked 1380 | 1381 | void exit_serial_interrupt(void) 1382 | 1383 | { 1384 | byte in_PSW; 1385 | 1386 | serial_psw = in_PSW; 1387 | return; 1388 | } 1389 | 1390 | 1391 | 1392 | void process_command_byte 1393 | (byte *pending_command_ptr,byte pending_command_len,byte pending_command_byte) 1394 | 1395 | { 1396 | bool bVar1; 1397 | undefined uVar2; 1398 | byte bVar3; 1399 | byte bVar4; 1400 | byte b; 1401 | 1402 | b = SBUF; 1403 | bVar4 = RB8; 1404 | 2a_2_current_parity = (bool)(bVar4 & 1); 1405 | RI = false; 1406 | DAT_INTMEM_5a = 0; 1407 | bVar1 = F0_reading_command_flag; 1408 | if (bVar1 != true) { 1409 | detect_and_process_possible_command(b); 1410 | return; 1411 | } 1412 | DAT_INTMEM_5c = b; 1413 | if (pending_command_len == 0) { 1414 | if (2a_1) { 1415 | if (b != 0) { 1416 | bVar4 = 2a_2_current_parity << 7; 1417 | do { 1418 | if ((char)bVar4 < '\0') { 1419 | DAT_INTMEM_5d = DAT_INTMEM_5d + 1; 1420 | bVar4 = 0; 1421 | } 1422 | bVar3 = b << 1 | bVar4 >> 7; 1423 | bVar4 = b & 0x80; 1424 | b = bVar3; 1425 | } while (bVar3 != 0); 1426 | bVar1 = 3 < DAT_INTMEM_5d; 1427 | DAT_INTMEM_5d = 0; 1428 | if (bVar1 << 7 < '\0') { 1429 | uVar2 = P; 1430 | send_special_response_and_exit_serial_interrupt(last_response_byte); 1431 | return; 1432 | } 1433 | } 1434 | if (pending_command_byte == 0xcc) { 1435 | other_imm_cmd(); 1436 | return; 1437 | } 1438 | 2a_1 = false; 1439 | F0_reading_command_flag = false; 1440 | send_special_response_and_exit_serial_interrupt(0); 1441 | return; 1442 | } 1443 | if (pending_command_byte == 0x11) { 1444 | cmdi_11_reset(); 1445 | return; 1446 | } 1447 | if (pending_command_byte == 0x22) { 1448 | cmdi_22_poll(); 1449 | return; 1450 | } 1451 | if (pending_command_byte == 0x33) { 1452 | cmdi_33_getkey(); 1453 | return; 1454 | } 1455 | other_imm_cmd(); 1456 | return; 1457 | } 1458 | bVar4 = P; 1459 | if (((char)((2a_2_current_parity & bVar4) << 7) < '\0') || 1460 | (bVar4 = P, -1 < (char)((2a_2_current_parity | bVar4) << 7))) { 1461 | *pending_command_ptr = b; 1462 | if ((pending_command_byte == 0xbb) && (pending_command_ptr == command_buffer_start)) { 1463 | if (b == 0) goto LAB_CODE_09ed; 1464 | pending_command_len = b + 1; 1465 | if ((pending_command_len == 0) || (-1 < (pending_command_len < 0xe) << 7)) { 1466 | pending_command_len = 0xd; 1467 | command_buffer_start[0] = 0xc; 1468 | } 1469 | } 1470 | b = 0; 1471 | uVar2 = 0; 1472 | if (pending_command_len == 1) { 1473 | if (pending_command_byte == 0xcc) { 1474 | cmdi_cc(); 1475 | return; 1476 | } 1477 | if (pending_command_byte == 0x88) { 1478 | cmdi_88_soundcontrol(); 1479 | return; 1480 | } 1481 | current_command_byte = ::pending_command_byte; 1482 | T0 = false; 1483 | LAB_CODE_09ed: 1484 | F0_reading_command_flag = false; 1485 | send_special_response_and_exit_serial_interrupt(0); 1486 | return; 1487 | } 1488 | } 1489 | else { 1490 | b = 0xff; 1491 | uVar2 = 1; 1492 | } 1493 | TB8 = uVar2; 1494 | SBUF = b; 1495 | bVar1 = F0_reading_command_flag; 1496 | if (bVar1 != false) { 1497 | DAT_INTMEM_5a = 0xff; 1498 | } 1499 | exit_serial_interrupt(); 1500 | return; 1501 | } 1502 | 1503 | 1504 | 1505 | // WARNING: Unknown calling convention 1506 | 1507 | void send_special_response_and_exit_serial_interrupt(byte b) 1508 | 1509 | { 1510 | bool bVar1; 1511 | byte in_PSW; 1512 | 1513 | TB8 = in_PSW >> 7; 1514 | SBUF = b; 1515 | bVar1 = F0_reading_command_flag; 1516 | if (bVar1 != false) { 1517 | DAT_INTMEM_5a = 0xff; 1518 | } 1519 | exit_serial_interrupt(); 1520 | return; 1521 | } 1522 | 1523 | 1524 | 1525 | void send_normal_response_and_exit_serial_interrupt(byte b) 1526 | 1527 | { 1528 | byte bVar1; 1529 | bool bVar2; 1530 | 1531 | bVar1 = P; 1532 | 2a_1 = true; 1533 | TB8 = bVar1 & 1; 1534 | SBUF = b; 1535 | bVar2 = F0_reading_command_flag; 1536 | if (bVar2 != false) { 1537 | DAT_INTMEM_5a = 0xff; 1538 | } 1539 | last_response_byte = b; 1540 | exit_serial_interrupt(); 1541 | return; 1542 | } 1543 | 1544 | 1545 | 1546 | // WARNING: Unknown calling convention 1547 | 1548 | void send_special_response_and_exit_serial_interrupt(byte b) 1549 | 1550 | { 1551 | bool bVar1; 1552 | byte in_PSW; 1553 | 1554 | TB8 = in_PSW >> 7; 1555 | SBUF = b; 1556 | bVar1 = F0_reading_command_flag; 1557 | if (bVar1 != false) { 1558 | DAT_INTMEM_5a = 0xff; 1559 | } 1560 | exit_serial_interrupt(); 1561 | return; 1562 | } 1563 | 1564 | 1565 | 1566 | void detect_and_process_possible_command(byte b) 1567 | 1568 | { 1569 | bool bVar1; 1570 | byte *command_ptr; 1571 | byte pending_command_byte; 1572 | byte b_00; 1573 | 1574 | if ((b >> 4 | b << 4) == b) { 1575 | F0_reading_command_flag = true; 1576 | } 1577 | bVar1 = F0_reading_command_flag; 1578 | ::pending_command_byte = b; 1579 | if (bVar1 == false) { 1580 | TB8 = 1; 1581 | SBUF = 0xff; 1582 | bVar1 = F0_reading_command_flag; 1583 | if (bVar1 != false) { 1584 | DAT_INTMEM_5a = 0xff; 1585 | } 1586 | exit_serial_interrupt(); 1587 | return; 1588 | } 1589 | if ((((pending_command_byte == 0x55) || (pending_command_byte == 0x44)) || 1590 | (pending_command_byte == 0x66)) || (pending_command_byte == 0x77)) { 1591 | LAB_CODE_0adc: 1592 | if (current_command_byte == 0) { 1593 | LAB_CODE_0ae5: 1594 | b_00 = 0; 1595 | goto exit; 1596 | } 1597 | } 1598 | else { 1599 | if (pending_command_byte == 0x88) goto LAB_CODE_0ae5; 1600 | if (pending_command_byte == 0x99) goto LAB_CODE_0adc; 1601 | if (pending_command_byte == 0xaa) { 1602 | if (current_command_byte == 0) { 1603 | poweron_reset(); 1604 | F0_reading_command_flag = false; 1605 | send_special_response_and_exit_serial_interrupt(0); 1606 | return; 1607 | } 1608 | } 1609 | else { 1610 | if (((pending_command_byte == 0xbb) || (pending_command_byte == 0xcc)) || 1611 | ((pending_command_byte == 0xdd || (pending_command_byte == 0xee)))) goto LAB_CODE_0adc; 1612 | if (pending_command_byte == 0xff) { 1613 | if (!2c_1_variable_length_command_flag_) { 1614 | 2c_1_variable_length_command_flag_ = true; 1615 | } 1616 | BYTE_INTMEM_2e = 0xd; 1617 | F0_reading_command_flag = false; 1618 | send_special_response_and_exit_serial_interrupt(0); 1619 | return; 1620 | } 1621 | if (pending_command_byte != 0) goto LAB_CODE_0ae5; 1622 | } 1623 | } 1624 | b_00 = 0xff; 1625 | F0_reading_command_flag = false; 1626 | exit: 1627 | send_special_response_and_exit_serial_interrupt(b_00); 1628 | return; 1629 | } 1630 | 1631 | 1632 | 1633 | // WARNING: Unknown calling convention -- yet parameter storage is locked 1634 | 1635 | void cmdi_11_reset(void) 1636 | 1637 | { 1638 | EA = false; 1639 | SP = 7; 1640 | IP = 0; 1641 | IE = 0; 1642 | SCON = 0; 1643 | TCON = 0; 1644 | P0_screen_out = 0xff; 1645 | P1_screen_in_ = 0xff; 1646 | P2 = 0xff; 1647 | P3 = 0xff; 1648 | F0_reading_command_flag = true; 1649 | return; 1650 | } 1651 | 1652 | 1653 | 1654 | void cmdi_22_poll(void) 1655 | 1656 | { 1657 | byte bVar1; 1658 | 1659 | if (2a_3_demomode_flag) { 1660 | bVar1 = 0x88; 1661 | } 1662 | else { 1663 | 2a_7 = keyboard_write_ptr == keyboard_read_ptr; 1664 | 2a_5 = current_command_byte != 0; 1665 | 2a_4 = sound_length_tick_counter != 0; 1666 | 2a_6_keyboard_buffer_full_flag = 0; 1667 | 2a_0 = 0; 1668 | bVar1 = DAT_INTMEM_2a & 0xf9; 1669 | } 1670 | send_normal_response_and_exit_serial_interrupt(bVar1); 1671 | return; 1672 | } 1673 | 1674 | 1675 | 1676 | void cmdi_33_getkey(void) 1677 | 1678 | { 1679 | byte bVar1; 1680 | 1681 | bVar1 = (byte)keyboard_write_ptr ^ (byte)keyboard_read_ptr; 1682 | if ((bVar1 != 0) && (bVar1 = 0, !2a_3_demomode_flag)) { 1683 | keyboard_read_ptr = keyboard_read_ptr + '\x01'; 1684 | if (keyboard_read_ptr == &keyboard_buffer_end) { 1685 | keyboard_read_ptr = keyboard_buffer_start; 1686 | } 1687 | bVar1 = *keyboard_read_ptr; 1688 | } 1689 | send_normal_response_and_exit_serial_interrupt(bVar1); 1690 | return; 1691 | } 1692 | 1693 | 1694 | 1695 | // WARNING: Unknown calling convention -- yet parameter storage is locked 1696 | 1697 | void cmdi_88_soundcontrol(void) 1698 | 1699 | { 1700 | bool bVar1; 1701 | 1702 | F0_reading_command_flag = false; 1703 | if (sound_length_tick_counter != 0) { 1704 | F0_reading_command_flag = true; 1705 | } 1706 | if (keyboard_buffer_end == 0x34) { 1707 | bVar1 = F0_reading_command_flag; 1708 | if (bVar1 != true) { 1709 | sound_length_tick_counter = 0x11; 1710 | sound_length_seconds = 1; 1711 | pitch_tick_counter = 0; 1712 | pitch_value = 0; 1713 | } 1714 | } 1715 | else if (keyboard_buffer_end == 0x40) { 1716 | 2b_6_keyclick_flag = true; 1717 | } 1718 | else if (keyboard_buffer_end == 0x41) { 1719 | 2b_6_keyclick_flag = false; 1720 | } 1721 | else if (keyboard_buffer_end == 0x30) { 1722 | 2b_4_play_sound_flag_ = false; 1723 | sound_length_tick_counter = 0x92; 1724 | sound_length_seconds = 2; 1725 | pitch_tick_counter = 1; 1726 | pitch_value = 1; 1727 | } 1728 | else if (keyboard_buffer_end == 0x31) { 1729 | 2b_4_play_sound_flag_ = false; 1730 | sound_length_tick_counter = 0x20; 1731 | sound_length_seconds = 4; 1732 | pitch_tick_counter = 1; 1733 | pitch_value = 1; 1734 | } 1735 | else if (keyboard_buffer_end == 0x32) { 1736 | 2b_4_play_sound_flag_ = false; 1737 | sound_length_tick_counter = 0x92; 1738 | sound_length_seconds = 2; 1739 | pitch_tick_counter = 0; 1740 | pitch_value = 0; 1741 | } 1742 | else if (keyboard_buffer_end == 0x33) { 1743 | 2b_4_play_sound_flag_ = false; 1744 | sound_length_tick_counter = 0x20; 1745 | sound_length_seconds = 4; 1746 | pitch_tick_counter = 0; 1747 | pitch_value = 0; 1748 | } 1749 | F0_reading_command_flag = false; 1750 | F0_reading_command_flag = false; 1751 | send_special_response_and_exit_serial_interrupt(0); 1752 | return; 1753 | } 1754 | 1755 | 1756 | 1757 | // WARNING: Unknown calling convention -- yet parameter storage is locked 1758 | 1759 | void cmdi_cc(void) 1760 | 1761 | { 1762 | byte bVar1; 1763 | undefined uVar2; 1764 | byte bVar3; 1765 | 1766 | if (command_buffer_start[0] == 0) { 1767 | F0_reading_command_flag = false; 1768 | } 1769 | else { 1770 | DAT_INTMEM_65 = P2; 1771 | bVar3 = current_screen_address / 0x30; 1772 | if (2b_0) { 1773 | bVar3 = bVar3 + ('\x05' - ((char)((current_screen_address % 0x30 >> 5) << 7) >> 7)); 1774 | } 1775 | bVar1 = P2; 1776 | P2 = bVar1 & 0xf0; 1777 | bVar1 = P2; 1778 | P2 = bVar1 | bVar3; 1779 | DAT_INTMEM_5f = DAT_INTMEM_63; 1780 | FUN_CODE_0656(); 1781 | FUN_CODE_0645(); 1782 | uVar2 = P2; 1783 | P2 = DAT_INTMEM_65; 1784 | DAT_INTMEM_65 = uVar2; 1785 | } 1786 | send_special_response_and_exit_serial_interrupt(0); 1787 | return; 1788 | } 1789 | 1790 | 1791 | 1792 | // WARNING: Unknown calling convention -- yet parameter storage is locked 1793 | 1794 | void other_imm_cmd(void) 1795 | 1796 | { 1797 | byte bVar1; 1798 | undefined uVar2; 1799 | bool bVar3; 1800 | byte in_R6; 1801 | byte in_ACC; 1802 | undefined uVar4; 1803 | byte bVar5; 1804 | byte in_PSW; 1805 | 1806 | if (in_R6 == 1) { 1807 | F0_reading_command_flag = false; 1808 | 2a_1 = false; 1809 | TB8 = in_PSW >> 7; 1810 | SBUF = in_ACC; 1811 | bVar3 = F0_reading_command_flag; 1812 | if (bVar3 != false) { 1813 | DAT_INTMEM_5a = 0xff; 1814 | } 1815 | exit_serial_interrupt(); 1816 | return; 1817 | } 1818 | uVar4 = P2; 1819 | P2 = DAT_INTMEM_65; 1820 | DAT_INTMEM_65 = uVar4; 1821 | uVar4 = FUN_CODE_0645(); 1822 | current_screen_address = current_screen_address + 1; 1823 | if (current_screen_address == 0) { 1824 | if (2b_0 == false) { 1825 | 2b_0 = true; 1826 | } 1827 | else { 1828 | 2b_0 = false; 1829 | } 1830 | } 1831 | bVar5 = current_screen_address / 0x30; 1832 | if (2b_0 != false) { 1833 | bVar5 = bVar5 + ('\x05' - ((char)((current_screen_address % 0x30 >> 5) << 7) >> 7)); 1834 | } 1835 | bVar1 = P2; 1836 | P2 = bVar1 & 0xf0; 1837 | bVar1 = P2; 1838 | P2 = bVar1 | bVar5; 1839 | DAT_INTMEM_5f = DAT_INTMEM_63; 1840 | FUN_CODE_0656(); 1841 | FUN_CODE_0645(); 1842 | uVar2 = P2; 1843 | P2 = DAT_INTMEM_65; 1844 | DAT_INTMEM_65 = uVar2; 1845 | send_normal_response_and_exit_serial_interrupt(uVar4); 1846 | return; 1847 | } 1848 | 1849 | 1850 | 1851 | // WARNING: Unknown calling convention -- yet parameter storage is locked 1852 | 1853 | void poweron_reset(void) 1854 | 1855 | { 1856 | 2a_3_demomode_flag = true; 1857 | 2b_6_keyclick_flag = true; 1858 | current_command_byte = 0xaa; 1859 | return; 1860 | } 1861 | 1862 | 1863 | 1864 | void unused_interrupt_handler(void) 1865 | 1866 | { 1867 | EA = false; 1868 | nop(); 1869 | nop(); 1870 | return; 1871 | } 1872 | 1873 | 1874 | --------------------------------------------------------------------------------