RISC-V Input controller

What the Input controller is

The Input controller reports key presses and releases, held keys, mouse buttons, pointer position and wheel turns. Unlike the keyboard registers, which provide typed characters, it also reports keys such as the arrows, Shift and the function keys.

This editor adds the device to RARS, alongside the Synth. Its memory registers start at 0xffff0300, after the Synth's. In RARS these addresses read 0 and provide no input. Check the ID register before using the controller, as the first program below does.

Click the Screen panel to send keyboard and mouse input to the program. While the Screen has focus, keys go to the program, including Escape. Press Shift+Tab, or click outside the Screen, to return keyboard control to the editor. Losing focus releases all held keys and mouse buttons: their state bits clear and release events enter the queue. This also happens when the browser window loses focus.

A first program

This program prints a letter for each arrow key you press, R, L, D or U, and ends when you press Escape. Build it and press Run, then click the Screen and press the arrows.

        .text
main:   li      s0, 0xffff0300  # the Input controller
        lw      t0, 0(s0)       # ID: 0x494E5001 when it is there
        srli    t0, t0, 8
        li      t1, 0x494E50    # "INP"
        bne     t0, t1, absent
poll:   lw      t0, 8(s0)       # Status: how many events wait
        beqz    t0, idle
        lw      t2, 12(s0)      # Event: take the oldest one
        srli    t3, t2, 8
        andi    t3, t3, 7       # its kind
        andi    t4, t2, 0xff    # its code
        li      t5, 1           # kind 1: a key went down
        bne     t3, t5, poll
        li      t5, 0x29        # Escape
        beq     t4, t5, done
        addi    t5, t4, -0x4F   # the arrows are 0x4F to 0x52
        sltiu   t6, t5, 4
        beqz    t6, poll        # another key
        la      t6, arrows
        add     t6, t6, t5
        lb      a0, 0(t6)       # R, L, D or U
        li      a7, 11
        ecall
        j       poll
idle:   li      a0, 16          # nothing waits: sleep a frame
        li      a7, 32
        ecall
        j       poll
absent: la      a0, missing
        li      a7, 4
        ecall
done:   li      a7, 10
        ecall
        .data
arrows: .ascii  "RLDU"
missing:.asciz  "This simulator has no Input controller.\n"

The program uses three registers:

  1. It reads the ID register once, to make sure the Input controller is there.
  2. It reads Status, which says how many events are waiting. If there are none, it sleeps for 16 milliseconds and looks again.
  3. Each read of Event takes the oldest waiting event, one word. The program takes it apart: the kind in bits 10–8, and the code in bits 7–0.

The registers

Offsets below are from 0xffff0300. Each register is one 32-bit word, except Keys, which occupies 32 bytes. Read registers with lw. Read individual bytes of Keys with lbu.

OffsetRegisterIn CAccessWhat it holds
0x00IdSIM_INPUT_IDreadReads 0x494E5001 ("INP", version 1) when the Input controller is available. Reads 0 in MARS and RARS, which do not provide this device
0x04ControlSIM_INPUT_CONTROLwriteReads 0. Bit 1 is reserved: setting it stops the program because the Input controller does not support interrupts. Leave it clear and poll Status for events. Other bits are ignored
0x08StatusSIM_INPUT_STATUSreadBits 6–0 hold the number of queued events, 0 to 127. Reading Status does not remove events
0x0CEventSIM_INPUT_EVENTread, takes an eventReading removes and returns the oldest queued event, or 0 if the queue is empty. Read once with lw and keep the result
0x10PointerSIM_INPUT_POINTERreadPointer position and held mouse buttons: y in bits 30–21, x in bits 20–11, buttons in bits 2–0
0x14–0x1FReservedReads 0
0x20KeysSIM_INPUT_KEYSread256 bits indicating held keys, one per HID usage code. For code u, test bit u & 7 of the byte at KEYS + (u >> 3)

Status normally ranges from 0 to 64, the queue's capacity. Undo can restore additional events, so Status can exceed 64. It reports at most 127, even if more events are waiting.

Reading Event removes an event. Even a byte read with lbu removes it. Read it once with lw and keep the result. Reads of the other registers remove nothing.

Writes do not change the controller's state or the values later reads return. One write causes an error: setting bit 1 of Control stops the program, because this controller does not support interrupts. Leave that bit clear and poll Status.

Events

Key presses, repeats and releases, mouse button presses and releases, and wheel turns produce events, one word each. Events wait in a queue, oldest first. Two key presses between reads are both kept in order, unless the queue is full.

BitsFieldWhat it holds
31always 0
30–21ythe pointer’s row when the event happened, 0 to 1023
20–11xthe pointer’s column, 0 to 1023
10–8kindwhat happened, from the table below
7–0codewhich key or button, or how far the wheel turned

Bit 31 of an Event word is always 0. Logical and arithmetic right shifts therefore give the same result when extracting its fields.

KindIn CMeaning
0No event: the queue was empty
1SIM_INPUT_KEY_DOWNA key went down; the code is its HID usage
2SIM_INPUT_KEY_UPA key came up; the code is its HID usage
3SIM_INPUT_KEY_REPEATA held key repeats, as it does when typing; the code is its HID usage
4SIM_INPUT_BUTTON_DOWNA mouse button went down; the code is 1 left, 2 right or 3 middle
5SIM_INPUT_BUTTON_UPA mouse button came up; the code is 1 left, 2 right or 3 middle
6SIM_INPUT_WHEELThe wheel turned; the code is a signed count of notches, positive away from the user
7SIM_INPUT_DROPPEDThe queue was full and events were lost here: read the state registers again
  • A key's code is its USB HID number. It identifies a physical key position rather than its printed character. For example, the position labelled S on a US keyboard is KeyS (0x16), regardless of the active layout. The table at the end lists the supported keys.
  • Holding a key sends one key-down event, then repeats at the keyboard's typing repeat rate, then one key-up event on release. To move something while a key is held, read the keys held now instead of counting repeats.
  • A button's code is 1 for the left button, 2 for the right and 3 for the middle one.
  • The wheel's code is a signed number of notches: positive away from you, negative towards you. Consecutive unread wheel turns in the same direction merge into one event while their sum fits from −127 to 127. The merged event uses the latest pointer position.
  • Every event carries the pointer's position at the moment it happened, so a click event says where the click was.

Taking an event apart:

        lw      t0, 12(s0)      # one event
        srli    t1, t0, 8
        andi    t1, t1, 7       # kind, bits 10-8
        andi    t2, t0, 0xff    # code, bits 7-0
        srli    t3, t0, 11
        andi    t3, t3, 0x3ff   # x, bits 20-11
        srli    t4, t0, 21      # y, bits 30-21: bit 31 is 0
        slli    t5, t0, 24
        srai    t5, t5, 24      # a wheel's code as a signed number of notches

The last two instructions sign-extend the wheel code on RV32. On RV64, use slli t5, t0, 56 followed by srai t5, t5, 56, as explained below.

Keys held now

From offset 0x20, 32 bytes hold one bit per key code. A bit is 1 while its key is held. For code k, read byte 0x20 + k / 8, bit k % 8, using integer division. Reads with lbu or lw leave the state unchanged.

This checks whether the left arrow, code 0x50, is held. 0x50 / 8 is 10 and 0x50 % 8 is 0, so the bit is bit 0 of the byte at offset 0x20 + 10:

        lbu     t0, 0x2a(s0)    # keys 0x50 to 0x57
        andi    t0, t0, 1       # bit 0: the left arrow, 0x50
        bnez    t0, left        # held: move left this frame

A game can read held keys once per frame and use events for single presses such as Escape. The two views answer different questions: a key pressed and released between frames is no longer held, but both events remain queued.

The pointer

The Pointer word holds y in bits 30–21, x in bits 20–11, and held buttons in bits 2–0: bit 0 left, bit 1 right and bit 2 middle.

x and y count words of the bitmap display, from 0 at the top left. The word under the pointer is y × columns + x, where columns is the display's width in words. Dragging past an edge keeps the coordinates at the last word. Pointer movement updates this register without queuing an event.

Waiting for input

To wait for input, sleep for a few milliseconds with service 32, then read Status again, as the first program does. Sleeping costs no instructions. Polling continuously uses instructions, fills the Undo history and can reach the instruction limit.

When the queue is full

The normal queue holds 64 events, including any dropped markers. When it is full, new events are lost. Once space becomes available, a later arriving event first adds a dropped marker, kind 7. If only one slot is free, the marker fills it and that arriving event is lost too. Reading events alone does not add a marker.

On receiving a dropped marker, read Keys and Pointer to recover the current state. Held-key and button state still updates when events are lost. Undo can temporarily restore more than 64 events, as explained below.

Typed characters and keys

A press that produces a character feeds both the keyboard registers at 0xffff0000 and the Input controller. Non-character keys, such as arrows and Shift, and key releases feed only the Input controller.

  • Use the keyboard registers for text. They provide characters in the user's keyboard layout, including Shift and AltGr combinations, pasted text and Testcase input.
  • Use the Input controller for keys. It provides physical key codes. A program using it for text must convert those codes into characters itself.

The two queues are separate. Reading the Input controller leaves typed characters waiting in the keyboard receiver. A later character read, including a read through a service, gets those old characters first. To discard them, read receiver data until the receiver's Ready bit is clear.

Escape types character 27 at the keyboard registers.

When the Input controller hears nothing

  • Before the program starts and after it ends. Input is accepted from the program's first instruction until it ends. A click that focuses the Screen before Run is not queued. A key already held when the program starts appears at its next repeat as a key-down event.
  • In a Testcase. A Testcase has no keyboard and no mouse: no events, no keys held, and the pointer at 0, 0. A Testcase's input still reaches the keyboard registers as typed lines.
  • Under the bitmap display. A display whose base address is 0xffff0000, RARS's "memory map" base, can cover the Input controller's addresses. Its words are then pixels, and the Input controller writes none of them. Move the display, or make it smaller, to use both.

Undo

Undo restores events removed by the instruction being undone, at the front of the queue and in their original order. Running forward can read them again. Restored events are kept even if the queue grows past 64. Status reports at most 127.

Undo does not rewind held keys, held buttons or pointer position. Those registers show current input, so the restored queue may contain a key-down event for a key that is now released.

From C

<sim.h> names the Input controller's registers, the parts of an event and every key:

  • Registers: SIM_INPUT_ID, SIM_INPUT_CONTROL, SIM_INPUT_STATUS, SIM_INPUT_EVENT and SIM_INPUT_POINTER read a register, and SIM_INPUT_KEYS is the 32 bytes of keys held.
  • Helpers: sim_input_present() says whether the Input controller is there, sim_input_pending() how many events wait, sim_input_event() takes the oldest event, sim_input_key_down(key) says whether a key is held, and sim_input_pointer() reads the Pointer register.
  • Parts of a word: SIM_INPUT_KIND(event), SIM_INPUT_CODE(event) and SIM_INPUT_WHEEL_NOTCHES(event), which gives the wheel's notches as a signed number; SIM_INPUT_X(word) and SIM_INPUT_Y(word) for an event or the pointer, and SIM_INPUT_BUTTONS(pointer).
  • Kinds: SIM_INPUT_KEY_DOWN, SIM_INPUT_KEY_UP, SIM_INPUT_KEY_REPEAT, SIM_INPUT_BUTTON_DOWN, SIM_INPUT_BUTTON_UP, SIM_INPUT_WHEEL and SIM_INPUT_DROPPED.
  • Buttons, in two forms: SIM_INPUT_BUTTON_LEFT, SIM_INPUT_BUTTON_RIGHT and SIM_INPUT_BUTTON_MIDDLE (1, 2 and 3) are a button event's code, while SIM_INPUT_HELD_LEFT, SIM_INPUT_HELD_RIGHT and SIM_INPUT_HELD_MIDDLE (1, 2 and 4) are the Pointer register's bits.
  • Keys: SIM_KEY_ and the key's name from the table below, such as SIM_KEY_A, SIM_KEY_ARROW_LEFT and SIM_KEY_SPACE.

See Using C and C++ for compiling. This program moves a dot with the arrow keys, draws where you click, and ends on Escape:

#include <sim.h>

SIM_SCREEN(screen, 64, 64, 1);

int main(void) {
    if (!sim_input_present()) {
        sim_print_string("This simulator has no Input controller.\n");
        return 1;
    }
    unsigned x = 32, y = 32;
    for (;;) {
        while (sim_input_pending()) {
            unsigned event = sim_input_event();
            unsigned kind = SIM_INPUT_KIND(event);
            if (kind == SIM_INPUT_KEY_DOWN && SIM_INPUT_CODE(event) == SIM_KEY_ESCAPE)
                return 0;
            /* a left click paints the word under the pointer */
            if (kind == SIM_INPUT_BUTTON_DOWN && SIM_INPUT_CODE(event) == SIM_INPUT_BUTTON_LEFT)
                screen[SIM_INPUT_Y(event) * 64 + SIM_INPUT_X(event)] = sim_rgb(255, 255, 0);
        }
        screen[y * 64 + x] = 0;
        if (sim_input_key_down(SIM_KEY_ARROW_LEFT) && x > 0) x--;
        if (sim_input_key_down(SIM_KEY_ARROW_RIGHT) && x < 63) x++;
        if (sim_input_key_down(SIM_KEY_ARROW_UP) && y > 0) y--;
        if (sim_input_key_down(SIM_KEY_ARROW_DOWN) && y < 63) y++;
        screen[y * 64 + x] = sim_rgb(0, 200, 255);
        sim_sleep(16);
    }
}

On RISC-V 64

The register addresses and layouts are the same on RV32 and RV64: li s0, 0xffff0300 reaches the controller on either target. The first program and the held-key example work on both.

lw sign-extends a word on RV64. Bit 31 is clear in Id, Control, Status, Event and Pointer, so those reads have the same value on both targets. Words in the Keys bitmap can have bit 31 set. Use lbu for a key byte, or mask the bit you need after a word read.

The wheel decoder above uses 32-bit shifts. On RV64, replace its last two instructions with slli t5, t0, 56 followed by srai t5, t5, 56. This moves the wheel code's sign bit, bit 7, into bit 63 before shifting it back. The shifts by 24 shown above are for RV32.

Read Event with lw. An ld spanning Status and Event also removes an event, and its Status half already counts one fewer. In C, unsigned is 32 bits on both targets, so the SIM_INPUT_ macros, including SIM_INPUT_WHEEL_NOTCHES, work on both.

Key codes

Each key is named after the browser's name for it, written in capitals with underscores: ArrowLeft is SIM_KEY_ARROW_LEFT in C. Left and right Shift, Control, Alt and Meta (the Windows or Command key) are different keys. The keypad's keys are their own and do not depend on Num Lock. Keys that a USB keyboard has no code for, such as volume and media keys and Fn, send nothing.

Letters and digits

KeyCodeIn C
KeyA0x04SIM_KEY_A
KeyB0x05SIM_KEY_B
KeyC0x06SIM_KEY_C
KeyD0x07SIM_KEY_D
KeyE0x08SIM_KEY_E
KeyF0x09SIM_KEY_F
KeyG0x0ASIM_KEY_G
KeyH0x0BSIM_KEY_H
KeyI0x0CSIM_KEY_I
KeyJ0x0DSIM_KEY_J
KeyK0x0ESIM_KEY_K
KeyL0x0FSIM_KEY_L
KeyM0x10SIM_KEY_M
KeyN0x11SIM_KEY_N
KeyO0x12SIM_KEY_O
KeyP0x13SIM_KEY_P
KeyQ0x14SIM_KEY_Q
KeyR0x15SIM_KEY_R
KeyS0x16SIM_KEY_S
KeyT0x17SIM_KEY_T
KeyU0x18SIM_KEY_U
KeyV0x19SIM_KEY_V
KeyW0x1ASIM_KEY_W
KeyX0x1BSIM_KEY_X
KeyY0x1CSIM_KEY_Y
KeyZ0x1DSIM_KEY_Z
Digit10x1ESIM_KEY_DIGIT_1
Digit20x1FSIM_KEY_DIGIT_2
Digit30x20SIM_KEY_DIGIT_3
Digit40x21SIM_KEY_DIGIT_4
Digit50x22SIM_KEY_DIGIT_5
Digit60x23SIM_KEY_DIGIT_6
Digit70x24SIM_KEY_DIGIT_7
Digit80x25SIM_KEY_DIGIT_8
Digit90x26SIM_KEY_DIGIT_9
Digit00x27SIM_KEY_DIGIT_0

Typing keys

KeyCodeIn C
Enter0x28SIM_KEY_ENTER
Escape0x29SIM_KEY_ESCAPE
Backspace0x2ASIM_KEY_BACKSPACE
Tab0x2BSIM_KEY_TAB
Space0x2CSIM_KEY_SPACE
Minus0x2DSIM_KEY_MINUS
Equal0x2ESIM_KEY_EQUAL
BracketLeft0x2FSIM_KEY_BRACKET_LEFT
BracketRight0x30SIM_KEY_BRACKET_RIGHT
Backslash0x31SIM_KEY_BACKSLASH
Semicolon0x33SIM_KEY_SEMICOLON
Quote0x34SIM_KEY_QUOTE
Backquote0x35SIM_KEY_BACKQUOTE
Comma0x36SIM_KEY_COMMA
Period0x37SIM_KEY_PERIOD
Slash0x38SIM_KEY_SLASH
CapsLock0x39SIM_KEY_CAPS_LOCK

Function keys

KeyCodeIn C
F10x3ASIM_KEY_F1
F20x3BSIM_KEY_F2
F30x3CSIM_KEY_F3
F40x3DSIM_KEY_F4
F50x3ESIM_KEY_F5
F60x3FSIM_KEY_F6
F70x40SIM_KEY_F7
F80x41SIM_KEY_F8
F90x42SIM_KEY_F9
F100x43SIM_KEY_F10
F110x44SIM_KEY_F11
F120x45SIM_KEY_F12
F130x68SIM_KEY_F13
F140x69SIM_KEY_F14
F150x6ASIM_KEY_F15
F160x6BSIM_KEY_F16
F170x6CSIM_KEY_F17
F180x6DSIM_KEY_F18
F190x6ESIM_KEY_F19
F200x6FSIM_KEY_F20
F210x70SIM_KEY_F21
F220x71SIM_KEY_F22
F230x72SIM_KEY_F23
F240x73SIM_KEY_F24

Navigation

KeyCodeIn C
PrintScreen0x46SIM_KEY_PRINT_SCREEN
ScrollLock0x47SIM_KEY_SCROLL_LOCK
Pause0x48SIM_KEY_PAUSE
Insert0x49SIM_KEY_INSERT
Home0x4ASIM_KEY_HOME
PageUp0x4BSIM_KEY_PAGE_UP
Delete0x4CSIM_KEY_DELETE
End0x4DSIM_KEY_END
PageDown0x4ESIM_KEY_PAGE_DOWN
ArrowRight0x4FSIM_KEY_ARROW_RIGHT
ArrowLeft0x50SIM_KEY_ARROW_LEFT
ArrowDown0x51SIM_KEY_ARROW_DOWN
ArrowUp0x52SIM_KEY_ARROW_UP
ContextMenu0x65SIM_KEY_CONTEXT_MENU
Help0x75SIM_KEY_HELP

Keypad

KeyCodeIn C
NumLock0x53SIM_KEY_NUM_LOCK
NumpadDivide0x54SIM_KEY_NUMPAD_DIVIDE
NumpadMultiply0x55SIM_KEY_NUMPAD_MULTIPLY
NumpadSubtract0x56SIM_KEY_NUMPAD_SUBTRACT
NumpadAdd0x57SIM_KEY_NUMPAD_ADD
NumpadEnter0x58SIM_KEY_NUMPAD_ENTER
Numpad10x59SIM_KEY_NUMPAD_1
Numpad20x5ASIM_KEY_NUMPAD_2
Numpad30x5BSIM_KEY_NUMPAD_3
Numpad40x5CSIM_KEY_NUMPAD_4
Numpad50x5DSIM_KEY_NUMPAD_5
Numpad60x5ESIM_KEY_NUMPAD_6
Numpad70x5FSIM_KEY_NUMPAD_7
Numpad80x60SIM_KEY_NUMPAD_8
Numpad90x61SIM_KEY_NUMPAD_9
Numpad00x62SIM_KEY_NUMPAD_0
NumpadDecimal0x63SIM_KEY_NUMPAD_DECIMAL
NumpadEqual0x67SIM_KEY_NUMPAD_EQUAL
NumpadComma0x85SIM_KEY_NUMPAD_COMMA
NumpadParenLeft0xB6SIM_KEY_NUMPAD_PAREN_LEFT
NumpadParenRight0xB7SIM_KEY_NUMPAD_PAREN_RIGHT
NumpadBackspace0xBBSIM_KEY_NUMPAD_BACKSPACE

Modifiers

KeyCodeIn C
ControlLeft0xE0SIM_KEY_CONTROL_LEFT
ShiftLeft0xE1SIM_KEY_SHIFT_LEFT
AltLeft0xE2SIM_KEY_ALT_LEFT
MetaLeft0xE3SIM_KEY_META_LEFT
ControlRight0xE4SIM_KEY_CONTROL_RIGHT
ShiftRight0xE5SIM_KEY_SHIFT_RIGHT
AltRight0xE6SIM_KEY_ALT_RIGHT
MetaRight0xE7SIM_KEY_META_RIGHT

International

KeyCodeIn C
IntlBackslash0x64SIM_KEY_INTL_BACKSLASH
IntlRo0x87SIM_KEY_INTL_RO
KanaMode0x88SIM_KEY_KANA_MODE
IntlYen0x89SIM_KEY_INTL_YEN
Convert0x8ASIM_KEY_CONVERT
NonConvert0x8BSIM_KEY_NON_CONVERT
Lang10x90SIM_KEY_LANG_1
Lang20x91SIM_KEY_LANG_2
Lang30x92SIM_KEY_LANG_3
Lang40x93SIM_KEY_LANG_4
Lang50x94SIM_KEY_LANG_5

On Windows, AltGr arrives as two keys at once: Control Left and Alt Right.

For assembly, these lines give the common keys names, ready to paste at the top of a program:

.eqv KEY_A, 0x04
.eqv KEY_B, 0x05
.eqv KEY_C, 0x06
.eqv KEY_D, 0x07
.eqv KEY_E, 0x08
.eqv KEY_F, 0x09
.eqv KEY_G, 0x0A
.eqv KEY_H, 0x0B
.eqv KEY_I, 0x0C
.eqv KEY_J, 0x0D
.eqv KEY_K, 0x0E
.eqv KEY_L, 0x0F
.eqv KEY_M, 0x10
.eqv KEY_N, 0x11
.eqv KEY_O, 0x12
.eqv KEY_P, 0x13
.eqv KEY_Q, 0x14
.eqv KEY_R, 0x15
.eqv KEY_S, 0x16
.eqv KEY_T, 0x17
.eqv KEY_U, 0x18
.eqv KEY_V, 0x19
.eqv KEY_W, 0x1A
.eqv KEY_X, 0x1B
.eqv KEY_Y, 0x1C
.eqv KEY_Z, 0x1D
.eqv KEY_DIGIT_1, 0x1E
.eqv KEY_DIGIT_2, 0x1F
.eqv KEY_DIGIT_3, 0x20
.eqv KEY_DIGIT_4, 0x21
.eqv KEY_DIGIT_5, 0x22
.eqv KEY_DIGIT_6, 0x23
.eqv KEY_DIGIT_7, 0x24
.eqv KEY_DIGIT_8, 0x25
.eqv KEY_DIGIT_9, 0x26
.eqv KEY_DIGIT_0, 0x27
.eqv KEY_ENTER, 0x28
.eqv KEY_ESCAPE, 0x29
.eqv KEY_BACKSPACE, 0x2A
.eqv KEY_TAB, 0x2B
.eqv KEY_SPACE, 0x2C
.eqv KEY_MINUS, 0x2D
.eqv KEY_EQUAL, 0x2E
.eqv KEY_BRACKET_LEFT, 0x2F
.eqv KEY_BRACKET_RIGHT, 0x30
.eqv KEY_BACKSLASH, 0x31
.eqv KEY_SEMICOLON, 0x33
.eqv KEY_QUOTE, 0x34
.eqv KEY_BACKQUOTE, 0x35
.eqv KEY_COMMA, 0x36
.eqv KEY_PERIOD, 0x37
.eqv KEY_SLASH, 0x38
.eqv KEY_CAPS_LOCK, 0x39
.eqv KEY_F1, 0x3A
.eqv KEY_F2, 0x3B
.eqv KEY_F3, 0x3C
.eqv KEY_F4, 0x3D
.eqv KEY_F5, 0x3E
.eqv KEY_F6, 0x3F
.eqv KEY_F7, 0x40
.eqv KEY_F8, 0x41
.eqv KEY_F9, 0x42
.eqv KEY_F10, 0x43
.eqv KEY_F11, 0x44
.eqv KEY_F12, 0x45
.eqv KEY_PRINT_SCREEN, 0x46
.eqv KEY_SCROLL_LOCK, 0x47
.eqv KEY_PAUSE, 0x48
.eqv KEY_INSERT, 0x49
.eqv KEY_HOME, 0x4A
.eqv KEY_PAGE_UP, 0x4B
.eqv KEY_DELETE, 0x4C
.eqv KEY_END, 0x4D
.eqv KEY_PAGE_DOWN, 0x4E
.eqv KEY_ARROW_RIGHT, 0x4F
.eqv KEY_ARROW_LEFT, 0x50
.eqv KEY_ARROW_DOWN, 0x51
.eqv KEY_ARROW_UP, 0x52
.eqv KEY_NUM_LOCK, 0x53
.eqv KEY_NUMPAD_DIVIDE, 0x54
.eqv KEY_NUMPAD_MULTIPLY, 0x55
.eqv KEY_NUMPAD_SUBTRACT, 0x56
.eqv KEY_NUMPAD_ADD, 0x57
.eqv KEY_NUMPAD_ENTER, 0x58
.eqv KEY_NUMPAD_1, 0x59
.eqv KEY_NUMPAD_2, 0x5A
.eqv KEY_NUMPAD_3, 0x5B
.eqv KEY_NUMPAD_4, 0x5C
.eqv KEY_NUMPAD_5, 0x5D
.eqv KEY_NUMPAD_6, 0x5E
.eqv KEY_NUMPAD_7, 0x5F
.eqv KEY_NUMPAD_8, 0x60
.eqv KEY_NUMPAD_9, 0x61
.eqv KEY_NUMPAD_0, 0x62
.eqv KEY_NUMPAD_DECIMAL, 0x63
.eqv KEY_INTL_BACKSLASH, 0x64
.eqv KEY_CONTEXT_MENU, 0x65
.eqv KEY_NUMPAD_EQUAL, 0x67
.eqv KEY_F13, 0x68
.eqv KEY_F14, 0x69
.eqv KEY_F15, 0x6A
.eqv KEY_F16, 0x6B
.eqv KEY_F17, 0x6C
.eqv KEY_F18, 0x6D
.eqv KEY_F19, 0x6E
.eqv KEY_F20, 0x6F
.eqv KEY_F21, 0x70
.eqv KEY_F22, 0x71
.eqv KEY_F23, 0x72
.eqv KEY_F24, 0x73
.eqv KEY_HELP, 0x75
.eqv KEY_NUMPAD_COMMA, 0x85
.eqv KEY_INTL_RO, 0x87
.eqv KEY_KANA_MODE, 0x88
.eqv KEY_INTL_YEN, 0x89
.eqv KEY_CONVERT, 0x8A
.eqv KEY_NON_CONVERT, 0x8B
.eqv KEY_LANG_1, 0x90
.eqv KEY_LANG_2, 0x91
.eqv KEY_LANG_3, 0x92
.eqv KEY_LANG_4, 0x93
.eqv KEY_LANG_5, 0x94
.eqv KEY_NUMPAD_PAREN_LEFT, 0xB6
.eqv KEY_NUMPAD_PAREN_RIGHT, 0xB7
.eqv KEY_NUMPAD_BACKSPACE, 0xBB
.eqv KEY_CONTROL_LEFT, 0xE0
.eqv KEY_SHIFT_LEFT, 0xE1
.eqv KEY_ALT_LEFT, 0xE2
.eqv KEY_META_LEFT, 0xE3
.eqv KEY_CONTROL_RIGHT, 0xE4
.eqv KEY_SHIFT_RIGHT, 0xE5
.eqv KEY_ALT_RIGHT, 0xE6
.eqv KEY_META_RIGHT, 0xE7