forked from adriancable/8086tiny
-
Notifications
You must be signed in to change notification settings - Fork 4
/
bios.asm
4090 lines (3038 loc) · 75.2 KB
/
bios.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; BIOS source for 8086tiny IBM PC emulator (revision 1.21 and above). Compiles with NASM.
; Copyright 2013-14, Adrian Cable (adrian.cable@gmail.com) - http://www.megalith.co.uk/8086tiny
;
; Revision 1.62
;
; This work is licensed under the MIT License. See included LICENSE.TXT.
; Here we define macros for some custom instructions that help the emulator talk with the outside
; world. They are described in detail in the hint.html file, which forms part of the emulator
; distribution.
%macro extended_putchar_al 0
db 0x0f, 0x00
%endmacro
%macro extended_get_rtc 0
db 0x0f, 0x01
%endmacro
%macro extended_read_disk 0
db 0x0f, 0x02
%endmacro
%macro extended_write_disk 0
db 0x0f, 0x03
%endmacro
cpu 8086
org 100h ; BIOS loads at offset 0x0100
main:
jmp strict short bios_entry
; Here go pointers to the different data tables used for instruction decoding
dw rm_mode12_reg1 ; Table 0: R/M mode 1/2 "register 1" lookup
dw rm_mode012_reg2 ; Table 1: R/M mode 1/2 "register 2" lookup
dw rm_mode12_disp ; Table 2: R/M mode 1/2 "DISP multiplier" lookup
dw rm_mode12_dfseg ; Table 3: R/M mode 1/2 "default segment" lookup
dw rm_mode0_reg1 ; Table 4: R/M mode 0 "register 1" lookup
dw rm_mode012_reg2 ; Table 5: R/M mode 0 "register 2" lookup
dw rm_mode0_disp ; Table 6: R/M mode 0 "DISP multiplier" lookup
dw rm_mode0_dfseg ; Table 7: R/M mode 0 "default segment" lookup
dw xlat_ids ; Table 8: Translation of raw opcode index ("Raw ID") to function number ("Xlat'd ID")
dw ex_data ; Table 9: Translation of Raw ID to Extra Data
dw std_flags ; Table 10: How each Raw ID sets the flags (bit 1 = sets SZP, bit 2 = sets AF/OF for arithmetic, bit 3 = sets OF/CF for logic)
dw parity ; Table 11: Parity flag loop-up table (256 entries)
dw base_size ; Table 12: Translation of Raw ID to base instruction size (bytes)
dw i_w_adder ; Table 13: Translation of Raw ID to i_w size adder yes/no
dw i_mod_adder ; Table 14: Translation of Raw ID to i_mod size adder yes/no
dw jxx_dec_a ; Table 15: Jxx decode table A
dw jxx_dec_b ; Table 16: Jxx decode table B
dw jxx_dec_c ; Table 17: Jxx decode table C
dw jxx_dec_d ; Table 18: Jxx decode table D
dw flags_mult ; Table 19: FLAGS multipliers
biosstr db '8086tiny BIOS Revision 1.62!', 0, 0
bios_entry:
; Set up initial stack to F000:F000
mov di, 0xf000
mov ss, di
mov sp, di
push cs
pop es
push ax
; The emulator requires a few control registers in memory to always be zero for correct
; instruction decoding (in particular, register look-up operations). These are the
; emulator's zero segment (ZS) and always-zero flag (XF). Because the emulated memory
; space is uninitialised, we need to be sure these values are zero before doing anything
; else. The instructions we need to use to set them must not rely on look-up operations.
; So e.g. MOV to memory is out but string operations are fine.
cld
xor ax, ax
mov di, 24
stosw ; Set ZS = 0
mov di, 49
stosb ; Set XF = 0
; Now we can do whatever we want! DL starts off being the boot disk.
mov [cs:boot_device], dl
; Set up Hercules graphics support. We start with the adapter in text mode
push dx
mov dx, 0x3b8
mov al, 0
out dx, al ; Set Hercules support to text mode
mov dx, 0x3b4
mov al, 1 ; Hercules CRTC "horizontal displayed" register select
out dx, al
mov dx, 0x3b5
mov al, 0x2d ; 0x2D = 45 (* 16) = 720 pixels wide (GRAPHICS_X)
out dx, al
mov dx, 0x3b4
mov al, 6 ; Hercules CRTC "vertical displayed" register select
out dx, al
mov dx, 0x3b5
mov al, 0x57 ; 0x57 = 87 (* 4) = 348 pixels high (GRAPHICS_Y)
out dx, al
pop dx
pop ax
; Check cold boot/warm boot. We initialise disk parameters on cold boot only
cmp byte [cs:boot_state], 0 ; Cold boot?
jne boot
mov byte [cs:boot_state], 1 ; Set flag so next boot will be warm boot
; First, set up the disk subsystem. Only do this on the very first startup, when
; the emulator sets up the CX/AX registers with disk information.
; Compute the cylinder/head/sector count for the HD disk image, if present.
; Total number of sectors is in CX:AX, or 0 if there is no HD image. First,
; we put it in DX:CX.
mov dx, cx
mov cx, ax
mov [cs:hd_secs_hi], dx
mov [cs:hd_secs_lo], cx
cmp cx, 0
je maybe_no_hd
mov word [cs:num_disks], 2
jmp calc_hd
maybe_no_hd:
cmp dx, 0
je no_hd
mov word [cs:num_disks], 2
jmp calc_hd
no_hd:
mov word [cs:num_disks], 1
calc_hd:
mov ax, cx
mov word [cs:hd_max_track], 1
mov word [cs:hd_max_head], 1
cmp dx, 0 ; More than 63 total sectors? If so, we have more than 1 track.
ja sect_overflow
cmp ax, 63
ja sect_overflow
mov [cs:hd_max_sector], ax
jmp calc_heads
sect_overflow:
mov cx, 63 ; Calculate number of tracks
div cx
mov [cs:hd_max_track], ax
mov word [cs:hd_max_sector], 63
calc_heads:
mov dx, 0 ; More than 1024 tracks? If so, we have more than 1 head.
mov ax, [cs:hd_max_track]
cmp ax, 1024
ja track_overflow
jmp calc_end
track_overflow:
mov cx, 1024
div cx
mov [cs:hd_max_head], ax
mov word [cs:hd_max_track], 1024
calc_end:
; Convert number of tracks into maximum track (0-based) and then store in INT 41
; HD parameter table
mov ax, [cs:hd_max_head]
mov [cs:int41_max_heads], al
mov ax, [cs:hd_max_track]
mov [cs:int41_max_cyls], ax
mov ax, [cs:hd_max_sector]
mov [cs:int41_max_sect], al
dec word [cs:hd_max_track]
dec word [cs:hd_max_head]
; Main BIOS entry point. Zero the flags, and set up registers.
boot:
xor ax, ax
push ax
popf
push cs
push cs
pop ds
pop ss
mov sp, 0xf000
; Set up the IVT. First we zero out the table
cld
xor ax, ax
mov es, ax
xor di, di
mov cx, 512
rep stosw
; Then we load in the pointers to our interrupt handlers
mov di, 0
mov si, int_table
mov cx, itbl_size / 2
rep movsw
; Set pointer to INT 41 table for hard disk
mov cx, int41
mov word [es:4*0x41], cx
mov cx, 0xf000
mov word [es:4*0x41 + 2], cx
; Set up the BIOS data area
mov ax, 0x40
mov es, ax
mov di, 0
mov si, bios_data
mov cx, 0x100
rep movsb
; Clear video memory
mov ax, 0xb800
mov es, ax
mov di, 0
mov cx, 80*25
mov ax, 0x0700
rep stosw
; Clear video memory shadow buffer
mov ax, 0xc800
mov es, ax
mov di, 0
mov cx, 80*25
mov ax, 0x0700
rep stosw
; Set up some I/O ports, between 0 and FFF. Most of them we set to 0xFF, to indicate no device present
mov dx, 0x61
mov al, 0
out dx, al ; Make sure the speaker is off
mov dx, 0x60
out dx, al ; No scancode
mov dx, 0x64
out dx, al ; No key waiting
mov dx, 0
mov al, 0xFF
next_out:
inc dx
cmp dx, 0x40 ; We deal with the PIT channel 0 later
je next_out
cmp dx, 0x42 ; We deal with the PIT channel 2 later
je next_out
cmp dx, 0x3B8 ; We deal with the Hercules port later, too
je next_out
cmp dx, 0x60 ; Keyboard scancode
je next_out
cmp dx, 0x61 ; Sound output
je next_out
cmp dx, 0x64 ; Keyboard status
je next_out
out dx, al
cmp dx, 0xFFF
jl next_out
mov al, 0
mov dx, 0x3DA ; CGA refresh port
out dx, al
mov dx, 0x3BA ; Hercules detection port
out dx, al
mov dx, 0x3B8 ; Hercules video mode port
out dx, al
mov dx, 0x3BC ; LPT1
out dx, al
mov dx, 0x62 ; PPI - needed for memory parity checks
out dx, al
; Get initial RTC value
push cs
pop es
mov bx, timetable
extended_get_rtc
mov ax, [es:tm_msec]
mov [cs:last_int8_msec], ax
int 19h
; ************************* INT 19h = reboot
int19:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 7C00h
push ax
popf
; Read boot sector from FDD, and load it into 0:7C00
mov ax, 0x0201
mov dh, 0
mov dl, [cs:boot_device]
mov cx, 1
mov bx, sp
int 13h
jc .error_reading
cmp word [bx + 510], 0AA55h
jne .error_signature
; Jump to boot sector
push es
push bx
retf
.error_reading:
mov si, msg.error_reading
jmp .error
.error_signature:
mov si, msg.error_signature
.error:
push cs
pop ds
push si
mov si, msg.error_boot
call disp_msg
pop si
call disp_msg
xor ax, ax
int 13h
xor ax, ax
int 16h
int 19h
disp_msg.disp:
mov ah, 0Eh
mov bx, 7
push bp
int 10h
pop bp
disp_msg:
lodsb
test al, al
jnz .disp
retn
msg:
.error_boot: db "Error during boot!",13,10,0
.error_reading: db "Could not read from boot device.",13,10,0
.error_signature: db "Boot sector signature mismatch.",13,10,0
; ************************* INT 7h handler - keyboard driver (8086tiny internal)
int7: ; Whenever the user presses a key, INT 7 is called by the emulator.
; ASCII character of the keystroke is at 0040:this_keystroke
push ds
push es
push ax
push bx
push bp
push cs
pop ds
mov bx, 0x40 ; Set segment to BIOS data area segment (0x40)
mov es, bx
; Retrieve the keystroke
mov ax, [es:this_keystroke-bios_data]
mov byte [es:this_keystroke+1-bios_data], 0
real_key:
mov byte [cs:last_key_sdl], 0
test ah, 4 ; This key doesn't come from SDL
jz check_linux_bksp
mov byte [es:keyflags1-bios_data], 0
mov byte [es:keyflags2-bios_data], 0
mov byte [cs:last_key_sdl], 1 ; Key down from SDL
test ah, 0x40 ; Key up
jz sdl_check_specials
mov byte [cs:last_key_sdl], 2 ; Key up from SDL
sdl_check_specials:
mov bx, ax
and bh, 7 ; If key is between 52F and 534 (Shift/Ctrl/Alt), ignore the key state flags
cmp bx, 0x52f
je sdl_just_press_shift
cmp bx, 0x530
je sdl_just_press_shift
cmp bx, 0x533
je sdl_just_press_alt
cmp bx, 0x534
je sdl_just_press_alt
cmp bx, 0x531
je sdl_just_press_ctrl
cmp bx, 0x532
je sdl_just_press_ctrl
jmp sdl_check_alt
sdl_just_press_shift:
mov al, 0x36 ; Shift
and ah, 0x40 ; Key up?
add al, ah
add al, ah
call io_key_available
jmp i2_dne
sdl_just_press_alt:
mov al, 0x38 ; Alt
and ah, 0x40 ; Key up?
add al, ah
add al, ah
call io_key_available
jmp i2_dne
sdl_just_press_ctrl:
mov al, 0x1d ; Ctrl
and ah, 0x40 ; Key up?
add al, ah
add al, ah
call io_key_available
jmp i2_dne
sdl_check_alt:
test ah, 8 ; Alt+something?
jz sdl_no_alt
add byte [es:keyflags1-bios_data], 8
add byte [es:keyflags2-bios_data], 2
sdl_no_alt:
test ah, 0x20 ; Ctrl+something?
jz sdl_no_ctrl
add byte [es:keyflags1-bios_data], 4
sdl_no_ctrl:
test ah, 0x10 ; Shift+something?
jz sdl_no_mods
add byte [es:keyflags1-bios_data], 1
sdl_no_mods:
and ah, 1 ; We have processed all SDL modifiers, so remove them
;cmp ax, 160 ; Alt+Space?
;jne next_sdl_alt_keys
;mov al, ' '
;mov byte [es:this_keystroke-bios_data], al
check_sdl_f_keys:
cmp ax, 0x125
ja i2_dne ; Unknown key
cmp ax, 0x11a
jb check_sdl_pgup_pgdn_keys
sub ax, 0xdf ; F1 - F10
cmp ax, 0x45
jb check_sdl_f_keys2
add ax, 0x12 ; F11 - F12
check_sdl_f_keys2:
mov bh, al
mov al, 0
jmp sdl_scancode_xlat_done
check_sdl_pgup_pgdn_keys:
cmp ax, 0x116
jb check_sdl_cursor_keys
cmp ax, 0x119
ja check_sdl_cursor_keys
sub ax, 0x116
mov bx, pgup_pgdn_xlt
cs xlat
mov bh, al
mov al, 0
jmp sdl_scancode_xlat_done
check_sdl_cursor_keys:
cmp ax, 0x111 ; SDL cursor keys
jb sdl_process_key ; No special handling for other keys yet
sub ax, 0x111
mov bx, unix_cursor_xlt
xlat ; Convert SDL cursor keys to scancode
mov bh, al
mov al, 0
mov byte [es:this_keystroke-bios_data], 0
jmp sdl_scancode_xlat_done
sdl_process_key:
cmp ax, 0x100
jae i2_dne ; Unsupported key
cmp al, 0x7f ; SDL 0x7F backspace? Convert to 0x08
jne sdl_process_key2
mov al, 8
sdl_process_key2:
push ax
mov bx, a2scan_tbl ; ASCII to scancode table
xlat
mov bh, al
pop ax ; Scancode in BH, keycode in AL
sdl_scancode_xlat_done:
add bh, 0x80 ; Key up scancode
cmp byte [cs:last_key_sdl], 2 ; Key up?
je sdl_not_in_buf
sub bh, 0x80 ; Key down scancode
sdl_key_down:
;;
;; BEGIN
;;
;; AndreiW 07-10-2018: handle ALT+key correctly,
;; without emitting key itself
;;
test byte [es:keyflags1-bios_data], 8
jz save_keystroke
mov al, 0
save_keystroke:
mov [es:this_keystroke-bios_data], al
;;
;; END
;;
sdl_not_in_buf:
mov al, bh
call io_key_available
jmp i2_dne
check_linux_bksp:
cmp al, 0 ; Null keystroke - ignore
je i2_dne
cmp al, 0x7f ; Linux code for backspace - change to 8
jne after_check_bksp
mov al, 8
mov byte [es:this_keystroke-bios_data], 8
after_check_bksp:
cmp byte [es:next_key_fn-bios_data], 1 ; If previous keypress was Ctrl+F (signifying this key is is Fxx), skip checks for Ctrl+A (Alt+xx) and Ctrl+F (Fxx)
je i2_n
cmp al, 0x01 ; Ctrl+A pressed - this is the sequence for "next key is Alt+"
jne i2_not_alt
mov byte [es:keyflags1-bios_data], 8 ; Alt flag down
mov byte [es:keyflags2-bios_data], 2 ; Alt flag down
mov al, 0x38 ; Simulated Alt by Ctrl+A prefix?
call io_key_available
mov byte [es:next_key_alt-bios_data], 1
jmp i2_dne
i2_not_alt:
cmp al, 0x06 ; Ctrl+F pressed - this is the sequence for "next key is Fxx"
jne i2_not_fn
mov byte [es:next_key_fn-bios_data], 1
jmp i2_dne
i2_not_fn:
cmp byte [es:notranslate_flg-bios_data], 1 ; If no translation mode is on, just pass through the scan code. ASCII key is zero.
mov byte [es:notranslate_flg-bios_data], 0
jne need_to_translate
mov byte [es:this_keystroke-bios_data], 0
jmp after_translate
need_to_translate:
cmp al, 0xe0 ; Some OSes return scan codes after 0xE0 for things like cursor moves. So, if we find it, set a flag saying the next code received should not be translated.
mov byte [es:notranslate_flg-bios_data], 1
je i2_dne ; Don't add the 0xE0 to the keyboard buffer
mov byte [es:notranslate_flg-bios_data], 0
cmp al, 0x1b ; ESC key pressed. Either this a "real" escape, or it is UNIX cursor keys. In either case, we do nothing now, except set a flag
jne i2_escnext
; If the last key pressed was ESC, then we need to stuff it
cmp byte [es:escape_flag-bios_data], 1
jne i2_sf
; Stuff an ESC character
mov byte [es:this_keystroke-bios_data], 0x1b
mov al, 0x01
call keypress_release
i2_sf:
mov byte [es:escape_flag-bios_data], 1
jmp i2_dne
i2_escnext:
; Check if the last key was an escape character
cmp byte [es:escape_flag-bios_data], 1
jne i2_noesc
; It is, so check if this key is a [ control character
cmp al, '[' ; [ key pressed
je i2_esc
; It isn't, so stuff an ESC character plus this key
mov byte [es:this_keystroke-bios_data], 0x1b
mov al, 0x01
call keypress_release
; Now actually process this key
mov byte [es:escape_flag-bios_data], 0
mov al, [es:this_keystroke-bios_data]
jmp i2_noesc
i2_esc:
; Last + this characters are ESC ] - do nothing now, but set escape flag
mov byte [es:escape_flag-bios_data], 2
jmp i2_dne
i2_noesc:
cmp byte [es:escape_flag-bios_data], 2
jne i2_regular_key
; No shifts or Alt for cursor keys
mov byte [es:keyflags1-bios_data], 0
mov byte [es:keyflags2-bios_data], 0
; Last + this characters are ESC ] xxx - cursor key, so translate and stuff it
sub al, 'A'
mov bx, unix_cursor_xlt
xlat
mov byte [es:this_keystroke-bios_data], 0
jmp after_translate
i2_regular_key:
mov byte [es:notranslate_flg-bios_data], 0
mov bx, a2shift_tbl ; ASCII to shift code table
xlat
; Now, BL is 1 if shift is down, 0 otherwise. If shift is down, put a shift down scan code
; in port 0x60. Then call int 9. Otherwise, put a shift up scan code in, and call int 9.
push ax
; Put shift flags in BIOS, 0040:0017. Add 8 to shift flags if Alt is down.
mov ah, [es:next_key_alt-bios_data]
cpu 186
shl ah, 3
cpu 8086
add al, ah
cmp byte [es:this_keystroke-bios_data], 0x1A ; Ctrl+A to Ctrl+Z? Then add Ctrl to BIOS key flags
ja i2_no_ctrl
cmp byte [es:this_keystroke-bios_data], 0
je i2_no_ctrl
cmp byte [es:this_keystroke-bios_data], 0xD ; CR
je i2_no_ctrl
cmp byte [es:this_keystroke-bios_data], 0xA ; LF
je i2_no_ctrl
cmp byte [es:this_keystroke-bios_data], 0x8 ; Backspace
je i2_no_ctrl
cmp byte [es:this_keystroke-bios_data], 0x9 ; Tab
je i2_no_ctrl
add al, 4 ; Ctrl in key flags
push ax
mov al, 0x1d ; Ctrl key down
call io_key_available
pop ax
i2_no_ctrl:
mov [es:keyflags1-bios_data], al
cpu 186
shr ah, 2
cpu 8086
mov [es:keyflags2-bios_data], ah
pop ax
test al, 1 ; Shift down?
jz i2_n
mov al, 0x36 ; Right shift down
call io_key_available
i2_n:
mov al, [es:this_keystroke-bios_data]
mov bx, a2scan_tbl ; ASCII to scan code table
xlat
cmp byte [es:next_key_fn-bios_data], 1 ; Fxx?
jne after_translate
cmp byte [es:this_keystroke-bios_data], 1 ; Ctrl+F then Ctrl+A outputs code for Ctrl+A
je after_translate
cmp byte [es:this_keystroke-bios_data], 6 ; Ctrl+F then Ctrl+F outputs code for Ctrl+F
je after_translate
mov byte [es:this_keystroke-bios_data], 0 ; Fxx key, so zero out ASCII code
add al, 0x39
after_translate:
mov byte [es:escape_flag-bios_data], 0
mov byte [es:escape_flag_last-bios_data], 0
; If the key is actually an Alt+ key we use an ASCII code of 0 instead of the real value.
cmp byte [es:next_key_alt-bios_data], 1
jne skip_ascii_zero
mov byte [es:this_keystroke-bios_data], 0
skip_ascii_zero:
; Output key down/key up event (scancode in AL) to keyboard port
call keypress_release
; If scan code is not 0xE0, then also send right shift up if necessary
cmp al, 0xe0
je i2_dne
test byte [es:keyflags1-bios_data], 1
jz check_ctrl
mov al, 0xb6 ; Right shift up
call io_key_available
check_ctrl:
test byte [es:keyflags1-bios_data], 4
jz check_alt
mov al, 0x9d ; Right Ctrl up
call io_key_available
check_alt:
mov al, byte [es:next_key_alt-bios_data]
mov byte [es:next_key_alt-bios_data], 0
mov byte [es:next_key_fn-bios_data], 0
cmp al, 1
je endalt
jmp i2_dne
endalt:
mov al, 0xb8 ; Left Alt up
call io_key_available
i2_dne:
pop bp
pop bx
pop ax
pop es
pop ds
iret
; ************************* INT 9h handler - keyboard (PC BIOS standard)
int9:
push es
push ax
push bx
push bp
in al, 0x60
cmp al, 0x80 ; Key up?
jae no_add_buf
cmp al, 0x36 ; Shift?
je no_add_buf
cmp al, 0x38 ; Alt?
je no_add_buf
cmp al, 0x1d ; Ctrl?
je no_add_buf
mov bx, 0x40
mov es, bx
mov bh, al
mov al, [es:this_keystroke-bios_data]
; Tail of the BIOS keyboard buffer goes in BP. This is where we add new keystrokes
mov bp, [es:kbbuf_tail-bios_data]
mov byte [es:bp], al ; ASCII code
mov byte [es:bp+1], bh ; Scan code
; ESC keystroke is in the buffer now
add word [es:kbbuf_tail-bios_data], 2
call kb_adjust_buf ; Wrap the tail around the head if the buffer gets too large
no_add_buf:
mov al, 1
out 0x64, al
pop bp
pop bx
pop ax
pop es
iret
; ************************* INT Ah handler - timer (8086tiny internal)
inta:
; 8086tiny called interrupt 0xA frequently, at a rate dependent on the speed of your computer.
; This interrupt handler scales down the call rate and calls INT 8 at 18.2 times per second,
; as per a real PC.
; See if there is an ESC waiting from a previous INT 7h. If so, put it in the keyboard buffer
; (because by now - 1/18.2 secs on - we know it can't be part of an escape key sequence).
; Also handle CGA refresh register. Also release any keys that are still marked as down.
push ax
push bx
push dx
push bp
push es
push cx
push di
push ds
push si
call vmem_driver_entry ; CGA text mode driver - documented later
; Increment 32-bit BIOS timer tick counter, once every 18.2 ms
push cs
pop es
mov bx, timetable
extended_get_rtc
mov ax, [cs:tm_msec]
sub ax, [cs:last_int8_msec]
make_ctr_positive:
cmp ax, 0
jge no_add_1000
add ax, 1000
jmp make_ctr_positive
no_add_1000:
mov bx, 0x40
mov es, bx
mov dx, 0
mov bx, 1193
mul bx
mov bx, [es:timer0_freq-bios_data]
cmp bx, 0 ; 0 actually means FFFF
jne no_adjust_10000
mov bx, 0xffff
no_adjust_10000:
div bx ; AX now contains number of timer ticks since last int 8 (DX is remainder)
cmp ax, 0
je i8_end
add word [es:0x6C], ax
adc word [es:0x6E], 0
inta_call_int8:
push ax ; Workaround for CPM-86 - INT 1C destroys AX!!
int 8
pop ax
dec ax
cmp ax, 0
jne inta_call_int8
mov ax, [cs:tm_msec]
mov [cs:last_int8_msec], ax
skip_timer_increment:
; If last key was from SDL, don't simulate key up events (SDL will do it for us)
cmp byte [cs:last_key_sdl], 0
jne i8_end
; See if we have any keys down. If so, release them