The usual PC keyboards are capable of producing three sets of scancodes. Writing 0xf0 followed by 1, 2 or 3 to port 0x60 will put the keyboard in scancode mode 1, 2 or 3. Writing 0xf0 followed by 0 queries the mode, resulting in a scancode byte 43, 41 or 3f from the keyboard.

Set 1 contains the values that the XT keyboard (with only one set of scancodes) produced, with extensions for new keys. Someone decided that another numbering was more logical and invented scancode Set 2. However, it was realized that new scancodes would break old programs, so the keyboard output was fed to a 8042 microprocessor on the motherboard that could translate Set 2 back into Set 1. Indeed a smart construction. This is the default today. Finally there is the PS/2 version, Set 3, more regular, but used by almost nobody.

(I wrote this long ago. Nowadays Linux 2.5 may try to use Set 3. Also certain HP machines, like the PS/2 version of the HP9000 workstation, have used Set 3.)

Sets 2 and 3 are designed to be translated by the 8042. Set 1 should not be translated.

Not all keyboards support all scancode sets. For example, my MyCom laptop only supports scancode Set 2, and its keyboard does not react at all when in mode 1 or 3.

The key press / key release is coded as follows:

For Set 1, if the make code of a key is c, the break code will be c+0x80. If the make code is e0 c, the break code will be e0 c+0x80. The Pause key has make code e1 1d 45 e1 9d c5 and does not generate a break code.

For Set 2, if the make code of a key is c, the break code will be f0 c. If the make code is e0 c, the break code will be e0 f0 c. The Pause key has the 8-byte make code e1 14 77 e1 f0 14 f0 77.

For Set 3, by default most keys do not generate a break code - only CapsLock, LShift, RShift, LCtrl and LAlt do. However, by default all non-traditional keys do generate a break code - thus, LWin, RWin, Menu do, and for example on the Microsoft Internet keyboard, so do Back, Forward, Stop, Mail, Search, Favorites, Web/Home, MyComputer, Calculator, Sleep. On my BTC keyboard, also the Macro key does.

In Scancode Mode 3 it is possible to enable or disable key repeat and the production of break codes either on a key-by-key basis or for all keys at once. And just like for Set 2, key release is indicated by a f0 prefix in those cases where it is indicated. There is nothing special with the Pause key in scancode mode 3.

The 8042 microprocessor translates the incoming byte stream produced by the keyboard, and turns an f0 prefix into an OR with 80 for the next byte. (Some implementations do this for the next byte that does not have this bit set already. A consequence is that in Set 3 the keys with Set-3 value 0x80 or more are broken in a peculiar way: hitting such a key and then some other key turns the make code for this last key into a break code. For example the Sleep key on a Microsoft Internet keyboard generates 54 / d4 for press/release. But pressing and releasing first Menu and then Sleep produces 8d 8d d4 d4 as translation of 8d f0 8d 54 f0 54. Other implementations are OK.)

Unless told not to translate, the keyboard controller translates keyboard scancodes into the scancodes it returns to the CPU using the following table (in hex):

00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
00 ff 43 41 3f 3d 3b 3c 58 64 44 42 40 3e 0f 29 59
10 65 38 2a 70 1d 10 02 5a 66 71 2c 1f 1e 11 03 5b
20 67 2e 2d 20 12 05 04 5c 68 39 2f 21 14 13 06 5d
30 69 31 30 23 22 15 07 5e 6a 72 32 24 16 08 09 5f
40 6b 33 25 17 18 0b 0a 60 6c 34 35 26 27 19 0c 61
50 6d 73 28 74 1a 0d 62 6e 3a 36 1c 1b 75 2b 63 76
60 55 56 77 78 79 7a 0e 7b 7c 4f 7d 4b 47 7e 7f 6f
70 52 53 50 4c 4d 48 01 45 57 4e 51 4a 37 49 46 54
80 80? 81 82 41 54 85 86 87 88 89 8a 8b 8c 8d 8e 8f
90 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
a0 a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af
b0 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
c0 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf
d0 d0 d1 d2 d3 d4 d5? d6 d7 d8 d9? da? db dc dd de df
e0 e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef?
f0 - f1? f2? f3? f4? f5? f6? f7? f8? f9? fa? fb? fc? fd? fe? ff

A reference for the first half of this table is the book by Gary J Konzak PC 8042 Controller, ISBN 0-929392-21-3. (Report by vojtech@suse.cz.)

A way to check this table is: (i) put the keyboard in untranslated modes 1, 2, 3 and look at the resulting values, and (ii) put the keyboard in translated scancode modes 1, 2, 3. Now compare the values. The entries with question marks were not checked in this way.

Note that the range 01-7f of this table is 1-1. In the second half of the table, translated and untranslated values are equal in all known cases, with the two exceptions 83 and 84.

One asks the controller to transmit untranslated scancodes by writing a keyboard controller command with bit 5 set and bit 6 cleared. E.g., use the command byte 45 to get translated codes, and 24 to get untranslated codes that do not cause interrupts.

Effects of translation

Origin of strange scan code set values

The keyboard command f0 with argument 1, 2 or 3 sets the current scancode set, and this same command with argument 0 asks for the current scancode set. The reply is 43, 41 or 3f for sets 1, 2, 3. Why? Because in reality the reply is 1, 2 or 3, and that is what one sees when translation is off. But translation turns these into 43, 41, 3f.

Keyboard IDs

Keyboards do report an ID as a reply to the command f2. (An XT keyboard does not reply, an AT keyboard only replies with an ACK.) An MF2 AT keyboard reports ID ab 83. Translation turns this into ab 41.

Many short keyboards, like IBM ThinkPads, and Spacesaver keyboards, send ab 84 untranslated, which becomes ab 54 translated. (The netbsd source has a misunderstanding here, and seems to associate the 54 and 84 to the ThinkPad model - cf. the defines KEYB_R_MF2ID2TP75X, KEYB_R_MF2ID2TP76X.)

Several 122-key keyboards are reported to send ab 86. Here translated and untranslated values coincide. (Reports mention "122-Key Enhanced Keyboard", "standard 122-key keyboard", "122 Key Mainframe Interactive (MFI) Keyboard".)

David Monro reports ab 85 for a NCD N-97 keyboard. Tim Clarke reports ab 85 for the "122-Key Host Connect(ed) Keyboard".

He also reports Also, when playing with my KVM problems Belkin gave me a 105-key Windows keyboard which Id.s itself as 18ABh.

Linux 2.5.25 kernel source has 0xaca1 for a "NCD Sun layout keyboard". It also mentions 0xab02 and 0xab7f, but these arise as (mistaken) back translations from ab 41 and ab 54.

Ralph Brown's Interrupt list mentions "old Japanese 'G', 'P', 'A' keyboards", with keyboard IDs ab 90, ab 91, ab 92. Here translated and untranslated versions coincide. ID ab 90 was also mentioned above.

For the traditional keys the correspondence is fairly clear: above we saw the translation table, and Set 1 equals translated Set 2, and Set 3 equals Set 2 in most cases where Set 2 has a single (non-escaped) scancode, and in any case the correspondence is constant (and given below).

On the other hand, modern keyboards have all kinds of multimedia and other additional keys, and what happens for them is completely random, and varies from keyboard to keyboard.

Let us look at an example.

The Microsoft Internet keyboard has keys Search, Favorites, Stop, Forward, Back, My Computer, Mail, Web / Home, Calculator with translated Set 3 scancodes 65, 66, 68, 69, 6a, 6b, 6c, 97, 99, respectively, and translated Set 2 scancodes e0 xx, with xx = 65, 66, 68, 69, 6a, 6b, 6c, 32, 21.

On the other hand, the IBM Rapid Access II keyboard has keys CD stop, CD play, Volume D, Volume U, CD back, CD fwd with translated Set 3 scancodes 69, 6a, 6b, 6c, 6d, 44, and translated Set 2 scancodes e0 xx, with xx = 20, 22, 21, 23, 24, 12.

Thus, different keyboards have different mappings between Set 2 and Set 3 codes.

Can these other scancode sets be used? Probably not.

() Translated scancode Set 1 has weird codes that nobody wants to use.

(i) My MyCom laptop does not support scancode sets 1 and 3 at all.

(ii) Some laptops have special key combinations that bring one into a setup or configuration utility. It is impossible to do anything useful, or to get out of it again, when the scancode mode is not translated Set 2.

(iii) Many keyboards have bugs in scancode sets 1 and/or 3 but are fine in scancode Set 2. Vojtech Pavlik reports that his BTC keyboard has the same codes for the '1' and '2' keys in Set3, both having the code for '1'). On my BTC keyboard the key up value for Esc and 1 are both ff in scancode Set 1. My Safeway keyboard has untranslated Set 1 equal to translated Set 2, except for the multimedia keys, where untranslated Set 1 equals untranslated Set 2.

(iv) A big advantage of Set 3 is that each key generates a unique code so that one does not need to parse sequences. However, the BTC keyboard mentioned above generates e0 6f for its Macro key also in scancode mode 3. The Safeway keyboard mentioned above does not generate any codes for its multimedia keys in scancode mode 3.

(v) Some keyboard controllers cannot handle Set 3 values that are larger than 0x7f, and give peculiar results for e.g. the Windows keys in translated scancode mode 3. The result is that the following key is "eaten": the key down action turns into a key up.

(vi) The USB legacy support only supports translated Set 2.

(vii) The Microsoft Keyboard Scan Code Specification writes: In the very early days of Windows NT, an attempt was made to use the much more orthogonal Scan Code Set 3, but due to bugs in the implementation of this Scan Code Set on numerous OEM keyboards, the idea was abandoned. And also: Scan Code Set 3 is not used or required for operation of Microsoft operating systems.

(viii) Others also tried Set 3. The PS/2 version of the HP9000 workstation uses it. This is fine with HP's keyboards but causes some problems with foreign keyboards.

(ix) It is said that Hal Snyder of Mark Williams, Co remarked: "We find that about 10% of cheap no-name keyboards do not work in scan code set 3".

(x) These days Linux probes the keyboard, and may try to enable Set 3. This is good for learning a lot about strange keyboards. It is bad for having a stable system that just works.

(USB codes in decimal, scancodes in hex.)

# USB Set 1 X(Set 1) Set 2 X(Set 2) Set 3 X(Set 3) keycap
1 53 29 39 0e 29 0e 29 ` ~
2 30 02 41 16 02 16 02 1 !
3 31 03 3f 1e 03 1e 03 2 @
4 32 04 3d 26 04 26 04 3 #
5 33 05 3b 25 05 25 05 4 $
6 34 06 3c 2e 06 2e 06 5 % E
7 35 07 58 36 07 36 07 6 ^
8 36 08 64 3d 08 3d 08 7 &
9 37 09 44 3e 09 3e 09 8 *
10 38 0a 42 46 0a 46 0a 9 (
11 39 0b 40 45 0b 45 0b 0 )
12 45 0c 3e 4e 0c 4e 0c - _
13 46 0d 0f 55 0d 55 0d = +
15 42 0e 29 66 0e 66 0e Backspace
16 43 0f 59 0d 0f 0d 0f Tab
17 20 10 65 15 10 15 10 Q
18 26 11 38 1d 11 1d 11 W
19 8 12 2a 24 12 24 12 E
20 21 13 70 2d 13 2d 13 R
21 23 14 1d 2c 14 2c 14 T
22 28 15 10 35 15 35 15 Y
23 24 16 02 3c 16 3c 16 U
24 12 17 5a 43 17 43 17 I
25 18 18 66 44 18 44 18 O
26 19 19 71 4d 19 4d 19 P
27 47 1a 2c 54 1a 54 1a [ {
28 48 1b 1f 5b 1b 5b 1b ] }
29 49 2b 21 5d 2b 5c 75 \ |
30 57 3a 32 58 3a 14 1d CapsLock
31 4 1e 03 1c 1e 1c 1e A
32 22 1f 5b 1b 1f 1b 1f S
33 7 20 67 23 20 23 20 D
34 9 21 2e 2b 21 2b 21 F
35 10 22 2d 34 22 34 22 G
36 11 23 20 33 23 33 23 H
37 13 24 12 3b 24 3b 24 J
38 14 25 05 42 25 42 25 K
39 15 26 04 4b 26 4b 26 L
40 51 27 5c 4c 27 4c 27 ; :
41 52 28 68 52 28 52 28 ' "
42 50 00 ff 00 ff 00 ff non-US-1
43 40 1c 1e 5a 1c 5a 1c Enter
44 225 2a 2f 12 2a 12 2a LShift
46 29 2c 14 1a 2c 1a 2c Z
47 27 2d 13 22 2d 22 2d X
48 6 2e 06 21 2e 21 2e C
49 25 2f 5d 2a 2f 2a 2f V
50 5 30 69 32 30 32 30 B
51 17 31 31 31 31 31 31 N
52 16 32 30 3a 32 3a 32 M
53 54 33 23 41 33 41 33 , <
54 55 34 22 49 34 49 34 . >
55 56 35 15 4a 35 4a 35 / ?
57 229 36 07 59 36 59 36 RShift
58 224 1d 11 14 1d 11 38 LCtrl
60 226 38 6a 11 38 19 71 LAlt
61 44 39 72 29 39 29 39 space
62 230 e0-38 e0-6a e0-11 e0-38 39 72 RAlt
64 228 e0-1d e0-11 e0-14 e0-1d 58 3a RCtrl
75 73 e0-52 e0-28 e0-70 e0-52 67 7b Insert
76 76 e0-53 e0-74 e0-71 e0-53 64 79 Delete
80 74 e0-47 e0-60 e0-6c e0-47 6e 7f Home
81 77 e0-4f e0-61 e0-69 e0-4f 65 7a End
85 75 e0-49 e0-34 e0-7d e0-49 6f 6f PgUp
86 78 e0-51 e0-73 e0-7a e0-51 6d 7e PgDn
79 80 e0-4b e0-26 e0-6b e0-4b 61 56 Left
83 82 e0-48 e0-6c e0-75 e0-48 63 78 Up
84 81 e0-50 e0-6d e0-72 e0-50 60 55 Down
89 79 e0-4d e0-19 e0-74 e0-4d 6a 7d Right
90 83 45 0b 77 45 76 01 NumLock
91 95 47 60 6c 47 6c 47 KP-7 / Home
92 92 4b 26 6b 4b 6b 4b KP-4 / Left
93 89 4f 61 69 4f 69 4f KP-1 / End
95 84 e0-35 e0-15 e0-4a e0-35 77 45 KP-/
96 96 48 6c 75 48 75 48 KP-8 / Up
97 93 4c 27 73 4c 73 4c KP-5
98 90 50 6d 72 50 72 50 KP-2 / Down
99 98 52 28 70 52 70 52 KP-0 / Ins
100 85 37 5e 7c 37 7e 46 KP-*
101 97 49 34 7d 49 7d 49 KP-9 / PgUp
102 94 4d 19 74 4d 74 4d KP-6 / Right
103 91 51 73 7a 51 7a 51 KP-3 / PgDn
104 99 53 74 71 53 71 53 KP-. / Del
105 86 4a 35 7b 4a 84 54 KP--
106 87 4e 0c 79 4e 7c 37 KP-+
108 88 e0-1c e0-1e e0-5a e0-1c 79 4e KP-Enter
110 41 01 43 76 01 08 64 Esc
112 58 3b 24 05 3b 07 58 F1
113 59 3c 16 06 3c 0f 59 F2
114 60 3d 08 04 3d 17 5a F3
115 61 3e 09 0c 3e 1f 5b F4
116 62 3f 5f 03 3f 27 5c F5
117 63 40 6b 0b 40 2f 5d F6
118 64 41 33 83 41 37 5e F7
119 65 42 25 0a 42 3f 5f F8
120 66 43 17 01 43 47 60 F9
121 67 44 18 09 44 4f 61 F10
122 68 57 6e 78 57 56 62 F11
123 69 58 3a 07 58 5e 63 F12
124 70 e0-37 e0-5e e0-7c e0-37 57 6e PrtScr
0 154 54 1a 84 54 57 6e Alt+SysRq
125 71 46 0a 7e 46 5f 76 ScrollLock
126 72 e1-1d-45 e1-11-0b e1-14-77 e1-1d-45 62 77 Pause
0 0 e0-46 e0-0a e0-7e e0-46 62 77 Ctrl+Break
0 227 e0-5b e0-1b e0-1f e0-5b 8b 8b LWin (USB: LGUI)
0 231 e0-5c e0-75 e0-27 e0-5c 8c 8c RWin (USB: RGUI)
0 0 e0-5d e0-2b e0-2f e0-5d 8d 8d Menu
0 0 e0-5f e0-76 e0-3f e0-5f 7f 54 Sleep
0 0 e0-5e e0-63 e0-37 e0-5e 00 ff Power
0 0 e0-63 e0-78 e0-5e e0-63 00 ff Wake

Logitech uses an e2 prefix for the codes sent by a pointing device integrated on the keyboard.