1 /*
2  *  Copyright (C) 2002-2010  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 /* $Id: bios_keyboard.cpp,v 1.36 2009-06-11 16:05:17 c2woody Exp $ */
20 
21 #include "dosbox.h"
22 #include "callback.h"
23 #include "mem.h"
24 #include "bios.h"
25 #include "keyboard.h"
26 #include "regs.h"
27 #include "inout.h"
28 #include "dos_inc.h"
29 #include "SDL.h"
30 
31 /* SDL by default treats numlock and scrolllock different from all other keys.
32  * In recent versions this can disabled by a environment variable which we set in sdlmain.cpp
33  * Define the following if this is the case */
34 #if SDL_VERSION_ATLEAST(1, 2, 14)
35 #define CAN_USE_LOCK 1
36 /* For lower versions of SDL we also use a slight hack to get the startup states of numclock and capslock right.
37  * The proper way is in the mapper, but the repeating key is an unwanted side effect for lower versions of SDL */
38 #endif
39 
40 static Bitu call_int16,call_irq1,call_irq6;
41 
42 /* Nice table from BOCHS i should feel bad for ripping this */
43 #define none 0
44 static struct {
45   Bit16u normal;
46   Bit16u shift;
47   Bit16u control;
48   Bit16u alt;
49   } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
50       {   none,   none,   none,   none },
51       { 0x011b, 0x011b, 0x011b, 0x01f0 }, /* escape */
52       { 0x0231, 0x0221,   none, 0x7800 }, /* 1! */
53       { 0x0332, 0x0340, 0x0300, 0x7900 }, /* 2@ */
54       { 0x0433, 0x0423,   none, 0x7a00 }, /* 3# */
55       { 0x0534, 0x0524,   none, 0x7b00 }, /* 4$ */
56       { 0x0635, 0x0625,   none, 0x7c00 }, /* 5% */
57       { 0x0736, 0x075e, 0x071e, 0x7d00 }, /* 6^ */
58       { 0x0837, 0x0826,   none, 0x7e00 }, /* 7& */
59       { 0x0938, 0x092a,   none, 0x7f00 }, /* 8* */
60       { 0x0a39, 0x0a28,   none, 0x8000 }, /* 9( */
61       { 0x0b30, 0x0b29,   none, 0x8100 }, /* 0) */
62       { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200 }, /* -_ */
63       { 0x0d3d, 0x0d2b,   none, 0x8300 }, /* =+ */
64       { 0x0e08, 0x0e08, 0x0e7f, 0x0ef0 }, /* backspace */
65       { 0x0f09, 0x0f00, 0x9400,   none }, /* tab */
66       { 0x1071, 0x1051, 0x1011, 0x1000 }, /* Q */
67       { 0x1177, 0x1157, 0x1117, 0x1100 }, /* W */
68       { 0x1265, 0x1245, 0x1205, 0x1200 }, /* E */
69       { 0x1372, 0x1352, 0x1312, 0x1300 }, /* R */
70       { 0x1474, 0x1454, 0x1414, 0x1400 }, /* T */
71       { 0x1579, 0x1559, 0x1519, 0x1500 }, /* Y */
72       { 0x1675, 0x1655, 0x1615, 0x1600 }, /* U */
73       { 0x1769, 0x1749, 0x1709, 0x1700 }, /* I */
74       { 0x186f, 0x184f, 0x180f, 0x1800 }, /* O */
75       { 0x1970, 0x1950, 0x1910, 0x1900 }, /* P */
76       { 0x1a5b, 0x1a7b, 0x1a1b, 0x1af0 }, /* [{ */
77       { 0x1b5d, 0x1b7d, 0x1b1d, 0x1bf0 }, /* ]} */
78       { 0x1c0d, 0x1c0d, 0x1c0a,   none }, /* Enter */
79       {   none,   none,   none,   none }, /* L Ctrl */
80       { 0x1e61, 0x1e41, 0x1e01, 0x1e00 }, /* A */
81       { 0x1f73, 0x1f53, 0x1f13, 0x1f00 }, /* S */
82       { 0x2064, 0x2044, 0x2004, 0x2000 }, /* D */
83       { 0x2166, 0x2146, 0x2106, 0x2100 }, /* F */
84       { 0x2267, 0x2247, 0x2207, 0x2200 }, /* G */
85       { 0x2368, 0x2348, 0x2308, 0x2300 }, /* H */
86       { 0x246a, 0x244a, 0x240a, 0x2400 }, /* J */
87       { 0x256b, 0x254b, 0x250b, 0x2500 }, /* K */
88       { 0x266c, 0x264c, 0x260c, 0x2600 }, /* L */
89       { 0x273b, 0x273a,   none, 0x27f0 }, /* ;: */
90       { 0x2827, 0x2822,   none, 0x28f0 }, /* '" */
91       { 0x2960, 0x297e,   none, 0x29f0 }, /* `~ */
92       {   none,   none,   none,   none }, /* L shift */
93       { 0x2b5c, 0x2b7c, 0x2b1c, 0x2bf0 }, /* |\ */
94       { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00 }, /* Z */
95       { 0x2d78, 0x2d58, 0x2d18, 0x2d00 }, /* X */
96       { 0x2e63, 0x2e43, 0x2e03, 0x2e00 }, /* C */
97       { 0x2f76, 0x2f56, 0x2f16, 0x2f00 }, /* V */
98       { 0x3062, 0x3042, 0x3002, 0x3000 }, /* B */
99       { 0x316e, 0x314e, 0x310e, 0x3100 }, /* N */
100       { 0x326d, 0x324d, 0x320d, 0x3200 }, /* M */
101       { 0x332c, 0x333c,   none, 0x33f0 }, /* ,< */
102       { 0x342e, 0x343e,   none, 0x34f0 }, /* .> */
103       { 0x352f, 0x353f,   none, 0x35f0 }, /* /? */
104       {   none,   none,   none,   none }, /* R Shift */
105       { 0x372a, 0x372a, 0x9600, 0x37f0 }, /* * */
106       {   none,   none,   none,   none }, /* L Alt */
107       { 0x3920, 0x3920, 0x3920, 0x3920 }, /* space */
108       {   none,   none,   none,   none }, /* caps lock */
109       { 0x3b00, 0x5400, 0x5e00, 0x6800 }, /* F1 */
110       { 0x3c00, 0x5500, 0x5f00, 0x6900 }, /* F2 */
111       { 0x3d00, 0x5600, 0x6000, 0x6a00 }, /* F3 */
112       { 0x3e00, 0x5700, 0x6100, 0x6b00 }, /* F4 */
113       { 0x3f00, 0x5800, 0x6200, 0x6c00 }, /* F5 */
114       { 0x4000, 0x5900, 0x6300, 0x6d00 }, /* F6 */
115       { 0x4100, 0x5a00, 0x6400, 0x6e00 }, /* F7 */
116       { 0x4200, 0x5b00, 0x6500, 0x6f00 }, /* F8 */
117       { 0x4300, 0x5c00, 0x6600, 0x7000 }, /* F9 */
118       { 0x4400, 0x5d00, 0x6700, 0x7100 }, /* F10 */
119       {   none,   none,   none,   none }, /* Num Lock */
120       {   none,   none,   none,   none }, /* Scroll Lock */
121       { 0x4700, 0x4737, 0x7700, 0x0007 }, /* 7 Home */
122       { 0x4800, 0x4838, 0x8d00, 0x0008 }, /* 8 UP */
123       { 0x4900, 0x4939, 0x8400, 0x0009 }, /* 9 PgUp */
124       { 0x4a2d, 0x4a2d, 0x8e00, 0x4af0 }, /* - */
125       { 0x4b00, 0x4b34, 0x7300, 0x0004 }, /* 4 Left */
126       { 0x4cf0, 0x4c35, 0x8f00, 0x0005 }, /* 5 */
127       { 0x4d00, 0x4d36, 0x7400, 0x0006 }, /* 6 Right */
128       { 0x4e2b, 0x4e2b, 0x9000, 0x4ef0 }, /* + */
129       { 0x4f00, 0x4f31, 0x7500, 0x0001 }, /* 1 End */
130       { 0x5000, 0x5032, 0x9100, 0x0002 }, /* 2 Down */
131       { 0x5100, 0x5133, 0x7600, 0x0003 }, /* 3 PgDn */
132       { 0x5200, 0x5230, 0x9200, 0x0000 }, /* 0 Ins */
133       { 0x5300, 0x532e, 0x9300,   none }, /* Del */
134       {   none,   none,   none,   none },
135       {   none,   none,   none,   none },
136       { 0x565c, 0x567c,   none,   none }, /* (102-key) */
137       { 0x8500, 0x8700, 0x8900, 0x8b00 }, /* F11 */
138       { 0x8600, 0x8800, 0x8a00, 0x8c00 }  /* F12 */
139       };
140 
BIOS_AddKeyToBuffer(Bit16u code)141 bool BIOS_AddKeyToBuffer(Bit16u code) {
142 	if (mem_readb(BIOS_KEYBOARD_FLAGS2)&8) return true;
143 	Bit16u start,end,head,tail,ttail;
144 	if (machine==MCH_PCJR) {
145 		/* should be done for cga and others as well, to be tested */
146 		start=0x1e;
147 		end=0x3e;
148 	} else {
149 		start=mem_readw(BIOS_KEYBOARD_BUFFER_START);
150 		end	 =mem_readw(BIOS_KEYBOARD_BUFFER_END);
151 	}
152 	head =mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
153 	tail =mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
154 	ttail=tail+2;
155 	if (ttail>=end) {
156 		ttail=start;
157 	}
158 	/* Check for buffer Full */
159 	//TODO Maybe beeeeeeep or something although that should happend when internal buffer is full
160 	if (ttail==head) return false;
161 	real_writew(0x40,tail,code);
162 	mem_writew(BIOS_KEYBOARD_BUFFER_TAIL,ttail);
163 	return true;
164 }
165 
add_key(Bit16u code)166 static void add_key(Bit16u code) {
167 	if (code!=0) BIOS_AddKeyToBuffer(code);
168 }
169 
get_key(Bit16u & code)170 static bool get_key(Bit16u &code) {
171 	Bit16u start,end,head,tail,thead;
172 	if (machine==MCH_PCJR) {
173 		/* should be done for cga and others as well, to be tested */
174 		start=0x1e;
175 		end=0x3e;
176 	} else {
177 		start=mem_readw(BIOS_KEYBOARD_BUFFER_START);
178 		end	 =mem_readw(BIOS_KEYBOARD_BUFFER_END);
179 	}
180 	head =mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
181 	tail =mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
182 
183 	if (head==tail) return false;
184 	thead=head+2;
185 	if (thead>=end) thead=start;
186 	mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,thead);
187 	code = real_readw(0x40,head);
188 	return true;
189 }
190 
check_key(Bit16u & code)191 static bool check_key(Bit16u &code) {
192 	Bit16u head,tail;
193 	head =mem_readw(BIOS_KEYBOARD_BUFFER_HEAD);
194 	tail =mem_readw(BIOS_KEYBOARD_BUFFER_TAIL);
195 	if (head==tail) return false;
196 	code = real_readw(0x40,head);
197 	return true;
198 }
199 
200 	/*	Flag Byte 1
201 		bit 7 =1 INSert active
202 		bit 6 =1 Caps Lock active
203 		bit 5 =1 Num Lock active
204 		bit 4 =1 Scroll Lock active
205 		bit 3 =1 either Alt pressed
206 		bit 2 =1 either Ctrl pressed
207 		bit 1 =1 Left Shift pressed
208 		bit 0 =1 Right Shift pressed
209 	*/
210 	/*	Flag Byte 2
211 		bit 7 =1 INSert pressed
212 		bit 6 =1 Caps Lock pressed
213 		bit 5 =1 Num Lock pressed
214 		bit 4 =1 Scroll Lock pressed
215 		bit 3 =1 Pause state active
216 		bit 2 =1 Sys Req pressed
217 		bit 1 =1 Left Alt pressed
218 		bit 0 =1 Left Ctrl pressed
219 	*/
220 	/*
221 		Keyboard status byte 3
222 		bit 7 =1 read-ID in progress
223 		bit 6 =1 last code read was first of two ID codes
224 		bit 5 =1 force Num Lock if read-ID and enhanced keyboard
225 		bit 4 =1 enhanced keyboard installed
226 		bit 3 =1 Right Alt pressed
227 		bit 2 =1 Right Ctrl pressed
228 		bit 1 =1 last code read was E0h
229 		bit 0 =1 last code read was E1h
230 	*/
231 
232 
233 /* the scancode is in reg_al */
IRQ1_Handler(void)234 static Bitu IRQ1_Handler(void) {
235 /* handling of the locks key is difficult as sdl only gives
236  * states for numlock capslock.
237  */
238 	Bitu scancode=reg_al;	/* Read the code */
239 
240 	Bit8u flags1,flags2,flags3,leds;
241 	flags1=mem_readb(BIOS_KEYBOARD_FLAGS1);
242 	flags2=mem_readb(BIOS_KEYBOARD_FLAGS2);
243 	flags3=mem_readb(BIOS_KEYBOARD_FLAGS3);
244 	leds  =mem_readb(BIOS_KEYBOARD_LEDS);
245 #ifdef CAN_USE_LOCK
246 	/* No hack anymore! */
247 #else
248 	flags2&=~(0x40+0x20);//remove numlock/capslock pressed (hack for sdl only reporting states)
249 #endif
250 	if (DOS_LayoutKey(scancode,flags1,flags2,flags3)) return CBRET_NONE;
251 //LOG_MSG("key input %d %d %d %d",scancode,flags1,flags2,flags3);
252 	switch (scancode) {
253 	/* First the hard ones  */
254 	case 0xfa:	/* ack. Do nothing for now */
255 		break;
256 	case 0xe1:	/* Extended key special. Only pause uses this */
257 		flags3 |=0x01;
258 		break;
259 	case 0xe0:						/* Extended key */
260 		flags3 |=0x02;
261 		break;
262 	case 0x1d:						/* Ctrl Pressed */
263 		if (!(flags3 &0x01)) {
264 			flags1 |=0x04;
265 			if (flags3 &0x02) flags3 |=0x04;
266 			else flags2 |=0x01;
267 		}	/* else it's part of the pause scancodes */
268 		break;
269 	case 0x9d:						/* Ctrl Released */
270 		if (!(flags3 &0x01)) {
271 			if (flags3 &0x02) flags3 &=~0x04;
272 			else flags2 &=~0x01;
273 			if( !( (flags3 &0x04) || (flags2 &0x01) ) ) flags1 &=~0x04;
274 		}
275 		break;
276 	case 0x2a:						/* Left Shift Pressed */
277 		flags1 |=0x02;
278 		break;
279 	case 0xaa:						/* Left Shift Released */
280 		flags1 &=~0x02;
281 		break;
282 	case 0x36:						/* Right Shift Pressed */
283 		flags1 |=0x01;
284 		break;
285 	case 0xb6:						/* Right Shift Released */
286 		flags1 &=~0x01;
287 		break;
288 	case 0x38:						/* Alt Pressed */
289 		flags1 |=0x08;
290 		if (flags3 &0x02) flags3 |=0x08;
291 		else flags2 |=0x02;
292 		break;
293 	case 0xb8:						/* Alt Released */
294 		if (flags3 &0x02) flags3 &= ~0x08;
295 		else flags2 &= ~0x02;
296 		if( !( (flags3 &0x08) || (flags2 &0x02) ) ) { /* Both alt released */
297 			flags1 &= ~0x08;
298 			Bit16u token =mem_readb(BIOS_KEYBOARD_TOKEN);
299 			if(token != 0){
300 				add_key(token);
301 				mem_writeb(BIOS_KEYBOARD_TOKEN,0);
302 			}
303 		}
304 		break;
305 
306 #ifdef CAN_USE_LOCK
307 	case 0x3a:flags2 |=0x40;break;//CAPSLOCK
308 	case 0xba:flags1 ^=0x40;flags2 &=~0x40;leds ^=0x04;break;
309 #else
310 	case 0x3a:flags2 |=0x40;flags1 |=0x40;leds |=0x04;break; //SDL gives only the state instead of the toggle					/* Caps Lock */
311 	case 0xba:flags1 &=~0x40;leds &=~0x04;break;
312 #endif
313 	case 0x45:
314 		if (flags3 &0x01) {
315 			/* last scancode of pause received; first remove 0xe1-prefix */
316 			flags3 &=~0x01;
317 			mem_writeb(BIOS_KEYBOARD_FLAGS3,flags3);
318 			if (flags2&1) {
319 				/* ctrl-pause (break), special handling needed:
320 				   add zero to the keyboard buffer, call int 0x1b which
321 				   sets ctrl-c flag which calls int 0x23 in certain dos
322 				   input/output functions;    not handled */
323 			} else if ((flags2&8)==0) {
324 				/* normal pause key, enter loop */
325 				mem_writeb(BIOS_KEYBOARD_FLAGS2,flags2|8);
326 				IO_Write(0x20,0x20);
327 				while (mem_readb(BIOS_KEYBOARD_FLAGS2)&8) CALLBACK_Idle();	// pause loop
328 				reg_ip+=5;	// skip out 20,20
329 				return CBRET_NONE;
330 			}
331 		} else {
332 			/* Num Lock */
333 #ifdef CAN_USE_LOCK
334 			flags2 |=0x20;
335 #else
336 			flags2 |=0x20;
337 			flags1 |=0x20;
338 			leds |=0x02;
339 #endif
340 		}
341 		break;
342 	case 0xc5:
343 		if (flags3 &0x01) {
344 			/* pause released */
345 			flags3 &=~0x01;
346 		} else {
347 #ifdef CAN_USE_LOCK
348 			flags1^=0x20;
349 			leds^=0x02;
350 			flags2&=~0x20;
351 #else
352 			/* Num Lock released */
353 			flags1 &=~0x20;
354 			leds &=~0x02;
355 #endif
356 		}
357 		break;
358 	case 0x46:flags2 |=0x10;break;				/* Scroll Lock SDL Seems to do this one fine (so break and make codes) */
359 	case 0xc6:flags1 ^=0x10;flags2 &=~0x10;leds ^=0x01;break;
360 //	case 0x52:flags2|=128;break;//See numpad					/* Insert */
361 	case 0xd2:
362 		if(flags3&0x02) { /* Maybe honour the insert on keypad as well */
363 			flags1^=0x80;
364 			flags2&=~0x80;
365 			break;
366 		} else {
367 			goto irq1_end;/*Normal release*/
368 		}
369 	case 0x47:		/* Numpad */
370 	case 0x48:
371 	case 0x49:
372 	case 0x4b:
373 	case 0x4c:
374 	case 0x4d:
375 	case 0x4f:
376 	case 0x50:
377 	case 0x51:
378 	case 0x52:
379 	case 0x53: /* del . Not entirely correct, but works fine */
380 		if(flags3 &0x02) {	/*extend key. e.g key above arrows or arrows*/
381 			if(scancode == 0x52) flags2 |=0x80; /* press insert */
382 			if(flags1 &0x08) {
383 				add_key(scan_to_scanascii[scancode].normal+0x5000);
384 			} else if (flags1 &0x04) {
385 				add_key((scan_to_scanascii[scancode].control&0xff00)|0xe0);
386 			} else if( ((flags1 &0x3) != 0) || ((flags1 &0x20) != 0) ) {
387 				add_key((scan_to_scanascii[scancode].shift&0xff00)|0xe0);
388 			} else add_key((scan_to_scanascii[scancode].normal&0xff00)|0xe0);
389 			break;
390 		}
391 		if(flags1 &0x08) {
392 			Bit8u token = mem_readb(BIOS_KEYBOARD_TOKEN);
393 			token = token*10 + (Bit8u)(scan_to_scanascii[scancode].alt&0xff);
394 			mem_writeb(BIOS_KEYBOARD_TOKEN,token);
395 		} else if (flags1 &0x04) {
396 			add_key(scan_to_scanascii[scancode].control);
397 		} else if( ((flags1 &0x3) != 0) || ((flags1 &0x20) != 0) ) {
398 			add_key(scan_to_scanascii[scancode].shift);
399 		} else add_key(scan_to_scanascii[scancode].normal);
400 		break;
401 
402 	default: /* Normal Key */
403 		Bit16u asciiscan;
404 		/* Now Handle the releasing of keys and see if they match up for a code */
405 		/* Handle the actual scancode */
406 		if (scancode & 0x80) goto irq1_end;
407 		if (scancode > MAX_SCAN_CODE) goto irq1_end;
408 		if (flags1 & 0x08) { 					/* Alt is being pressed */
409 			asciiscan=scan_to_scanascii[scancode].alt;
410 #if 0 /* old unicode support disabled*/
411 		} else if (ascii) {
412 			asciiscan=(scancode << 8) | ascii;
413 #endif
414 		} else if (flags1 & 0x04) {					/* Ctrl is being pressed */
415 			asciiscan=scan_to_scanascii[scancode].control;
416 		} else if (flags1 & 0x03) {					/* Either shift is being pressed */
417 			asciiscan=scan_to_scanascii[scancode].shift;
418 		} else {
419 			asciiscan=scan_to_scanascii[scancode].normal;
420 		}
421 		/* cancel shift is letter and capslock active */
422 		if(flags1&64) {
423 			if(flags1&3) {
424 				/*cancel shift */
425 				if(((asciiscan&0x00ff) >0x40) && ((asciiscan&0x00ff) <0x5b))
426 					asciiscan=scan_to_scanascii[scancode].normal;
427 			} else {
428 				/* add shift */
429 	   			if(((asciiscan&0x00ff) >0x60) && ((asciiscan&0x00ff) <0x7b))
430 					asciiscan=scan_to_scanascii[scancode].shift;
431 			}
432 		}
433 		if (flags3 &0x02) {
434 			/* extended key (numblock), return and slash need special handling */
435 			if (scancode==0x1c) {	/* return */
436 				if (flags1 &0x08) asciiscan=0xa600;
437 				else asciiscan=(asciiscan&0xff)|0xe000;
438 			} else if (scancode==0x35) {	/* slash */
439 				if (flags1 &0x08) asciiscan=0xa400;
440 				else if (flags1 &0x04) asciiscan=0x9500;
441 				else asciiscan=0xe02f;
442 			}
443 		}
444 		add_key(asciiscan);
445 		break;
446 	};
447 irq1_end:
448 	if(scancode !=0xe0) flags3 &=~0x02;									//Reset 0xE0 Flag
449 	mem_writeb(BIOS_KEYBOARD_FLAGS1,flags1);
450 	if ((scancode&0x80)==0) flags2&=0xf7;
451 	mem_writeb(BIOS_KEYBOARD_FLAGS2,flags2);
452 	mem_writeb(BIOS_KEYBOARD_FLAGS3,flags3);
453 	mem_writeb(BIOS_KEYBOARD_LEDS,leds);
454 /*	IO_Write(0x20,0x20); moved out of handler to be virtualizable */
455 #if 0
456 /* Signal the keyboard for next code */
457 /* In dosbox port 60 reads do this as well */
458 	Bit8u old61=IO_Read(0x61);
459 	IO_Write(0x61,old61 | 128);
460 	IO_Write(0x64,0xae);
461 #endif
462 	return CBRET_NONE;
463 }
464 
465 
466 /* check whether key combination is enhanced or not,
467    translate key if necessary */
IsEnhancedKey(Bit16u & key)468 static bool IsEnhancedKey(Bit16u &key) {
469 	/* test for special keys (return and slash on numblock) */
470 	if ((key>>8)==0xe0) {
471 		if (((key&0xff)==0x0a) || ((key&0xff)==0x0d)) {
472 			/* key is return on the numblock */
473 			key=(key&0xff)|0x1c00;
474 		} else {
475 			/* key is slash on the numblock */
476 			key=(key&0xff)|0x3500;
477 		}
478 		/* both keys are not considered enhanced keys */
479 		return false;
480 	} else if (((key>>8)>0x84) || (((key&0xff)==0xf0) && (key>>8))) {
481 		/* key is enhanced key (either scancode part>0x84 or
482 		   specially-marked keyboard combination, low part==0xf0) */
483 		return true;
484 	}
485 	/* convert key if necessary (extended keys) */
486 	if ((key>>8) && ((key&0xff)==0xe0))  {
487 		key&=0xff00;
488 	}
489 	return false;
490 }
491 
INT16_Handler(void)492 static Bitu INT16_Handler(void) {
493 	Bit16u temp=0;
494 	switch (reg_ah) {
495 	case 0x00: /* GET KEYSTROKE */
496 		if ((get_key(temp)) && (!IsEnhancedKey(temp))) {
497 			/* normal key found, return translated key in ax */
498 			reg_ax=temp;
499 		} else {
500 			/* enter small idle loop to allow for irqs to happen */
501 			reg_ip+=1;
502 		}
503 		break;
504 	case 0x10: /* GET KEYSTROKE (enhanced keyboards only) */
505 		if (get_key(temp)) {
506 			if (((temp&0xff)==0xf0) && (temp>>8)) {
507 				/* special enhanced key, clear low part before returning key */
508 				temp&=0xff00;
509 			}
510 			reg_ax=temp;
511 		} else {
512 			/* enter small idle loop to allow for irqs to happen */
513 			reg_ip+=1;
514 		}
515 		break;
516 	case 0x01: /* CHECK FOR KEYSTROKE */
517 		// enable interrupt-flag after IRET of this int16
518 		mem_writew(SegPhys(ss)+reg_sp+4,(mem_readw(SegPhys(ss)+reg_sp+4) | FLAG_IF));
519 		for (;;) {
520 			if (check_key(temp)) {
521 				if (!IsEnhancedKey(temp)) {
522 					/* normal key, return translated key in ax */
523 					CALLBACK_SZF(false);
524 					reg_ax=temp;
525 					break;
526 				} else {
527 					/* remove enhanced key from buffer and ignore it */
528 					get_key(temp);
529 				}
530 			} else {
531 				/* no key available */
532 				CALLBACK_SZF(true);
533 				break;
534 			}
535 //			CALLBACK_Idle();
536 		}
537 		break;
538 	case 0x11: /* CHECK FOR KEYSTROKE (enhanced keyboards only) */
539 		if (!check_key(temp)) {
540 			CALLBACK_SZF(true);
541 		} else {
542 			CALLBACK_SZF(false);
543 			if (((temp&0xff)==0xf0) && (temp>>8)) {
544 				/* special enhanced key, clear low part before returning key */
545 				temp&=0xff00;
546 			}
547 			reg_ax=temp;
548 		}
549 		break;
550 	case 0x02:	/* GET SHIFT FlAGS */
551 		reg_al=mem_readb(BIOS_KEYBOARD_FLAGS1);
552 		break;
553 	case 0x03:	/* SET TYPEMATIC RATE AND DELAY */
554 		if (reg_al == 0x00) { // set default delay and rate
555 			IO_Write(0x60,0xf3);
556 			IO_Write(0x60,0x20); // 500 msec delay, 30 cps
557 		} else if (reg_al == 0x05) { // set repeat rate and delay
558 			IO_Write(0x60,0xf3);
559 			IO_Write(0x60,(reg_bh&3)<<5|(reg_bl&0x1f));
560 		} else {
561 			LOG(LOG_BIOS,LOG_ERROR)("INT16:Unhandled Typematic Rate Call %2X BX=%X",reg_al,reg_bx);
562 		}
563 		break;
564 	case 0x05:	/* STORE KEYSTROKE IN KEYBOARD BUFFER */
565 		if (BIOS_AddKeyToBuffer(reg_cx)) reg_al=0;
566 		else reg_al=1;
567 		break;
568 	case 0x12: /* GET EXTENDED SHIFT STATES */
569 		reg_al=mem_readb(BIOS_KEYBOARD_FLAGS1);
570 		reg_ah=mem_readb(BIOS_KEYBOARD_FLAGS2);
571 		break;
572 	case 0x55:
573 		/* Weird call used by some dos apps */
574 		LOG(LOG_BIOS,LOG_NORMAL)("INT16:55:Word TSR compatible call");
575 		break;
576 	default:
577 		LOG(LOG_BIOS,LOG_ERROR)("INT16:Unhandled call %02X",reg_ah);
578 		break;
579 
580 	};
581 
582 	return CBRET_NONE;
583 }
584 
585 //Keyboard initialisation. src/gui/sdlmain.cpp
586 extern bool startup_state_numlock;
587 extern bool startup_state_capslock;
588 
InitBiosSegment(void)589 static void InitBiosSegment(void) {
590 	/* Setup the variables for keyboard in the bios data segment */
591 	mem_writew(BIOS_KEYBOARD_BUFFER_START,0x1e);
592 	mem_writew(BIOS_KEYBOARD_BUFFER_END,0x3e);
593 	mem_writew(BIOS_KEYBOARD_BUFFER_HEAD,0x1e);
594 	mem_writew(BIOS_KEYBOARD_BUFFER_TAIL,0x1e);
595 	Bit8u flag1 = 0;
596 	Bit8u leds = 16; /* Ack recieved */
597 
598 #if SDL_VERSION_ATLEAST(1, 2, 14)
599 //Nothing, mapper handles all.
600 #else
601 	if (startup_state_capslock) { flag1|=0x40; leds|=0x04;}
602 	if (startup_state_numlock)  { flag1|=0x20; leds|=0x02;}
603 #endif
604 
605 	mem_writeb(BIOS_KEYBOARD_FLAGS1,flag1);
606 	mem_writeb(BIOS_KEYBOARD_FLAGS2,0);
607 	mem_writeb(BIOS_KEYBOARD_FLAGS3,16); /* Enhanced keyboard installed */
608 	mem_writeb(BIOS_KEYBOARD_TOKEN,0);
609 	mem_writeb(BIOS_KEYBOARD_LEDS,leds);
610 }
611 
BIOS_SetupKeyboard(void)612 void BIOS_SetupKeyboard(void) {
613 	/* Init the variables */
614 	InitBiosSegment();
615 
616 	/* Allocate/setup a callback for int 0x16 and for standard IRQ 1 handler */
617 	call_int16=CALLBACK_Allocate();
618 	CALLBACK_Setup(call_int16,&INT16_Handler,CB_INT16,"Keyboard");
619 	RealSetVec(0x16,CALLBACK_RealPointer(call_int16));
620 
621 	call_irq1=CALLBACK_Allocate();
622 	CALLBACK_Setup(call_irq1,&IRQ1_Handler,CB_IRQ1,Real2Phys(BIOS_DEFAULT_IRQ1_LOCATION),"IRQ 1 Keyboard");
623 	RealSetVec(0x09,BIOS_DEFAULT_IRQ1_LOCATION);
624 	// pseudocode for CB_IRQ1:
625 	//	push ax
626 	//	in al, 0x60
627 	//	mov ah, 0x4f
628 	//	stc
629 	//	int 15
630 	//	jc skip
631 	//	callback IRQ1_Handler
632 	//	label skip:
633 	//	cli
634 	//	mov al, 0x20
635 	//	out 0x20, al
636 	//	pop ax
637 	//	iret
638 
639 	if (machine==MCH_PCJR) {
640 		call_irq6=CALLBACK_Allocate();
641 		CALLBACK_Setup(call_irq6,NULL,CB_IRQ6_PCJR,"PCJr kb irq");
642 		RealSetVec(0x0e,CALLBACK_RealPointer(call_irq6));
643 		// pseudocode for CB_IRQ6_PCJR:
644 		//	push ax
645 		//	in al, 0x60
646 		//	cmp al, 0xe0
647 		//	je skip
648 		//	int 0x09
649 		//	label skip:
650 		//	cli
651 		//	mov al, 0x20
652 		//	out 0x20, al
653 		//	pop ax
654 		//	iret
655 	}
656 }
657 
658