; This file is part of the Next186 SoC PC project
; http://opencores.org/project,next186

; Filename: BIOS_Next186.asm
; Description: Part of the Next186 SoC PC project, ROM BIOS code
; Version 1.0
; Creation date: Feb-Jun 2013

; Author: Nicolae Dumitrache 
; e-mail: ndumitrache@opencores.org

; -------------------------------------------------------------------------------------
 
; Copyright (C) 2013 Nicolae Dumitrache
 
; This source file may be used and distributed without 
; restriction provided that this copyright statement is not 
; removed from the file and that any derivative work contains 
; the original copyright notice and the associated disclaimer.
 
; This source file is free software; you can redistribute it 
; and/or modify it under the terms of the GNU Lesser General 
; Public License as published by the Free Software Foundation;
; either version 2.1 of the License, or (at your option) any 
; later version. 
 
; This source is distributed in the hope that it will be 
; useful, but WITHOUT ANY WARRANTY; without even the implied 
; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
; PURPOSE. See the GNU Lesser General Public License for more 
; details. 
 
; You should have received a copy of the GNU Lesser General 
; Public License along with this source; if not, download it 
; from http://www.opencores.org/lgpl.shtml 
 
; -----------------------------------------------------------------------

; Additional Comments: 
; Assembled with MASM v6.14.8444
; Next186 SoC PC have no ROM, only RAM. The bootstrap code is the initial value of cache 
;  (last half 1K = 4 lines of 256bytes each), initially marked as "dirty", in order to
;  be saved in RAM at first flush
; The bootstrap code may load the BIOS from SD, or from RS232, and place it at F000:E000



.186
.model tiny
.code

SCANCODE1   equ 1

;-------------------------- BIOS data area (BDA) -----------------
;40:0000   2  Base port address of first RS-232 adapter (COM1) See COM Ports
;40:0002   2  Port of COM2
;40:0004   2  Port of COM3
;40:0006   2  Port of COM4
;40:0008   2  Base port addr of first parallel printer (LPT1)  Printer Ports
;40:000A   2  Port of LPT2
;40:000C   2  Port of LPT3
;40:000E   2  Port of LPT4
;40:0010   2  Equipment/hardware installed/active; see Equipment List
;40:0012   1  Errors in PCjr infrared keyboard link
;40:0013   2  Total memory in K-bytes (same as obtained via INT 12H)
;40:0015   2  Scratch pad for manufacturing error tests
;
;40:0017   2  Keyboard status bits; see Keyboard Shift Status Flags
;40:0019   1  Current (accumulating) value of Alt+numpad pseudo-key input;
;             normally 0.  When [Alt] is released, value is stored in
;             keyboard buffer at 001e.
;40:001a   2  Addr of keyboard buffer head (keystroke at that addr is next)
;40:001c   2  Address of keyboard buffer tail
;40:001e  32  Keyboard buffer.  BIOS stores keystrokes here (head and tail
;             point to addresses from 041eH to 043dH inclusive).
;
;40:003e   1  Diskette drive needs recalibration (bit 0=A, bit 1=B, etc.)
;             bits 4-5 indicate which drive is currently selected
;40:003f   1  Diskette motor is running (bit 0=drive A, bit 1=B, etc.)
;40:0040   1  Time until motor off. INT 08H turns motor off when this is 0.
;40:0041   1  Diskette error status; same as status returned by INT 13H
;40:0042   7  Diskette controller status information area
;
;40:0049   1  Current active video mode.  See Video Modes and INT 10H.
;40:004a   2  Screen width in text columns
;40:004c   2  Length (in bytes) of video area (regen size)
;40:004e   2  Offset from video segment of active video memory page
;40:0050  16  Cursor location (8 byte-pairs; low byte=clm, hi byte=row)
;40:0060   2  Cursor size/shape.  Low byte=end scan line; hi byte=start line.
;40:0062   1  Current active video page number
;40:0063   2  Port address for 6845 video controller chip; see CGA I/O Ports
;40:0065   1  Current value of 6845 video ctrlr CRT_MODE (port 3x8H register)
;40:0066   1  Current value of 6845 video ctrlr CRT_PALETTE (port 3x9H reg)
;
;40:0067   5  Cassette data area or POST data area
;               40:0067: 1 byte mouse buffer counter (DataCounter)
;               40:0068: 1 byte mouse packet size (PacketSize): 0 for 3 bytes, 1 for 4 bytes (Intellimouse)
;				40:0069: 2 bytes palette offset during set video mode	
;
;40:006c   4  Timer tick counter (count of 55ms ticks since CPU reset)
;40:0070   1  Timer overflow flag (timer has rolled over 24 hr)
;40:0071   1  Ctrl-Break flag.  Bit 7=1 when break was pressed.  This never
;             gets reset unless you do it yourself.
;
;40:0072   2  1234H means Ctrl+Alt+Del reboot is in progress.  BIOS checks
;             this to avoid doing a "cold boot" with the time-consuming POST
;             4321H means reset, preserving memory
;             5678H, 9abcH, and abcdH (are internal PC Convertible codes)
;
;40:0074   4  PCjr diskette or AT hard disk control area
;  (0074)   1 Status of last fixed-disk drive operation
;  (0075)   1 Number of hard disk drives for AT
;  (0077)   1 Hard disk port for XT.  See XT Hard Disk Ports.
;40:0078   4  Printer time-out values (478H=Lpt1, 478H=Lpt2...)
;40:007c   4  RS-232 time-out values  (47cH=Com1, 47dH=Com2...)
;
;40:0080   2  AT PS/2 keyboard buffer offset start address (usually 01eH)
;40:0082   2                                   end address (usually 003eH)
;
;40:0084   1  EGA text rows-1  (maximum valid row value)
;40:0085   2  EGA bytes per character (scan-lines/char used in active mode)
;40:0087   1  EGA flags; see EgaMiscInfoRec
;40:0088   1  EGA flags; see EgaMiscInfo2Rec
;40:0089   1  VGA flags; see VgaFlagsRec
;             See also:  EGA/VGA Data Areas
;
;40:008b   1  AT PS/2 Media control: data rate, step rate
;40:008c   1  AT PS/2 Hard disk drive controller status
;40:008d   1  AT PS/2 Hard disk drive error status
;40:008e   1  AT PS/2 Hard disk drive interrupt control
;
;40:0090   1  AT PS/2 Disk media state bits for drive 0
;40:0091   1                                for drive 1
;40:0092   1  AT PS/2 Disk operation started flag for drive 0
;40:0093   1                                      for drive 1

;40:0094   1  AT PS/2 Present cylinder number for drive 0
;40:0095   1                                  for drive 1
        ; 2 - Number of 512bytes sectors of HD0
;
;40:0096   1  AT Keyboard flag bit 4=1 (10H) if 101-key keyboard is attached
;40:0097   1  AT Keyboard flag for LED 'key lock' display
;             bits 0-2 are ScrollLock, NumLock, CapsLock
;
;40:0098   4  AT Pointer to 8-bit user wait flag; see INT 15H 86H
;40:009c   4  AT Microseconds before user wait is done
;40:00a0   1  AT User wait activity flag:
;                01H=busy, 80H=posted, 00H=acknowledged
;
;40:00a1   7  AT Reserved for network adapters
;               40:00a1: 4 bytes far pointer to mouse callback (HandlerPtr)
;               40:00a5: 3 bytes mouse buffer (DataBuffer)
;
;40:00a8   4  EGA Address of table of pointers; see EgaSavePtrRec
;40:00ac  68  Reserved
;40:00f0  16  (IAC) Inter-Aapplication Communication area.  Programs may use
;             this area to store status, etc.  Might get overwritten by
;             another program.

; http://www.ctyme.com/intr/int.htm

; video memory: 8 physical segments at 0a000h, 0b000h, 0c000h, 0d000h, 0e000h, 0f000h, 10000h, 11000h
; Memory segments mapping
; 1Mb virtual seg address   physical seg address
;       0000h                   0000h
;       1000h                   1000h
;       2000h                   2000h
;       3000h                   3000h
;       4000h                   4000h
;       5000h                   5000h
;       6000h                   6000h
;       7000h                   7000h
;       8000h                   8000h
;       9000h                   9000h
;       a000h                   a000h       - video
;       b000h                   b000h       - video
;       c000h                   12000h
;       d000h                   13000h
;       e000h                   14000h
;       f000h                   15000h


RAMSize   equ    80h        ; 64KB segments

; Graphics character set
font8x8		equ	bios - 800h        
font8x16	equ	font8x8 - 1000h    
font8x14	equ	font8x16 - 0e00h


		org 0e000h
bios:        
biosmsg     db 'Next186 PapilioPro SoC PC BIOS (C) 2018 Nicolae Dumitrache', 0
msgmb       db 'MB SD Card', 13, 10, 0
msgkb       db 'PS2 KB detected', 13, 10, 0

		org 0e05bh
coldboot:
warmboot:
        cli
        cld
        mov     ax, 30h
        mov     ss, ax
        mov     sp, 100h
        
        push    0
        popf
        
        mov     al, 36h
        out     43h, al
        xor     ax, ax
        out     7, ax       ; NMIonIORQ_HI = 0
        out     40h, al
        out     40h, al      ; 18Hz PIT CH0
        out     61h, al      ; speaker off
        not     al
        out     21h, al      ; disable all interrupts
		out		0a1h, al
        out     6, ax       ; NMIonIORQ_LO = 255 -> disabled
		

; ------------------ MAP init
        call    flush
        mov     ax, 15h     ; BIOS physical segment 15h mapped on virtual segment 0ch
        out     8ch, ax
        push    0c000h
        pop     es
        push    0f000h
        pop     ds
        xor     si, si
        xor     di, di
        mov     cx, 8000h
        rep     movsw       ; copy BIOS virtual segment 0fh over physical segment 15h

        call    flush
        mov     dx, 80h      
        xor     ax, ax
mapi:        
        out     dx, ax
        inc     ax
        inc     dx
        cmp     al, 0ch
        jne     short mapi1
        add     al, 6
mapi1:        
        cmp     al, 16h
        jne     short mapi
        
; -------------------- Interrupt table init
        push    0
        pop     ds
        push    ds
        pop     es
        xor     si, si
        mov     di, 4
        mov     word ptr [si], offset defint
        mov     word ptr [si+2], cs
        mov     cx, 256-2
        rep     movsw
        mov     word ptr ds:[7*4], offset int07
        mov     word ptr ds:[8*4], offset int08
        mov     word ptr ds:[9*4], offset int09
        mov     word ptr ds:[10h*4], offset int10        
        mov     word ptr ds:[11h*4], offset int11        
        mov     word ptr ds:[12h*4], offset int12        
        mov     word ptr ds:[13h*4], offset int13        
        mov     word ptr ds:[14h*4], offset int14        
        mov     word ptr ds:[15h*4], offset int15
        mov     word ptr ds:[16h*4], offset int16
        mov     word ptr ds:[18h*4], offset int18
        mov     word ptr ds:[19h*4], offset int19
        mov     word ptr ds:[1ah*4], offset int1a
        mov     word ptr ds:[70h*4], offset int70
        mov     word ptr ds:[74h*4], offset int74

; ------------------- BDA init
        push    40h
        pop     ds
        push    ds
        pop     es
        xor     di, di
        xor     si, si
        xor     ax, ax
        mov     cl, 80h
        rep     stosw
		mov		word ptr [si+00h], 3f8h	 ; COM1 base port address
		mov		word ptr [si+08h], 378h	 ; LPT1 base port address
        mov     byte ptr [si+10h], 24h   ; equipment word (color 80x25, PS2 mouse present)
        mov     word ptr [si+13h], 640   ; memory size in KB
        add     word ptr [si+1ah], 1eh   ; next char pointer in kb buffer
        add     word ptr [si+1ch], 1eh   ; last char pointer in kb buffer
        mov     word ptr [si+60h], 0e0fh ; cursor shape
        mov     word ptr [si+63h], 3d4h  ; video port address
        add     word ptr [si+80h], 1eh   ; start kb buffer
        add     word ptr [si+82h], 3eh   ; end kb buffer
        mov     word ptr [si+87h], 0940h ; video adapter options (512Kb video)
        mov     word ptr [si+89h], 0b71h ; VGA video flags: 400 line text mode, default palette loading on (0), blinking on
        mov     byte ptr [si+96h], 10h   ; 101 keyboard installed
 
; ------------------- Graph mode init
        mov     ax, 3
        int     10h

 ; ------------------- KB init ----------------
        mov     al, 0aeh
        out     64h, al     ; enable kb
        mov     al, 0a7h
        out     64h, al     ; disable mouse
        mov     cx, 25
kbi1:       
        call    getps2byte
        loop    short kbi1  ; wait for kb timeout
        mov     ah, 0ffh    ; reset kb
        clc                 ; kb command
        call    sendcmd   
        jc      short nokb
        mov     cl, 25
kbi2:        
        dec     cx
        jcxz    short nokb
        call    getps2byte
        jc      short kbi2  ; wait for BAT
        cmp     al, 0aah
        jne     short nokb
        mov     ah, 0f2h    ; kb id
        call    sendcmd     ; CF = 0
        jc      short nokb
        call    getps2byte
        cmp     al, 0abh
        jne     short nokb
        call    getps2byte
        cmp     al, 83h
; set scan code 1
IFDEF SCANCODE1
        jne     short nokb
        mov     ah, 0f0h    ; kb scan set
        call    sendcmd   
        jc      short nokb
        mov     ah, 1       ; scan set 1
        call    sendcmd   
        jnc     short kbok
ELSE
        je     short kbok
ENDIF        

nokb:   
        mov     byte ptr KbdFlags3, 0   ; kb not present
kbok:
        mov     al, 0adh
        out     64h, al      ; disable kb interface

; ------------------- Mouse init ----------------
        mov     al, 0a8h
        out     64h, al      ; enable mouse
mousei0:        
        call    getps2byte
        jnc     short mousei0
        mov     ah, 0ffh
        call    sendcmd      ; reset mouse (CF = 1)
        jc      short nomouse
        mov     cl, 25
mousei1:        
        dec     cx
        jcxz    short nomouse
        call    getps2byte
        jc      short mousei1
        cmp     al, 0aah     ; BAT
        jne     short nomouse
        call    getps2byte
        cmp     al, 0        ; mouse ID
        je      short mouseok
nomouse:
        mov     al, 0a7h        
        out     64h, al      ; disable mouse
        and     byte ptr EquipmentWord, not 4 ; ps2 mouse not present in equipement word
mouseok:
        call    enableKbIfPresent

        mov     al, 20h
        out     64h, al
        in      al, 60h
        or      al, 3
        mov     ah, al
        mov     al, 60h
        out     64h, al
        mov     al, ah
        out     60h, al     ; enable 8042 mouse and kb interrupts

		mov     ax, 0		; 1000-1   ; 1ms
		out     70h, ax     ; set RTC frequency (stop)

;		mov     al, 0
		out     21h, al     ; enable all PIC interrupts (8h, 9h, 0ch)
		out		0a1h, al	; enable all PIC interrupts (70h, 74h)
		out		1, al		; intialize COM mux
		inc		ax
		out		1, ax		; enable auto flush on vblank
		sti                 ; enable CPU interrupts

; ---------------------- COM flush
        mov     ah, 3       ; get serial port status
        xor     dx, dx      ; COM1
COMFlush:
        int     14h
        shr     ah, 1
        mov     ah, 2
        jc      short COMFlush

; ---------------------   HDD init
        call    sdinit
        mov     HDSize, ax
        push    cs
        pop     es
        mov     si, offset biosmsg
        call    prts
        mov     si, offset bioscont
        call    prts
        mov     ax, HDSize
        shr     ax, 1
        call    dispAX
        mov     si, offset msgmb
        call    prts
        test    byte ptr KbdFlags3, 10h
        jz      nokbmsg
        mov     si, offset msgkb
        call    prts
nokbmsg:
        test    byte ptr EquipmentWord, 4
        jz      nomousemsg
        mov     si, offset msgmouse
        call    prts
nomousemsg:

;-------------- HD bootstrap
		mov		ax, 00e3h
		int		14h		; init COM to 9.6K, 8n1
        mov     ax, 305h
        xor     bx, bx
        int     16h     ; set typematic rate and delay to fastest
        int     19h

msgmouse    db 'PS2 Mouse detected', 13, 10, 0        
bioscont    db 13, 10, 'CPU: 80186 47Mhz (47MIPS, 94Mhz 32bit bus)', 13, 10
            db 'RAM: 8MB SDR 144Mhz', 13, 10
            db 'Cache: 16KB, 4-way, 256x64 bytes data/inst', 13, 10
            db 'HD0: ', 0

; ---------------------------- INT 07 ---------------------
int07 proc near ; coprocessor ESC sequence
        push    ax
        push    bx
        push    ds
        push    bp
        mov     bp, sp
        lds     bx, [bp+8]  
int07_pfx:        
        mov     al, [bx]
        inc     bx
        and     al, 0f8h
        cmp     al, 0d8h        ; ESC code
        jne     short int07_pfx
              
        cmp     byte ptr [bx], 0c0h ; mod reg r/m of ESC 8087 instruction
        sbb     al, al
        and     al, [bx]
        and     ax, 0c7h
        cmp     al, 6
        jne     int072
        mov     al, 80h
int072:
        shr     al, 6
        inc     ax
        add     ax, bx
        mov     [bp+8], ax
        pop     bp
        pop     ds
        pop     bx
        pop     ax
        iret
int07 endp


; ---------------------------- INT 08 ---------------------
int08 proc near
        push    ds
        push    bx
        push    40h
        pop     ds
        mov     bx, 6ch
        add     word ptr [bx], 1
        adc     word ptr [bx+2], 0
        cmp     word ptr [bx+2], 18h
        jne     short int081
        cmp     word ptr [bx], 0b0h
        jne     short int081
        mov     word ptr [bx], 0
        mov     word ptr [bx+2], 0
        mov     byte ptr [bx+4], 1
int081:
        int     1ch
        sti
        push    ax
        mov     ah, 4
kloop:        
        in      al, 64h
        test    al, 1
        jz      short nokey
        dec     ah
        jnz     short kloop
        test    al, 20h
        jz      short kbdata
        int     74h
        jmp     short nokey
kbdata:
        int     9h        
nokey:
        pop     ax
        pop     bx
        pop     ds
        iret
int08 endp

; --------------------- INT 09 - keyboard ------------------
KbdFlags1       equ     <ds:[17h]>
KbdFlags2       equ     <ds:[18h]>
AltKpd          equ     <ds:[19h]>
CtrlBreak       equ     <ds:[71h]>
KbdFlags3       equ     <ds:[96h]>
KbdFlags4       equ     <ds:[97h]>

; Bits for the KbdFlags1
RShfDown        equ     1
LShfDown        equ     2
CtrlDown        equ     4
AltDown         equ     8
ScrLock         equ     10h
NumLock         equ     20h
CapsLock        equ     40h
Insert          equ     80h

; Bits for the KbdFlags2
LCtrDown        equ     1
LAltDown        equ     2
SysReqDown      equ     4
Pause           equ     8
ScrLockDown     equ     10h
NumLockDown     equ     20h
CapsLockDown    equ     40h
InsDown         equ     80h
 
; Bits for the KbdFlags3
LastE1          equ     1
LastE0          equ     2
RCtrDown        equ     4
RAltDown        equ     8
LastF0          equ     20h

; Bits for the KbdFlags4
ScrLockLED      equ     1
NumLockLED      equ     2
CapsLockLED     equ     4
SetRepeat       equ     8       ; Set auto repeat command in progress
AckReceived     equ     10h
LEDUpdate       equ     40h

IFDEF SCANCODE1

int09 proc near
        pusha
        push    ds
        push    es
        push    40h
        pop     ds
        in      al, 60h         ; al contains the scan code
        mov     dx, KbdFlags1
        mov     cx, KbdFlags3
        cmp     al, 0fah        ; ACK
        jne     short noACK
; ------------ manage ACK response
        test    ch, LEDUpdate
        jz      short ToggleACK ; no LED update
        test    ch, AckReceived
        jnz     short SecondACK ; second ACK received
        mov     ah, ch          ; LED update command sent, ACK received, need to send second byte
        and     ah, ScrLockLED or NumLockLED or CapsLockLED
        mov     bl, 0
        call    sendps2byte
        jmp     short ToggleACK
SecondACK:        
        xor     ch, LEDUpdate   ; second ACK, clear LED update bit
ToggleACK:
        xor     ch, AckReceived ; toggle ACK bit 
SetFlags1:                                  
        jmp     SetFlags               
        
; ------------ no ACK
noACK:
        mov     ah,4fh
        stc
        int     15h
        jnc     int09Exit
        cmp     al, 0e0h
        jne     short noE0
        or      cl, LastE0
        jmp     short SetFlags1
noE0:
        cmp     al, 0e1h
        jne     short noE1
        or      cl, LastE1
        jmp     short SetFlags1
noE1:   
        cmp     al, 53h     ; is DEL?
        jne     short noDEL
        mov     ah, dl
        and     ah, CtrlDown or AltDown
        cmp     ah, CtrlDown or AltDown
        jne     NormalKey   ; is DEL, but no CTRL+ALt+DEL
        mov     word ptr ds:[72h], 1234h    ; warm boot flag
        db      0eah
        dw      0, 0ffffh       ; reboot
noDEL:
        test    cl, LastE0
        jnz     short noRSUp    ; ignore fake shifts
        cmp     al, 2ah         ; left shift
        jne     short noLSDown
        or      dl, LShfDown
        jmp     short SetFlagsKey2
noLSDown:
        cmp     al, 2ah or 80h
        jne     short noLSUp
        and     dl, not LShfDown
        jmp     short SetFlagsKey2
noLSUp:
        cmp     al, 36h         ; right shift
        jne     short noRSDown
        or      dl, RShfDown
        jmp     short SetFlagsKey2
noRSDown:
        cmp     al, 36h or 80h
        jne     short noRSUP
        and     dl, not RShfDown
        jmp     short SetFlagsKey2
noRSUp:
        cmp     al, 38h         ; ALT
        jne     short noALTDown
        test    cl, LastE0
        jz      short LALTDn
        or      cl, RAltDown
        or      dl, AltDown
        jmp     short SetFlagsKey2
LALTDn:
        or      dx, (LAltDown shl 8) or AltDown
        jmp     short SetFlagsKey2
noALTDown:
        cmp     al, 38h or 80h
        jne     short noALTUp
        test    cl, LastE0
        jz      short LALTUp
        and     cl, not RAltDown
        and     dl, not AltDown
        jmp     short ALTup
LALTUp:
        and     dx, not ((LAltDown shl 8) or AltDown)
ALTUp:
        xor     ax, ax
        xchg    al, AltKpd
        test    al, al
        jz      short SetFlagsKey2     
        jmp     pushKey
noALTUp:
        cmp     al, 1dh         ; CTL
        jne     short noCTLDown
        test    cl, lastE0
        jz      short LCTLDn
        or      cl, RCtrDown
        or      dl, CtrlDown
SetFlagsKey2:        
        jmp     short SetFlagsKey1
LCTLDn:
        or      dx, (LCtrDown shl 8) or CtrlDown
        jmp     short SetFlagsKey1
noCTLDown:
        cmp     al, 1dh or 80h
        jne     short noCTLUp
        test    cl, LastE0
        jz      short LCTLUp
        and     cl, not RCtrDown
        and     dl, not CtrlDown
        jmp     short SetFlagsKey1
LCTLUp:
        and     dx,  not ((LCtrDown shl 8) or CtrlDown)
        jmp     short SetFlagsKey1
noCTLUp:
        mov     bx, 3a00h + CapsLock
        call    KeyLock
        jnc     short SetFlagsKey1
        
        mov     bx, 4600h + ScrLock
        push    dx          ; save ScrLock state bit (dl)
        call    KeyLock
        pop     bx          ; restore ScrLock state bit (bl)
        jc      short noScrLock
        test    dl, CtrlDown
        jz      short SetFlagsKey1; no break, just ScollLock
        mov     dl, bl      ; restore ScrLock flag
        test    bh, ScrLockDown
        jnz     short SetFlagsKey1 
        mov     byte ptr CtrlBreak, 80h   ; CTRL+BREAK flag
        mov     ax, Buffer
        mov     HeadPtr, ax
        mov     TailPtr, ax
        int     1bh
        xor     ax, ax
        jmp     pushkey
noScrLock:        
        test    cl, LastE0  ; INS
        jnz     short testINS
        test    dl, RShfDown or LShfDown
        jnz     short testINS
        test    dl, NumLock
        jnz     short NoIns      
testINS:
        mov     bx, 5200h + Insert
        call    KeyLock  
noIns:
        mov     bx, 4500h + NumLock
        push    dx          ; save NumLock state bit (dl)
        call    KeyLock
        pop     bx          ; restore NumLock state bit (bl)
        jc      short NormalKey   ; CTRL+NumLock = Pause
        test    dl, CtrlDown
        jz      short SetFlagsKey1
        mov     dl, bl      ; restore NumLock flag
        or      dh, Pause   ; set Pause bit
SetFlagsKey1:
        jmp     SetFlagsKey
E0Key:
        mov     di, offset E0KeyList
        push    cx
        mov     cx, E0KeyIndex - E0KeyList
        cld
        push    cs
        pop     es
        repne   scasb
        pop     cx
        jne     short SetFlagsKey
        mov     al, es:[di + E0KeyIndex - E0KeyList - 1]
        jmp     short KeyDown
NormalKey:
        test    al, 80h
        jnz     short SetFlagsKey ; key up
        test    cl, LastE0
        jnz     short E0Key
        cmp     al, 59h
        sbb     ah, ah
        and     al, ah
        mov     bx, offset KeyIndex
        xlat    cs:[bx]
KeyDown:
        xor     bx, bx 
        test    dl, RShfDown or LShfDown
        jz      short noShift
        mov     bl, 2
noShift:
        cmp     al, 26
        ja      short noCaps
        test    dl, CapsLock
        jz      short noNum
        xor     bl, 2
        jmp     short noNum 
noCaps:
        cmp     al, 37
        ja      short noNum
        test    dl, NumLock
        jnz     short NumDown
        mov     bl, 2
NumDown:
        xor     bl, 2
noNum:        
        test    dl, CtrlDown
        jz      short noCtrl
        mov     bl, 4
noCtrl:
        test    dl, AltDown
        jz      short noAlt
        mov     bl, 6
noAlt:
        cbw
        shl     ax, 3
        add     bx, ax
        mov     ax, cs:KeyCode[bx]
        cmp     ax, 000ah
        ja      short pushKey
        dec     ax
        js      short SetFlagsKey     ; ax was 0
        mov     ah, AltKpd
        aad
        mov     AltKpd, al
        jmp     short SetFlagsKey
pushKey:                
        push    cx
        mov     cx, ax
        mov     ah, 5
        int     16h
        pop     cx
        and     dh, not Pause    ; clear Pause bit
SetFlagsKey:
        and     cl, not (LastE0 or LastE1)    ; not prefix key code, clear all prefixes
SetFlags:
        mov     al, dl
        shr     al, 4
        xor     al, ch
        and     al, 7
        jz      short SF1   ; no LEDs to update
        test    ch, SetRepeat or AckReceived or LEDUpdate
        jnz     short SF1   ; can not update LEDS, so just write the flags and exit
        or      al, LEDUpdate
        xor     ch, al      ; insert the LEDs in KbdFlags4
        mov     ah, 0edh    ; set LED
        mov     bl, 0
        call    sendps2byte
SF1:        
        mov     KbdFlags1, dx
        mov     KbdFlags3, cx
        
int09Exit:
        pop     es
        pop     ds
        popa
        iret
int09 endp

ELSE    ; SCANCODE2

int09 proc near
        pusha
        push    ds
        push    es
        push    40h
        pop     ds
        in      al, 60h         ; al contains the scan code
        mov     dx, KbdFlags1
        mov     cx, KbdFlags3
        cmp     al, 0fah        ; ACK
        jne     short noACK
; ------------ manage ACK response
        test    ch, LEDUpdate
        jz      short ToggleACK ; no LED update
        test    ch, AckReceived
        jnz     short SecondACK ; second ACK received
        mov     ah, ch          ; LED update command sent, ACK received, need to send second byte
        and     ah, ScrLockLED or NumLockLED or CapsLockLED
        mov     bl, 0
        call    sendps2byte
        jmp     short ToggleACK
SecondACK:        
        xor     ch, LEDUpdate   ; second ACK, clear LED update bit
ToggleACK:
        xor     ch, AckReceived ; toggle ACK bit 
SetFlags1:                                  
        jmp     SetFlags               
        
; ------------ no ACK
noACK:
        cmp     al, 0e0h
        jne     short noE0
        or      cl, LastE0
        jmp     short SetFlags1
noE0:
        cmp     al, 0e1h
        jne     short noE1
        or      cl, LastE1
        jmp     short SetFlags1
noE1:   
        cmp     al, 0f0h
        jne     short noF0
        or      cl, LastF0
        jmp     short SetFlags1
noF0:   
        cmp     al, 71h     ; is DEL?
        jne     short noDEL
        mov     ah, dl
        and     ah, CtrlDown or AltDown
        cmp     ah, CtrlDown or AltDown
        je      short noF01
NormalKey1:        
        jmp     NormalKey
noF01:        
        mov     word ptr ds:[72h], 1234h    ; warm boot flag
        db      0eah
        dw      0, 0ffffh       ; reboot
noDEL:
        cmp     al, 83h         ; is F7
        je      short NormalKey1
        ja      short SetFlags1
        test    cl, LastF0      ; key up?
        jz      short noKeyUp
        or      al, 80h         ; key up flag
noKeyUp:
        test    cl, LastE0
        jnz     short noRSUp    ; ignore fake shifts      
        cmp     al, 12h         ; left shift
        jne     short noLSDown
        or      dl, LShfDown
        jmp     short SetFlagsKey2
noLSDown:
        cmp     al, 12h or 80h
        jne     short noLSUp
        and     dl, not LShfDown
        jmp     short SetFlagsKey2
noLSUp:
        cmp     al, 59h         ; right shift
        jne     short noRSDown
        or      dl, RShfDown
        jmp     short SetFlagsKey2
noRSDown:
        cmp     al, 59h or 80h
        jne     short noRSUP
        and     dl, not RShfDown
        jmp     short SetFlagsKey2
noRSUp:
        cmp     al, 11h         ; ALT
        jne     short noALTDown
        test    cl, LastE0
        jz      short LALTDn
        or      cl, RAltDown
        or      dl, AltDown
        jmp     short SetFlagsKey2
LALTDn:
        or      dx, (LAltDown shl 8) or AltDown
        jmp     short SetFlagsKey2
noALTDown:
        cmp     al, 11h or 80h
        jne     short noALTUp
        test    cl, LastE0
        jz      short LALTUp
        and     cl, not RAltDown
        and     dl, not AltDown
        jmp     short ALTup
LALTUp:
        and     dx, not ((LAltDown shl 8) or AltDown)
ALTUp:
        xor     ax, ax
        xchg    al, AltKpd
        test    al, al
        jz      short SetFlagsKey2     
        jmp     pushKey
noALTUp:
        cmp     al, 14h         ; CTL
        jne     short noCTLDown
        test    cl, lastE0
        jz      short LCTLDn
        or      cl, RCtrDown
        or      dl, CtrlDown
SetFlagsKey2:        
        jmp     short SetFlagsKey1
LCTLDn:
        or      dx, (LCtrDown shl 8) or CtrlDown
        jmp     short SetFlagsKey1
noCTLDown:
        cmp     al, 14h or 80h
        jne     short noCTLUp
        test    cl, LastE0
        jz      short LCTLUp
        and     cl, not RCtrDown
        and     dl, not CtrlDown
        jmp     short SetFlagsKey1
LCTLUp:
        and     dx,  not ((LCtrDown shl 8) or CtrlDown)
        jmp     short SetFlagsKey1
noCTLUp:
        mov     bx, 5800h + CapsLock
        call    KeyLock
        jnc     short SetFlagsKey1
        
        mov     bx, 7e00h + ScrLock
        push    dx          ; save ScrLock state bit (dl)
        call    KeyLock
        pop     bx          ; restore ScrLock state bit (bl)
        jc      short noScrLock
        test    dl, CtrlDown
        jz      short SetFlagsKey1; no break, just ScollLock
        mov     dl, bl      ; restore ScrLock flag
        test    bh, ScrLockDown
        jnz     short SetFlagsKey1 
        mov     byte ptr CtrlBreak, 80h   ; CTRL+BREAK flag
        mov     ax, Buffer
        mov     HeadPtr, ax
        mov     TailPtr, ax
        int     1bh
        xor     ax, ax
        jmp     pushkey
noScrLock:        
        test    cl, LastE0  ; INS
        jnz     short testINS
        test    dl, RShfDown or LShfDown
        jnz     short testINS
        test    dl, NumLock
        jnz     short NoIns      
testINS:
        mov     bx, 7000h + Insert
        call    KeyLock  
noIns:
        mov     bx, 7700h + NumLock
        push    dx          ; save NumLock state bit (dl)
        call    KeyLock
        pop     bx          ; restore NumLock state bit (bl)
        jc      short noPause
        test    dl, CtrlDown
        jz      short SetFlagsKey1
        mov     dl, bl      ; restore NumLock flag
        or      dh, Pause   ; set Pause bit
SetFlagsKey1:
        jmp     SetFlagsKey
E0Key:
        mov     di, offset E0KeyList
        push    cx
        mov     cx, E0KeyIndex - E0KeyList
        cld
        push    cs
        pop     es
        repne   scasb
        pop     cx
        jne     short SetFlagsKey
        mov     al, es:[di + E0KeyIndex - E0KeyList - 1]
        jmp     short KeyDown
noPause:
        and     al, 07fh    ; delete up bit
NormalKey:
        test    cl, LastF0
        jnz     short SetFlagsKey ; key up
        test    cl, LastE0
        jnz     short E0Key
        mov     bx, offset KeyIndex
        xlat    cs:[bx]
KeyDown:
        xor     bx, bx 
        test    dl, RShfDown or LShfDown
        jz      short noShift
        mov     bl, 2
noShift:
        cmp     al, 26
        ja      short noCaps
        test    dl, CapsLock
        jz      short noNum
        xor     bl, 2
        jmp     short noNum 
noCaps:
        cmp     al, 37
        ja      short noNum
        test    dl, NumLock
        jnz     short NumDown
        mov     bl, 2
NumDown:
        xor     bl, 2
noNum:        
        test    dl, CtrlDown
        jz      short noCtrl
        mov     bl, 4
noCtrl:
        test    dl, AltDown
        jz      short noAlt
        mov     bl, 6
noAlt:
        cbw
        shl     ax, 3
        add     bx, ax
        mov     ax, cs:KeyCode[bx]
        cmp     ax, 000ah
        ja      short pushKey
        dec     ax
        js      short SetFlagsKey     ; ax was 0
        mov     ah, AltKpd
        aad
        mov     AltKpd, al
        jmp     short SetFlagsKey
pushKey:                
        push    cx
        mov     cx, ax
        mov     al, ah      ; scan code
        mov     ah,4fh
        stc
        int     15h
        jnc     nopush
        mov     ah, 5
        int     16h
nopush:        
        pop     cx
        and     dh, not Pause    ; clear Pause bit
SetFlagsKey:
        and     cl, not (LastE0 or LastE1 or LastF0)    ; not prefix key code, clear all prefixes
SetFlags:
        mov     al, dl
        shr     al, 4
        xor     al, ch
        and     al, 7
        jz      short SF1   ; no LEDs to update
        test    ch, SetRepeat or AckReceived or LEDUpdate
        jnz     short SF1   ; can not update LEDS, so just write the flags and exit
        or      al, LEDUpdate
        xor     ch, al      ; insert the LEDs in KbdFlags4
        mov     ah, 0edh    ; set LED
        mov     bl, 0
        call    sendps2byte
SF1:        
        mov     KbdFlags1, dx
        mov     KbdFlags3, cx
        
int09Exit:
        pop     es
        pop     ds
        popa
        iret
int09 endp

ENDIF

KeyLock proc near   ; input: BH = expected scan code, al = scan code, BL = key lock flag. Returns CF=1 to continue, CF=0 to exit
        xor     bh, al
        jnz     short s2
        mov     ah, dh
        or      dh, bl      ; set flag
        xor     ah, dh      ; get flag difference
        xor     dl, ah      ; toggle only if key was not already down
        ret
s2:     cmp     bh, 80h
        stc
        jne     short exit
        xor     dh, bl      ; key up
exit:
        ret
KeyLock endp


; --------------------- INT 10h - Video ----------------
ActiveVideoMode     equ <ds:[49h]>  ; 1  byte
ScreenWidth         equ <ds:[4ah]>  ; 2  Screen width in text columns
RegenLength         equ <ds:[4ch]>  ; 2  Length (in bytes) of video area (regen size)
PageOffset          equ <ds:[4eh]>  ; 2  Offset from video segment of active video memory page
CursorPos           equ <ds:[50h]>  ; 16 Cursor location (8 byte-pairs; low byte=col, hi byte=row)
CursorShape         equ <ds:[60h]>  ; 2  Cursor size/shape.  Low byte=end scan line; hi byte=start line.
ActivePage          equ <ds:[62h]>  ; 1  Current active video page number
PortAddress         equ <ds:[63h]>  ; 2  Port address for 6845 video controller chip; see CGA I/O Ports
CrtMode             equ <ds:[65h]>  ; 1  Current value of 6845 video ctrlr CRT_MODE (port 3x8H register)
CrtPalette          equ <ds:[66h]>  ; 1  Current value of 6845 video ctrlr CRT_PALETTE (port 3x9H reg)
ScreenRows          equ <ds:[84h]>  ; 1  EGA text rows-1  (maximum valid row value)
ScanLinesChar       equ <ds:[85h]>  ; 2  EGA bytes per character (scan-lines/char used in active mode)
EgaMiscInfo         equ <ds:[87h]>  ; 1  EGA flags; see EgaMiscInfoRec
EgaMiscInfo2        equ <ds:[88h]>  ; 1  EGA flags; see EgaMiscInfo2Rec
VgaFlags            equ <ds:[89h]>  ; 1  VGA flags; see VgaFlagsRec
VgaFlags2           equ <ds:[8ah]>  ; 1  VGA flags2
PalOffset			equ <ds:[69h]>  ; 2  current palette offset (temporary during the video mode set)


int10 proc near     
        sti                     ; no interrupt reentrant
        cld
        push    ds
        push    si
        push    40h
        pop     ds
        cmp     ah, 4fh
        je      short svga
        cmp     ah, 1ch
        ja      short exit
        mov     si, ax
        shr     si, 7
        and     si, 1feh
        call    cs:vidtbl[si]
exit:        
        pop     si
        pop     ds
        iret
svga:
        cmp     al, 5
        je      short VESAMemControl
        cmp     al, 1
        jb      short VESAGetInfo
        je      short VESAGetModeInfo
        cmp     al, 3
        jb      short VESASetMode
        je      short VESAGetMode
        mov     ax, 100h
        jmp     short exit

; ---------------- VESA fn00
VESAGetInfo:
        push    cx
        push    di
        mov     si, offset VESAInfo
        mov     cx, 10
        rep     movsw es:[di], cs:[si]
        mov     cl, 118     ; 236 bytes 0
VESASupportedClear:        
        xor     ax, ax
        rep     stosw
        pop     di
        pop     cx     
VESASupported:
        mov     ah, 0       ; success    
VESASupportedErr:
        mov     al, 4fh
        jmp     short exit

; ---------------- VESA fn01
VESAGetModeInfo:
        cmp     cx, 101h
VESAGetModeInfo1:        
        mov     ah, 1       ; error
        jne     short VESASupportedErr
        push    cx
        push    di
        mov     cx, 9
        mov     si, offset VESAModeInfo
        rep     movsw es:[di], cs:[si]
        mov     cl, 119       
        jmp     short VESASupportedClear

; ---------------- VESA fn02
VESASetMode:
        imul    ax, bx, 2
        cmp     ax, 101h*2
        jne     short VESASetMode1      
        lea     ax, [bx+23ffh]
        xchg    ah, al
        int     10h
        jmp     short VESASupported   
VESASetMode1:
        mov     al, bl
        mov     ah, 0
        int     10h
        jmp     short VESASupported

; ---------------- VESA fn03
VESAGetMode:
        mov     bh, EgaMiscInfo
        and     bh, 80h
        mov     bl, ActiveVideoMode
        cmp     bl, 25h
        je      short VESAGetMode1
        or      bl, bh
        mov     bh, 0
        jmp     short VESASupported
VESAGetMode1:
        add     bx, 257-25h        
        jmp     short VESASupported

; ---------------- VESA fn05
VESAMemControl:
;        test    bx, not 101h                ; BX validation
;        jnz     short VESAGetModeInfo1      ; error
        push    cs
        push    offset VESASupported
;        call    VESAMemControlCB
;        jmp     short VESASupported
VESAMemControlCB:
        pushf
        cli
        push    ax
        push    dx
        mov     ax, bx
        and     ax, 1
        add     al, 8ah
        xchg    ax, dx
        and     ax, 7
        add     al, 0ah
        test    bh, bh
        jnz     getpageinfo
        call    flush
        out     dx, ax          
        pop     dx
        pop     ax
        popf
        retf
getpageinfo:
        in      ax, dx
        sub     al, 0ah
        and     ax, 7
        xchg    ax, dx
        pop     ax
        pop     ax                
        popf
        retf
   
VESAInfo    db  'VESA'
            dw  100h, VESAOEM, 0f000h, 2, 0, VESAModes, 0f000h, 8
VESAOEM     db  'Nicolae Dumitrache', 0
VESAModes   dw  101h, 0ffffh
VESAModeInfo:
;Bit(s)  Description - mode attributes 
;0      mode supported by present hardware configuration
;1      optional information available (must be =1 for VBE v1.2+)
;2      BIOS output supported
;3      set if color, clear if monochrome
;4      set if graphics mode, clear if text mode
;---VBE v2.0+ ---
;5      mode is not VGA-compatible
;6      bank-switched mode not supported
;7      linear framebuffer mode supported
;8      double-scan mode available (e.g. 320x200 and 320x240)
;---VBE v3.0 ---
;9      interlaced mode available
;10     hardware supports triple buffering
;11     hardware supports stereoscopic display
;12     dual display start address support
;13-15  reserved
        dw  0000000010011001b       
;Bit(s)  Description - window attributes
;0      exists
;1      readable
;2      writable
;3-7    reserved
        db  00000111b, 00000111b
        dw  64, 64, 0a000h, 0b000h, VESAMemControlCB, 0f000h, 640

p3c0r10	db		0	; port 3c0h reg 10h mirror
; --------------- fn 00h, set video mode
setmode:
        pusha
        push    es
        add     al, al      ; CF = cls bit
        rcl     byte ptr EgaMiscInfo, 1
        ror     byte ptr EgaMiscInfo, 1

	push	ax
	mov	dx, 3c4h
	mov	ax, 0f02h
	out	dx, ax		; enable all write planes
	mov	ax, 0804h
	out	dx, ax		; clear planar mode
	mov	dl, 0ceh
	mov	ax, 0001h
	out	dx, ax		; disable set/reset
		mov		al, 03h
	out	dx, ax		; reset logical op and rotate count
		mov		al, 05h
	out	dx, ax		; set write mode to 00 (CPU access)
		mov		ax, 0ff07h
        out     dx, ax      ; set color don't care to 0Fh
        inc     ax
	out	dx, ax		; set bitmask to CPU access
	pop	ax

        cmp     al, 3*2
        ja      short setmode1
        mov     al, 0b6h        ; reset sound generator
        out     43h, al
        mov     al, 0
        out     42h, al
        out     42h, al
        mov     ax, 0806h   ; text mode (80x25, 16 colors), flash enabled
        mov     word ptr ScreenWidth, 80
        mov     word ptr RegenLength, 1000h
        mov     bx, 0b800h  ; segment
        mov     cx, 4000h   ; video len/2
        mov     si, 0720h   ; clear value
		mov		di, 08f14h  ; 400lines, 14h offset
		mov		word ptr PalOffset, offset PalVGA
		jmp     setmode2
setmode1:
	cmp	al, 0dh*2
	jne	short setmode12
		mov		di, 08f14h	; 400 lines, 14h offset
		mov		ah, 11h		; graphic, 640x480x16, half -> 320x200x16
        mov     word ptr ScreenWidth, 40
        mov     word ptr RegenLength, 2000h
		mov		word ptr PalOffset, offset PalEGA
	jmp	short setmode121
setmode12:	
		cmp		al, 0eh*2	; 640x200x16
		jne		short setmode122
		mov		word ptr PalOffset, offset PalEGA
		mov		di, 8f28h	; 400lines, 28h offset
setmode1221:
		mov		ah, 1		; graphic, 640x400
		mov     word ptr ScreenWidth, 80
		mov     word ptr RegenLength, 04000h
		jmp		short setmode121
setmode122:
		mov		word ptr PalOffset, offset PalVGA
		cmp		al, 10h*2
		mov		di, 5d28h	; 350lines, 28h offset
		je		short setmode1221	; 640x350x16
	cmp	al, 12h*2
	jne	short setmode13
		mov		di, 0df28h	; 480lines, 28h offset
		mov		ah, 1		; graphic, 640x480x16
        mov     word ptr ScreenWidth, 80
        mov     word ptr RegenLength, 0a000h
setmode121:
	push	ax
	mov	dx, 3c4h
	mov	ax, 0ff02h
	out	dx, ax		; set write all planes
	mov	ax, 0004h
	out	dx, ax		; set planar mode
	pop	ax
	jmp	short setmode21
setmode13:    
        cmp     al, 13h*2
        jne     short setmode3    
        mov     ah, 41h     ; graphic mode, 320x200, 256 colors
        mov     word ptr ScreenWidth, 40
        mov     word ptr RegenLength, 0000h
		mov		di, 08f28h	; 400 lines, 28h offset
		mov		word ptr PalOffset, offset Pal256
        jmp     short setmode21
setmode3:
        cmp     al, 25h*2
        jne	setmodeexit
		mov		di, 0df50h	; 480 lines, 50h offset
		mov     ah, 1       ; graphic mode, 640x480, 256 colors
        mov     word ptr ScreenWidth, 80
        mov     word ptr RegenLength, 0000h
		mov		word ptr PalOffset, offset Pal256
setmode21:
        mov     bx, 0a000h  ; segment
        mov     cx, 8000h   ; video len/2
        xor     si, si      ; clear value
setmode2:
        shr     al, 1
        mov     ActiveVideoMode, al
        push    ax
        push    cx
		and		al, 0bh		
		cmp		al, 3
		mov		ax, 4009h	; build the reg 09h data (lcr9 and repln) of VGA port 3d4h
		sbb		ah, -1		; repln count (1 for graphic modes 0dh, 0eh, 13h)
		push	ax
        push    ds
        pop     es
        mov	dx, 3d4h
        mov	ax, di
		mov		al, 12h     ; vde
        out	dx, ax		; set scanlines
        xchg	ax, di
        mov	ah, al
        mov	al, 13h
        out	dx, ax		; set offset
        mov     ax, 1207h   ; lcr8, vde8
        out     dx, ax
        pop		ax			;	mov     ax, 4009h   ; lcr9
        out     dx, ax
        mov     ax, 0ff18h  ; lcr7..0
        out     dx,ax
        xor     ax, ax
        mov     di, offset CursorPos
        mov     cx, 8
        rep     stosw           ; reset cursor position for all pages
        mov     ax, 0500h
        int     10h             ; set page0
        pop     cx
        test    byte ptr EgaMiscInfo, 80h
        jnz     short setmode4    ; no clear video memory

        mov     es, bx
clearnext:
        xchg    ax, si
        xor     di, di
		push	cx
        rep     stosw        
		pop		cx
		cmp		bh, 0a0h
		jnz		short clearok
		xchg	ax, si
		inc		ax
		int		10h
		cmp		al, 8
		jnz		short clearnext				
clearok:

setmode4:
		mov		dx, 3dah
		in		al, dx
		pop     ax
		mov     dx, 3c0h
		mov     al, 10h
		out     dx, al
		mov     al, ah      
		out     dx, al          ; set video mode
		mov		cs:p3c0r10, al
		mov		al, 13h
		out 	dx, al
		mov		al, 0
		out		dx, al		; 0 pan

		mov     ax, 1114h
		int     10h         ; set 8x16 ROM font
		mov     ax, 1123h
		int     10h         ; set ROM 8x8 font for graphics mode
		mov     ah, 1
		mov		cx, 607h	; scanlines 13 and 14
		int     10h         ; show cursor
		test    byte ptr VgaFlags, 8  ; test default palette loading
		jnz     short setmodeexit     ; no default palette
		mov     ax, 1012h
		xor     bx, bx
		mov     cx, 40h    
		mov     dx, PalOffset
		push    cs
		pop     es
		int     10h             ; set default palette
		mov		al, 02h
		add		dx, 40h*3
		int		10h				; set default palette EGA registers
		mov		al, 13h
		mov		bx, 0001h		; select page0
		int		10h
setmodeexit:
        pop     es
        popa
nullproc:
        ret        

; --------------- fn 01h, set cursor shape and visibility
cursor:     ; CH = start line (0-7), CH bit 5 = 1  -> cursor off
			; CL = end line (0-7)
        push    ax
        push    dx
		mov		[CursorShape], cx
		test	byte ptr [ScanLinesChar], 10h
		jz		short cursor8
		add		cx, cx
		inc		ch
cursor8:
		mov     dx, 3d4h
		mov     al, 0ah
		mov		ah, ch
        out     dx, ax
		inc		ax
        mov     ah, cl
        out     dx, ax
        pop     dx
        pop     ax
        ret

;---------------- fn 02h, set cursor pos
curpos:
        push    ax
        push    bx
        mov     al, bh
        shr     bx, 7
        and     bx, 0eh
        mov     CursorPos[bx], dx
        cmp     byte ptr ActiveVideoMode, 3
        jne     short curpos1
        cmp     al, ActivePage
        jne     short curpos1
        push    dx
        xor     ax, ax
        xchg    al, dh        
        imul    ax, 80
        add     ax, dx
        mov     dx, 3d4h
        push    ax
	mov 	ah, al
	mov	al, 0fh
	out	dx, ax
	pop	ax
	mov	al, 0eh
	out 	dx, ax
        pop     dx
curpos1:        
        pop     bx
        pop     ax
        ret

;---------------- fn 03h, get cursor pos
getcurpos:
        push    bx
        shr     bx, 7
        and     bx, 0eh
        mov     dx, CursorPos[bx]
        mov     cx, CursorShape
        pop     bx
        ret

;---------------- fn 04h, light pen
lightpen:
        mov     ah, 0   ; not triggered
        ret

;---------------- fn 05h, set active video page
apage:
        pusha
        call    flush
        and     al, 7
        mov     bh, al
        mov     ActivePage, al
        mov     al, ActiveVideoMode
        cmp     al, 13h
	jae	short apage1
        cmp     al, 3
	ja	short apage2
        mov     ax, 0ah
        out     8ah, ax
        inc     ax
        out     8bh, ax
        mov     ah, 3
        int     10h        ; get cursor pos
        mov     ah, 2
        int     10h        ; set cursor pos
	mov	ax, 400h
        jmp     short apage4
apage1:	
        mov     ax, 0ah		; mode 13h and 25h
        add     al, bh
        out     8ah, ax
        inc     ax
        cmp     al, 12h
        jne     short apage3
        mov     al, 0ah
apage3: 
	out     8bh, ax

apage2:
	mov	ax, RegenLength
apage4:
        shr     bx, 8      ; page number
        mul     bx
        mov     bx, ax     ; 1 means 4 bytes, or 2 characters
        shl     ax, 2	   ; 1 means 1 byte   
        mov     PageOffset, ax
        mov     dx, 3d4h
	mov	ah, bl
	mov	al, 0dh
	out	dx, ax
	mov	ah, bh
	dec	ax
	out	dx, ax
        popa
        ret

;---------------- fn 06h, scroll up / clr
scrollup:
        pusha
        push    es
        xchg    cx, dx
        sub     cx, dx
        inc     cx
        call    scr_params
scrollup6:        
        push    0b800h          ; segment
        pop     es
        add     dl, dl
        add     di, di
        add     di, PageOffset  ; di = top left corner address
        xchg    ax, cx          ; ah = 0
        test    bl, bl
        jz      short scrollup3       ; clear
        sub     ah, bl
        jb      short scrollup3       ; clear
        add     si, di
scrollup4:        
        mov     cl, al
        rep     movsw es:[si], es:[di]
        add     si, dx
        add     di, dx
        dec     ah              
        jns     short scrollup4       ; ch = lines - 1
scrollup3:                      
        add     ah, bl          ; clear rectangle: DI=address, ah=lines, al=columns, bh=attribute
        xchg    ax, bx
        mov     al, ' '
scrollup5:
        mov     cl, bl
        rep     stosw
        add     di, dx
        dec     bh
        jns     short scrollup5       ; ch = lines - 1
scrollexit:
        pop     es
        popa
        ret

;---------------- fn 07h, scroll dn / clr
scrolldn:
        std
        pusha
        push    es
        neg     cx
        add     cx, dx
        inc     cx
        call    scr_params
        neg     dx
        neg     si
        jmp     short scrollup6

scr_params:
        mov     bl, al          ; lines
        xor     ax, ax
        xchg    al, dh
        imul    di, ax, 80
        add     di, dx
        mov     dl, 80          ; dh = 0
        sub     dl, cl
        mov     al, bl
        imul    si, ax, 160
        ret
;---------------- fn 08h, read char/attr
readchar:
        push    bx
        call    mode3chaddr
        mov     ax, [bx]
        pop     bx
        ret

mode3chaddr:    ; returns current char address in mode3 in ds:bx. Input: bh=page, ds=40h 
        push    ax
        and     bx, 700h
        lea     ax, [bx+0b800h]
        shr     bx, 7
        mov     bx, CursorPos[bx]
        mov     ds, ax
        xor     ax, ax
        xchg    al, bh
        imul    ax, 80
        add     bx, ax
        add     bx, bx
        pop     ax
        ret

;---------------- fn 09h, write char/attr
writecharattr:
        push    ax
        push    es
        push    bx
        push    cx
        mov     ah, bl
        call    mode3chaddr
        push    ds
        pop     es
        xchg    di, bx
        rep     stosw
        xchg    di, bx
        pop     cx
        pop     bx
        pop     es
        pop     ax
        ret

;---------------- fn 0ah, write char
writechar:
        jcxz    short writecharskip
        push    bx
        push    cx
        call    mode3chaddr
writechar3:        
        mov     [bx], al
        add     bx, 2
        loop    short writechar3
        pop     cx
        pop     bx
writecharskip:        
        ret

;---------------- fn 0eh, write char as TTY
writecharTTY:
        push    ax
        push    bx
        push    dx
        mov     bl, ActivePage
        mov     bh, 0
        add     bx, bx
        mov     dx, CursorPos[bx]
        shl     bx, 7
        mov     ah, 0ah
        call    tty
        mov     ah, 2       ; set cursor pos
        int     10h
        pop     dx
        pop     bx
        pop     ax 
        ret        

tty:    ; dx=xy, bh=page, al=char, bl=attr, ah=0ah(no attr) or 09h(with attr)
        test    word ptr KbdFlags2, Pause
        jnz     short tty
        push    cx
        cmp     al, 7
        je      short bell
        cmp     al, 8
        je      short bs
        cmp     al, 0ah
        je      short cr
        cmp     al, 0dh
        je      short lf
        mov     cx, 1
        int     10h         ; write char at cursor
        inc     dx
        cmp     dl, ScreenWidth
        jae     short crlf
tty1:
        pop     cx
        ret
bell:
; TODO bell code        
        jmp     short tty1
bs:
        sub     dl, 1
        adc     dl, 0      
        jmp     short tty1
lf:
        mov     dl, 0
        jmp     short tty1
crlf:
        mov     dl, 0        
cr:        
        inc     dh
        cmp     dh, ScreenRows
        jbe     short tty1
        dec     dh
;        mov     ah, 8
;        int     10h         ; read attribute at cursor pos
        push    bx          ; save active page in bh
        push    dx
;        xchg    ax, bx
        mov     bh, 7       ; default attribute
        mov     ax, 601h    
        mov     dh, ScreenRows
        mov     dl, ScreenWidth
        dec     dx
        xor     cx, cx
        int     10h         ; scroll up
        pop     dx
        pop     bx          ; restore active page in bh         
        jmp     short tty1
        
;---------------- fn 0fh, read video mode
readmode:
        mov     al, EgaMiscInfo
        and     al, 80h
        or      al, ActiveVideoMode
        mov     ah, ScreenWidth
        mov     bh, ActivePage
        ret


;---------------- fn 10h, palette
paltable    dw  setonereg, palexit, setallreg, setblink, palexit, palexit, palexit, readonereg, readoverscan, readallreg, palexit, palexit, palexit, palexit, palexit, palexit
            dw  setoneDAC, palexit, setblockDAC, paging, palexit, readoneDAC, palexit, readblockDAC, setPELmask, getPELmask, getpaging, grayscale

pal:
		cmp     al, 1bh
		ja      short palexit
		mov     si, ax
		add     si, si
		call    cs:paltable[si-2000h]
palexit:
		ret

setonereg:
		pusha
		mov		dx, 3dah
		in		al, dx
		mov		dx, 3c0h
		mov		al, bl
		out		dx, al
		mov		al, bh
		out		dx, al
		popa
setonereg1:        
        ret        

setallreg:
        pusha
        mov     al, 0
        mov     si, dx
        mov     bl, 15
setallreg1:        
        mov     bh, es:[si+15]
        int     10h
        dec     si
        dec     bl
        jns     short setallreg1
        popa
        ret

setblink:
		pusha
		mov		dx, 3dah
		in		al, dx
		mov     dx, 3c0h
		mov     al, 10h
		out     dx, al
		mov		al, cs:p3c0r10      
		shl     bl, 3
		xor		al, bl
		and		al, not 8
		xor		al, bl
		out     dx, al          ; set blink flag
		mov		cs:p3c0r10, al
		shl     al, 2
		xor     al, VgaFlags
		and     al, 20h
		xor     VgaFlags, al
setblink1:
		popa
		ret

readonereg:
		push	dx
		push	ax
		mov		dx, 3dah
		in		al, dx
		mov		dx, 3c0h
		mov		al, bl
		out		dx, al
		inc		dx
		in		al, dx
		dec		dx
		out		dx, al
		mov		bh, al
		pop		ax
		pop		dx
		ret

readallreg:
        pusha
        mov     di, dx
        mov     bl, 0
readllreg1:
        mov     al, 7
        int     10h
        mov     al, bh
        stosb
        inc     bx
        cmp     bl, 16
        jne     short readllreg1
        mov     al, 0   ; overscan color
        stosb
        popa
        ret

readoverscan:
        mov     bh, 0
        ret

setoneDAC:
        push    ax
        push    dx
        xchg    ax, dx
        mov     al, bl
        mov     dx, 3c8h
        out     dx, al
        inc     dx
        mov     al, ah
        out     dx, al
        mov     al, ch
        out     dx, al
        mov     al, cl
        out     dx, al
        pop     dx
        pop     ax
        ret

setblockDAC:
        pusha
        mov     si, dx
        mov     dx, 3c8h
        xchg    ax, bx
        out     dx, al
        inc     dx
        imul    cx, 3
        rep     outsb dx, es:[si]
        popa
        ret

paging:
		pusha
		mov		dx, 3dah
		in		al, dx
		mov		dx, 3c0h
		test    bl, bl
		mov		al, cs:p3c0r10
		jnz     short paging1
		add		al, al
		ror		bh, 1
		rcr		al, 1
		mov		cs:p3c0r10, al
		push	ax
		mov		al, 10h
		out		dx, al
		pop		ax
		jmp     short paging2
paging1:
		add		al, al
		jc		paging3
		shl		bh, 2
paging3:
		mov		al, 14h
		out		dx, al
		mov		al, bh
paging2:        
		out		dx, al
		popa
		ret

readoneDAC:
        push    ax
        push    dx
        mov     al, bl
        mov     dx, 3c7h
        out     dx, al
        inc     dx
        inc     dx
        in      al, dx
        mov     ah, al
        in      al, dx
        mov     ch, al
        in      al, dx
        mov     cl, al
        pop     dx
        mov     dh, ah
        pop     ax             
        ret

readblockDAC:
        pusha
        mov     di, dx
        mov     dx, 3c7h
        xchg    ax, bx
        out     dx, al
        inc     dx
        inc     dx
        imul    cx, 3
        rep     insb                
        popa
        ret

setPELmask:
        push    dx
        xchg    ax, bx
        mov     dx, 3c6h
        out     dx, al
        xchg    ax, bx
        pop     dx
        ret

getPELmask:
        push    dx
        xchg    ax, bx
        mov     dx, 3c6h
        in      al, dx
        xchg    ax, bx
        pop     dx
        ret

getpaging:
		mov		bl, 14h
		call	readonereg	; returns BH
		mov		bl, cs:p3c0r10
		and		bh, 0fh
		shr		bl, 7
		jnz		short getpaging1
		shr		bh, 2
getpaging1:
		ret

grayscale:
        jcxz    short grayscale2
        pusha
        mov     bh, cl
grayscale1:        
        mov     al, 15h
        int     10h
        shr     dx, 8
        imul    si, dx, 77
        mov     dl, ch
        imul    dx, 151
        mov     ch, 0
        imul    cx, 28
        add     dx, si
        add     dx, cx
        mov     ch, dh
        mov     cl, dh
        mov     al, 10h
        int     10h
        inc     bl
        dec     bh
        jne     short grayscale1        
        popa
grayscale2:        
        ret


;---------------- fn 11h, character generator
loadUDF:    ; CX=chars, DX=first char, BH=bytes/char, ES:BP=font
		pusha
		xchg    ax, dx
		xchg    cx, bx
		shr     cx, 16 ; 8 when fonts are installed
		mov     dx, 03cbh
		mov     si, bp
loadUDF1:
		out     dx, ax
		push    cx
        rep     outsb dx, es:[si]
		pop     cx
		inc     ax
		dec     bx
		jnz     short loadUDF1
		popa
		test    al, 10h
		jz      short loadUDFexit
		push    40h
		pop     ds
		push    ax
		mov     ax, 400 ; screen lines in text mode
		div     bh
		dec     ax
		mov     ScreenRows, al
		mov     byte ptr ScanLinesChar, bh
		mov		al, cs:p3c0r10		; set/reset half bit
		xor		al, bh		
		or		al, 10h
		xor		al, bh
		test	al, 1
		jnz		short loadUDFexit1	; not in text mode
		push	ax
		mov		dx, 3dah
		in		al, dx
		mov		dx, 3c0h
		mov		al, 10h
		out		dx, al
		pop		ax
		out		dx, al
		mov		cs:p3c0r10, al
loadUDFexit1:
		pop     ax 
loadUDFexit:        
		ret

chargen:
		test    al, not 10h     ; test for 00h and 10h  ; UDF
		jz      short loadUDF
		test    al, not 11h     ; test for 01h and 11h  ; 8x14
		jz      short loadROMfont16
		test    al, not 12h     ; test for 02h and 12h  ; 8x8
		jz      short loadROMfont8
		test    al, not 14h     ; test for 04h and 14h  ; 8x16
		jz      short loadROMfont16
		cmp     al, 20h
		jb      loadUDFexit
		je      short set1f
		cmp     al, 21h
		je      short setgrUDF
		cmp     al, 24h
		jbe     short setROMgrFont
		cmp     al, 30h
		je      short getfontinfo
		ret

loadROMFont8:
		pusha
		mov     bx, 0800h       ; 8x8 chars, block 0
		mov     bp, cs:fontinfo[2]
		jmp     short loadROMFont161
	 
loadROMFont16:
		pusha
		mov     bx, 1000h       ; 8x16 chars, block 0
		mov     bp, cs:fontinfo[8]
loadROMFont161:
		mov     cx, 100h        ; all chars
		xor     dx, dx
		push    es
		push    cs
		pop     es
		and     al, 10h
		int     10h             ; loadUDF
		pop     es
		popa
		ret

set1f:
        xor     si, si
        mov     ds, si
        mov     [si+1fh*4], bp
        mov     [si+1fh*4+2], es
        ret
        
setgrUDF:
        pusha
        jcxz    short loadUDFexit
        push    ds
        xor     si, si
        mov     ds, si
        mov     [si+43h*4], bp
        mov     [si+43h*4+2], es
        pop     ds
        mov     ax, 200
        cmp     byte ptr ActiveVideoMode, 13h
        jb      short setgrUDFexit
        je      short setgrUDF1
        mov     ax, 480         ; mode 25h, 480 lines
setgrUDF1:
        mov     ScanLinesChar, cx
        cwd
        div     cx
        dec     ax
        mov     ScreenRows, al
setgrUDFexit:
        popa
        ret

setROMgrFont:       
        pusha
        push    es
        mov     cx, 8
        push    cs
        pop     es
		mov     bp, font8x8
        cmp     al, 23h
        je      short setROMgrFont1
        mov     bp, offset font8x16
setROMgrFont1:        
        mov     al, 21h
        int     10h     ; set graphic UDF
        dec     ax
		mov     bp, font8x8 + 128*8
        int     10h     ; set INT 1fh
        pop     es
        popa
        ret
        
getfontinfo:
        mov     cx, ScanLinesChar
        mov     dl, ScreenRows
        cmp     bh, 1
        ja      short getfontinfo1
        push    0
        pop     ds
        les     bp, ds:[1fh*4] 
        jb      short getfontinfoexit
        les     bp, ds:[43h*4]
        ret
getfontinfo1:
        cmp     bh, 7
        ja      short getfontinfoexit
        mov     si, bx
        shr     si, 8
        add     si, si
        mov     bp, cs:fontinfo[si-4]
        push    cs
        pop     es                    
getfontinfoexit:
        ret

fontinfo    dw  font8x14, font8x8, font8x8+128*8, font8x14, font8x16, font8x16

;---------------- fn 12h, special functions
special:
        cmp     bl, 10h
        jne     short special1
        mov     cl, EgaMiscInfo2    ; cl = switch settings
        and     cx, 15              ; ch <- 0 (feature bits)
        mov     bx, 3               ; bh <- 0 (color mode), bl = video memory size
        ret
special1:
        cmp     bl, 31h
        jne     short special2
        neg     al
        xor     al, VgaFlags
        and     al, 8       ; transfer palette loading bit to VgaFlags
        xor     VgaFlags, al
        mov     al, 12h     ; supported function
        ret
special2:
        mov     al, 0       ; unsupported function
        ret


;---------------- fn 13h, write string
writestr:
        jcxz    short wstrexit
        pusha
        mov     si, bx
        shr     si, 8
        add     si, si
        push    CursorPos[si]
        mov     ah, 9       ; write tty char/attribute
wstr1:        
        push    ax
        test    al, 2
        mov     al, es:[bp]
        jz      short noattr
        inc     bp
        mov     bl, es:[bp]
noattr:
        inc     bp
        mov     CursorPos[si], dx
        call    tty
        pop     ax
        loop    short wstr1
        pop     CursorPos[si]
        test    al, 1
        jz      short wstr2             
        mov     ah, 2       ; set cursor pos
        int     10h                
wstr2:        
        popa
wstrexit:        
        ret

;---------------- fn 1ah, get/set display combination code
getdcc:
        cmp     al, 1
        ja      short getdccexit
        mov     al, ah
        je      short setdcc
        mov     bx, 08h
dccval  label word        
setdcc:
        mov     cs:[dccval-2], bx
getdccexit:        
        ret        

;---------------- fn 1bh, query status
querystatus:
        pusha
        mov     ax, offset staticfunctable
        stosw
        mov     ax, cs
        stosw
        mov     si, offset ActiveVideoMode
        cmp     byte ptr [si], 12h
        mov     cx, 33          ; info copied from BDA        
        rep     movsb
        mov     ax, 8
        stosw                   ; display info (one VGA analog color monitor)
        mov     bx, 208h        ; 400 scan lines, 8 pages
        mov     al, 10h         ; 16 colors         
        jb      short querystatus1	; mode03h
	mov	bx, 302h	; 480 scan lines, 2 pageS
        je	short querystatus1	; mode12h 
        mov     bh, 0           ; scan lines code (0=200, 1=350, 2=400, 3=480)
        mov     ax, 100h        ; 256 colors
        cmp     byte ptr ActiveVideoMode, 13h
        je      short querystatus3     ; mode13h
        mov     bx, 301h        ; 480 scan lines, 1 page
querystatus1:
	cmp	byte ptr ActiveVideoMode, 0dh
	jne	short querystatus3
	mov	bx, 0008h	; 200 scan lines, 8 pages
querystatus3:
        stosw
        xchg    ax, bx
        stosw
        xor     ax, ax
        stosw                   ; font block info (45)
        mov     al, VgaFlags
        and     al, 00101111b
        stosw        
        stosw
        mov     al, EgaMiscInfo
        shr     al, 4
        and     al, 7           ; video memory size
        stosw
        mov     al, 2
        stosb                   ; color display attached
        mov     cl, 6
        xor     ax, ax
        rep     stosw           ; 12 reserved bytes
        popa
        mov     al, ah          ; supported function
        ret

staticfunctable db  00001100b   ; video modes 2h, 3h supported
				db  01100000b	; video mode 0dh, 0eh supported
				db  00001101b   ; video modes 10h, 12h, 13h supported
				db  00000000b
				db  00100000b   ; video mode 25h supported
				db  0, 0
				db  00000100b   ; 400 scanline supported
				db  1           ; font blocks available in text mode
				db  1           ; max active font blocks available in text mode

;Bit(s)  Description
;0      all modes on all displays function supported
;1      gray summing function supported
;2      character font loading function supported
;3      default palette loading enable/disable supported
;4      cursor emulation function supported
;5      EGA palette present
;6      color palette present
;7      color-register paging function supported
;8      light pen supported (see AH=04h)
;9      save/restore state function 1Ch supported
;10     intensity/blinking function supported (see AX=1003h)
;11     Display Combination Code supported (see #00039)
;12-15  unused (0)
                db  11101111b   ; miscellaneous function support flags 
                db  00001100b   ; miscellaneous function support flags
                 
                db  0, 0        ; reserved
                db  0           ; save pointer function flags
                db  0           ; reserved  

       
vidtbl  dw  setmode, cursor, curpos, getcurpos, lightpen, apage, scrollup, scrolldn, readchar, writecharattr
        dw  writechar, nullproc, nullproc, nullproc, writecharTTY, readmode
        dw  pal, chargen, special, writestr, nullproc, nullproc, nullproc, nullproc, nullproc, nullproc, getdcc, querystatus, nullproc
int10 endp

; --------------------- INT 11h - Equipment ----------------
EquipmentWord       equ     <ds:[10h]>

int11   proc near
        push    ds
        push    40h
        pop     ds
        mov     ax, EquipmentWord
        pop     ds
        iret
int11   endp

; --------------------- INT 12h - Memory size ----------------
MemorySize       equ     <ds:[13h]>

int12   proc near
        push    ds
        push    40h
        pop     ds
        mov     ax, MemorySize
        pop     ds
        iret        
int12   endp

; --------------------- INT 13h - Disk services ----------------
HDLastError       equ     <ds:[74h]>
HDOpStarted       equ     <ds:[92h]>    ; bit 3: in INT13h (all other bits must be 0)
HDSize            equ     <ds:[94h]>

int13   proc near
        push    ds
        push    bp
        push    40h
        pop     ds
        xor     byte ptr HDOpStarted, 8
        jz      short inINT13
        sti                     
        cld
        cmp     ah, 1ah
        jbe     short Disk1
        sub     ah, 41h-1bh     ; extensions
        cmp     ah, 22h
        jbe     short Disk1
        mov     ah, 1           ; bad command error
        jmp     short exit
inINT13:        
        mov     ah, 0aah        ; drive not ready
        jmp     short exit2
Disk1:
        mov     bp, ax
        shr     bp, 7
        and     bp, 1feh
        push    ds
        call    cs:disktbl[bp]
        pop     ds
exit:        
        mov     HDLastError, ah
exit2:
        xor     byte ptr HDOpStarted, 8
        neg     ah              ; CF <- (AH != 0)
exit1:
        mov     bp, sp
        rcr     byte ptr [bp+8], 1
        rol     byte ptr [bp+8], 1  ; insert error CF on stack
        neg     ah
        pop     bp
        pop     ds
        iret

disktbl dw      DiskReset, DiskGetStatus, DiskRead, DiskWrite, DiskVerify, DiskFormat, DiskFormat, DiskFormat, DiskGetParams, DiskInit, DiskRead, DiskWrite, DiskSeek, DiskRst, DiskReadSectBuffer, DiskWriteSectBuffer
        dw      DiskReady, DiskRecalibrate, DiskDiag, DiskDiag, DiskDiag, DiskGetType, DiskChanged, DiskSetDASDType, DiskSetMediaType, DiskPark, DiskFormat,  DiskExtInstCheck, DiskExtRead, DiskExtWrite, DiskExtVerify, DiskExtLock
        dw      DiskExtEject, DiskExtSeek, DiskExtGetParams

DiskGetType:
        cmp     dl, 80h
        jne     short DiskReset ; ah=0, drive not present
        mov     cx, HDSize      
        mov     dx, cx
        test    cx, cx
        jz      short DiskReset ; ah=0, drive not present
        mov     ah, -3      ; HD present
        shr     cx, 6
        shl     dx, 10      ; CX:DX = HDSize * 1024
DiskGetTypeexit:        
        pop     ds          ; discard ret address
        pop     ds          ; discard DS
        xor     byte ptr HDOpStarted, 8     ; CF <- 0 
        jmp     short   exit1        

DiskExtInstCheck:
        xchg    bl, bh
        mov     ah, -1
        mov     cx, 1       ; extended disk access functions (AH=42h-44h,47h,48h) supported
        cmp     dl, 80h
        jne     short notready
        jmp     short DiskGetTypeexit

DiskReset:
DiskChanged:
DiskPark:
        mov     ah, 0       ; success
        ret

DiskGetStatus:
        mov     ah, HDLastError
        ret
      
DiskVerify:
        mov     bp, sdverify
        jmp     short   DiskRead1
DiskWrite:
        mov     bp, sdwrite
        jmp     short   DiskRead1
DiskRead:
        mov     bp, sdread
DiskRead1:        
        test    al, al
        jz      short DiskReset
        cmp     dl, 80h
        jne     short notready
        mov     ah, 4
        test    cl, 3fh
        jz      short DiskReadend   ; bad sector 0
        pusha
        mov     ah, 0
        push    ax
        call    HCStoLBA
        pop     cx
        push    cx        
        call    bp              ; DX:AX sector, ES:BX buffer, CX=sectors, returns AX=read sectors
        pop     cx
        sub     cx, ax
        neg     cx              ; CF=1 if cx != 0
        rcl     ah, 3           ; AH = 4*CF (sector not found / read error)
        mov     ds, ax
        popa
        mov     ax, ds
DiskReadend:
        ret

HCStoLBA:       ; CX = {cyl[7:0], cyl[9:8], sect[5:0]}, DH = head. Returns DX:AX LBA
        mov     al, ch
        mov     ah, cl
        shr     ah, 6
        shr     dx, 8
        imul    dx, 63
        and     cx, 3fh
        add     cx, dx
        dec     cx
        mov     dx, 255*63
        mul     dx
        add     ax, cx
        adc     dx, 0
        ret       
;    unsigned int s = cs & 0x3f;
;    unsigned int c = ((cs & 0xc0) << 2) | (cs >> 8);
;    return (c*255l + h)*63l + s - 1l;

DiskFormat:
DiskInit:
DiskSeek:
DiskRst:
DiskReady:
DiskRecalibrate:
DiskDiag:
DiskExtSeek:
        cmp     word ptr HDSize, 0
        je      short notready
        cmp     dl, 80h
        je      short DiskReset
notready:        
        mov     ah, 0aah        ; disk not ready
        ret

DiskGetParams:
        cmp     dl, 80h
        mov     ah, 7
        jne     short DiskReadend   ; ret
        mov     bl, 0   ; ???
        mov     ax, HDSize
        mov     dx, ax
        shl     ax, 10
        shr     dx, 6
        sub     ax, 30
        sbb     dx, 0
        mov     cx, 63*255
        div     cx
        dec     ax
        cmp     ax, 3feh
        jbe     dgpok
        mov     ax, 3feh
dgpok:        
        xchg    al, ah
        shl     al, 6
        or      al, 3fh
        mov     cx, ax
        mov     dx, 0fe01h
        xor     ax, ax
        ret        

DiskExtVerify:
        mov     bp, sdverify
        jmp     short DiskExtRead1
DiskExtWrite:
        mov     bp, sdwrite
        jmp     short DiskExtRead1
DiskExtRead:
        mov     bp, sdread
DiskExtRead1:
        cmp     dl, 80h
        jne     short notready
        push    es
        push    ax
        pusha
        mov     bx, sp
        mov     ds, ss:[bx+26]
        mov     cx, [si+2]
        les     bx, [si+4]
        mov     ax, [si+8]
        mov     dx, [si+10]
        push    ds
        push    si
        call    bp
        pop     si
        pop     ds
        sub     ax, [si+2]
        add     [si+2], ax
        popa
        pop     ax
        sbb     ah, ah
        and     ah, 4
        pop     es
        ret

DiskExtGetParams:
        cmp     dl, 80h
        jne     short notready
        push    ax
        mov     ax, HDSize   
        mov     bp, sp
        mov     ds, [bp+8]
        xor     bp, bp
        mov     word ptr [si], 1ah      ; size
        mov     word ptr [si+2], 0bh    ; flags
        mov     word ptr [si+4], 1023   ; cylinders
        mov     word ptr [si+6], bp
        mov     word ptr [si+8], 255    ; heads
        mov     word ptr [si+10], bp
        mov     word ptr [si+12], 63     ; sectors/track
        mov     word ptr [si+14], bp
        mov     word ptr [si+16], ax
        shl     word ptr [si+16], 10
        shr     ax, 6
        mov     word ptr [si+18], ax
        mov     word ptr [si+20], bp
        mov     word ptr [si+22], bp
        mov     word ptr [si+24], 512   ; bytes/sector
        pop     ax
        mov     ah, 0
        ret 

DiskReadSectBuffer:
DiskWriteSectBuffer:
DiskSetDASDType:
DiskSetMediaType:
DiskExtLock:
DiskExtEject:
        mov     ah, 1       ; unsupported fn
        ret

int13   endp


OFFDX MACRO n
	IF n LE -3 OR n GE 3
		add		dx, n
	ENDIF
	IF n EQ -2
		dec		dx
	ENDIF 
	IF n EQ -1 or n EQ -2
		dec		dx
	ENDIF
	IF n EQ 1 or n EQ 2
		inc		dx
	ENDIF 
	IF n eq 2
		inc		dx
	ENDIF
ENDM
; --------------------- INT 14h - Serial port I/O ----------------
ComPort		equ		0
THR			equ		0	; Transmit holding buffer
RBR			equ		0	; Receive buffer
DLL			equ		0	; divisor latch low byte (DLAB = 1)
DLH			equ		1	; divisor latch high byte	(DLAB = 1)
IER			equ		1	; Interrupt enable register
LCR			equ		3	; line control register (DLAB, BrkEnable, PPP, S, LL)
LSR			equ		5	; line status register
MSR			equ		6	; Modem status register

; AH=function number, AL=char sent or received, DX=zero based COM index. Preserve all registers except AX
int14 proc near
		push	ds
		push	dx
		push	si
		push	40h
		pop		ds
		mov		si, dx
		add		si, si
		mov		dx, ComPort[si]
		OFFDX	LCR
		mov		si, ax
		in		al, dx
		and		al, 7fh		; clear DLAB bit
		out		dx, al
		OFFDX	LSR-LCR
		mov		ax, si
		shr		si, 8
		jz		short SetCharFormat
		dec		si
		jz		short STransmit
		dec		si
		jz		short SReceive
		dec		si
		jz		short GetPortStatus
;		dec		si
;		jz		short SetCharFormatExt
SExit:
		pop		si
		pop		dx
		pop		ds
		iret

; ----- Set COM char format --------
; AL = BBBPPSLL (BB=000..111 for baud 110,150,300,600,1200,2400,4800,9600)
; returns LSR in AH and MSR in AL
SetCharFormat:		; DX = LSR
		push	dx
		push	cx
		mov		cl, al
		mov		ch, al
		OFFDX	LCR-LSR
		in		al, dx
		or		al, 80h		; set DLAB=1
		out		dx, al
		shr		cl, 5
		mov		ax, 417h
		jz		short Baud110
		mov		ax, 300h
		shr		ax, cl
Baud110:
		OFFDX	DLL-LCR
		out		dx, al		; set baud low
		OFFDX	DLH-DLL
		mov		al, ah
		out		dx, al		; set baud high
		OFFDX	LCR-DLH
		mov		al, ch
		and		al, 1fh
		out		dx, al		; set format, DLAB=0

		OFFDX	IER-LCR
		xor		al, al
		out		dx, al		; disable all interrupts
		pop		cx
		pop		dx

; ----- get COM port status --------
GetPortStatus:			; DX = LSR
		OFFDX	MSR-LSR
		in		al, dx		; read modem status
		OFFDX	LSR-MSR
GetPortStatus1:
		mov		ah, al
		in		al, dx		; read line (port) status
		xchg	ah, al
		jmp		short SExit
		
; ----- Transmit char to COM port --------
; Waits for THRE(bit5 in LSR) and then sends AL. returns LSR in AH
STransmit:			; DX = LSR
		push	dx
		mov		ah, al
STr1:
		in		al, dx
		test	al, 20h
		jz		short STr1
		OFFDX	THR-LSR
		mov		al, ah
		out		dx, al
STr2:
		pop		dx
		jmp		short GetPortStatus1

; ----- receive char from COM port --------
; Waits for RDA(bit0 in  LSR) and returns the received char in AL, and the LSR in AH
SReceive:			; DX = LSR
		push	dx
SReceive1:
		in		al, dx
		test	al, 1
		jz		short SReceive1
		OFFDX	RBR-LSR
		in		al, dx
		jmp		short Str2


; ----- Set COM char format extended --------
;SetCharFormatExt:
int14 endp

; --------------------- INT 15h - Extended services ----------------
UFPtr           equ     <ds:[98h]>
WaitCount       equ     <ds:[9ch]>
UWaitFlag       equ     <ds:[0a0h]>
HandlerPtr      equ     <ds:[0a1h]> ; 4 bytes
DataBuffer      equ     <ds:[0a5h]> ; 3 bytes
DataCounter     equ     <ds:[067h]> ; 1 byte
PacketSize      equ     <ds:[068h]> ; 1 byte, 0->3bytes, 1->4bytes
FreeXMSKb       equ     RAMSize*64 - 640 - 512 - 32	;total - DOS - VGA - BIOS

; ------------ MovExt
IncSeg: ; DX = segment port address
        jnz     short SetSegExit
        in      ax, dx
        and     ax, RAMSize - 1
        inc     ax
        cmp     ax, 12h
        jne     short IncSeg1
        xor     ax, ax
IncSeg1:
        cmp     ax, 0ch
        jne     short SetSeg2
SetSeg: ; DX = segment port address, ax = logical segment (0..RAMSize-1)    
        and     ax, RAMSize - 1
        cmp     ax, 0ch
        jb      short SetSeg1
        add     ax, 6
SetSeg2:        
        cmp     ax, RAMSize
        jb      short SetSeg1
        sub     ax, RAMSize - 0ch
SetSeg1:
        out     dx, ax          
SetSegExit:              
        ret

MovSeg  equ     01h
savess  dw      0
savesp  dw      MovExt, 0 ; tmp stack
; Log(idx) to Phy(val) segment map (RAMSize segs): 0,1,2,3,4,5,6,7,8,9,a,b,12h,13h,...,RAMSize-2,RAMSize-1,c,d,e,f,10h,11h, then wrap to 0,1,2,...
MovExt:
        push    es
        push    ds
        pusha
        cli
        mov     cs:savess, ss
        push    cs
        pop     ss
        xchg    sp, cs:savesp
        mov     dx, 80h + MovSeg + 1
        jcxz    short MovExt_exit
        push    es
        pop     ds
        cld
        mov     al, [si+1ch]
        mov     ah, [si+1fh]
        mov     bl, [si+14h]
        mov     bh, [si+17h]
        mov     di, [si+1ah]
        mov     si, [si+12h]
        call    flush   
        call    SetSeg      ; 02000h = destination, DX=82h
        dec     dx
        xchg    ax, bx
        call    SetSeg      ; 01000h = source, DX=81h
        push    MovSeg shl 12 
        pop     ds
        push    (MovSeg + 1) shl 12
        pop     es
        xor     bx, bx
        add     cx, cx
        adc     bx, bx      ; BX:CX = bytes to transfer
; move from 01000h:si to 02000h:di, 2*cx bytes
MovExtLoop:
        inc     dx          ; 82h
        mov     ax, si
        cmp     ax, di
        ja      short MovExt1
        mov     ax, di
MovExt1:
        neg     ax
        adc     bx, -1
        sub     cx, ax
        sbb     bx, 0
        xchg    ax, cx      ; cx = bytes to move, bx:ax = bytes left for the next transfer
        jns     short MovExt2   ; ax <= bx:cx     
        add     cx, ax
        xor     ax, ax
        inc     bx
MovExt2:
        movsb               ; if CX = 0 transfer 10000h bytes
        dec     cx
        jz      short MovExt_next
        test    si, 1       ; read align
        jz      short raligned
        movsb
        dec     cx
raligned:
        shr     cx, 1
        rep     movsw
        jnc     short MovExt_next
        movsb
MovExt_next:
        call    flush
        mov     cx, ax
        or      ax, bx
        jz      short MovExt_exit  ; finalized
        test    di, di
        call    incseg      ; does nothing if ZF == 0, dx = 8bh
        dec     dx          ; 81h
        test    si, si      
        call    incseg      ; dx = 81h
        jmp     short MovExtLoop
MovExt_exit:
        mov     ax, MovSeg + 1
        out     dx, ax      ; 82h
        dec     ax
        dec     dx
        out     dx, ax      ; 81h
        mov     ss, cs:savess
        xchg    sp, cs:savesp
        popa
        pop     ds
        pop     es
        xor     ah, ah
        jmp     short exit_ax
MovExtProxy:
        jmp     MovExt        

int15:
        cmp     ah, 4fh
        je      short exit_iret
        xchg    al, ah
        cmp     al, 80h
        jb      short exit15; CF=1  for <80h
        cmp     al, 83h
        jb      short done  ; no error for 80, 81, 82
        je      short SetEventWait; 83
        cmp     al, 86h
        jb      short exit15; CF=1 for 84, 85
        je      short Wait1 ; 86
        cmp     al, 88h
        jb      short MovExtProxy ; 87
        je      short ExtSize     ; 88
        cmp     al, 90h
        jb      short  exit15; CF=1 for 89..8f
        cmp     al, 92h
        jb      short done  ; no error for 90, 91
        cmp     al, 0c0h
        jb      short exit15; CF=1 for 92..bf
        je      short GetConfig   ; c0
        cmp     al, 0c2h
        jb      short exit15; CF=1 for c1
        je      short Mouse ; c2
done:
        cmc                 ; CF=1 for >c2
exit15:
        mov     ax, 8600h
exit_ax:        
        sti
        retf    2           ; discard flags (need to keep CF)
exit_iret:
        iret        

; ------------ SetEventWait
SetEventWait:
        push    ds
        push    40h
        pop     ds
        xor     ah, 1
        jz      short cancel
        test    ah, byte ptr UWaitFlag ; ah=1
        jnz     short busy  ; CF=0
        mov     ax, 1000-1  ; 1ms
        out     70h, ax     ; restart RTC timer
        mov     UFPtr[0], bx
        mov     UFPtr[2], es
        add     ax, dx
        adc     cx, 0
        mov     WaitCount[0], ax
        mov     WaitCount[2], cx
        mov     ah, 1       ; wait in progress
cancel:
        mov     byte ptr UWaitFlag, ah   
        int     70h
        stc                 ; no error
busy:   
        cmc                 ; eror        
nowait:
        pop     ds
        jmp     short exit15

; ------------ Wait
Wait1:
        push    es
        push    bx
        mov     ax, 8300h
        push    4ah
        pop     es
        xor     bx, bx      ; user wait flag address=0040:00a0
        int     15h         ; returns with IF = 1
        jc      short wbusy
wloop:        
        hlt   
        test    byte ptr es:[bx], 80h
        jz      short wloop
wbusy:        
        pop     bx
        pop     es
        jmp     short exit15
        

; ------------ ExtSize
ExtSize:
        mov     ax, FreeXMSKb
        jmp     short exit_ax
        
; ------------ GetConfig
GetConfig:
        xor     ax, ax
        push    cs
        pop     es
        mov     bx, offset SysParams
        jmp     short exit_ax
        
; ------------ Mouse 
Mouse:
        push    ds
        push    dx
        push    40h
        pop     ds
        test    byte ptr EquipmentWord, 4 ; ps2 mouse equipement word
        jnz     short mouse_present
if_err:
        mov     ax, 03a7h   ; interface error (no mouse present)
        out     64h, al     ; disable mouse
errexit:        
        stc                 ; error
exitok:        
        pushf               ; save CF
        in      al, 0a1h
        and     al, not 10h     
        out     0a1h, al    ; enable mouse interrupts
        call    enableKbIfPresent
        popf
        pop     dx
        pop     ds
        jmp     exit_ax
mouse_present:
        mov     al, ah                                  
        mov     ah, 1       ; invalid function
        cmp     al, 7
        ja      short errexit
        push    ax
        in      al, 0a1h
        or      al, 10h     
        out     0a1h, al    ; disable mouse interrupts
        sti                 ; allow interrupts for a short time, to flush possible pending KB/mouse requests
        mov     al, 0adh
        out     64h, al     ; disable kb interface
        pop     ax
        cmp     al, 1
        cli                 ; from now on we are working with ints disabled, as the following code is highly non re-entrant
        jb      short en_dis
        je      short reset
        cmp     al, 3
        jb      short sampling
        je      short resolution
        cmp     al, 5
        jb      short gettype
        je      short reset
        cmp     al, 6
        je      short extend

; ------------- set handler
        mov     HandlerPtr[0], bx
        mov     HandlerPtr[2], es
        jmp     short exit_success1        

; ------------- enable/disable
en_dis:
        mov     ax, 02f5h   ; ah = invalid input
        sub     al, bh
        cmp     bh, ah
        jnc     short errexit
        mov     ah, al
        call    sendcmd     ; enable/disable data reporting (CF = 1)
if_err1:        
        jc      short if_err
exit_success:
        mov     byte ptr DataCounter, 0
exit_success1:
        xor     ah, ah      ; success
        jmp     short exitok

; ------------- reset
reset:
        mov     ah, 0f6h    ; set defaults
        stc                 ; mouse command
        call    sendcmd     
        jc      short if_err
        mov     bx, 00aah
        mov     byte ptr PacketSize, bh ; 3bytes packet
        jmp     short exit_success

; ------------- sampling
sampling:
        cmp     bh, 6
badparam:
        mov     ah, 2       ; invalid input
        ja      short errexit
        shr     bx, 8
        mov     ah, cs:sample_tbl[bx]
        push    ax
        mov     ah, 0f3h    ; st sample rate
send2c:
        stc
        call    sendcmd              
        pop     ax
        jc      short if_err1
send1c:
        stc
        call    sendcmd
        jmp     short if_err1

; ------------- resolution
resolution:
        cmp     bh, 3
        ja      short badparam
        push    bx
        mov     ah, 0e8h    ; set resolution
        jmp     short send2c

; ------------- gettype
gettype:
        mov     ah, 0f2h
        stc
        call    sendcmd
        jc      short if_err1
        call    getps2byte
        jc      short if_err1
        mov     bh, al
        neg     al          ; CF=1 if al != 0
        adc     al, bh
        mov     byte ptr PacketSize, al ; 3 or 4 bytes packet
        jmp     short exit_success
        
; ------------- extended commands
extend:
        test    bh, bh
        jnz     short setscaling
        mov     ah, 0e9h    ; status request
        stc
        call    sendcmd
        jc      short if_err1
        call    getps2byte
        jc      short if_err1
        mov     bl, al
        call    getps2byte
        jc      short if_err1
        mov     cl, al
        call    getps2byte
        jc      short if_err1
        pop     dx  
        push    ax          ; replace dx on stack
        jmp     short exit_success
setscaling:    
        cmp     bh, 2
        ja      short badparam
        mov     ah, 0e5h    ; set scaling 1:1 or 2:1
        add     ah, bh
        jmp     short send1c

sample_tbl  db  10, 20, 40, 60, 80, 100, 200
SysParams   db  8, 0, 0fch, 0, 0
;--------------------------------------------------------------------------
; Feature byte 1
; b7: 1=DMA channel 3 used by hard disk
; b6: 1=2 interrupt controllers present
; b5: 1=RTC present
; b4: 1=BIOS calls int 15h/4Fh every key
; b3: 1=wait for extern event supported (Int 15h/41h)
; b2: 1=extended BIOS data area used
; b1: 0=AT or ESDI bus, 1=MicroChannel
; b0: 1=Dual bus (MicroChannel + ISA)
;--------------------------------------------------------------------------
            db      10h
;--------------------------------------------------------------------------
; Feature byte 2
; b7: 1=32-bit DMA supported
; b6: 1=int16h, function 9 supported
; b5: 1=int15h/C6h (get POS data) supported
; b4: 1=int15h/C7h (get mem map info) supported
; b3: 1=int15h/C8h (en/dis CPU) supported
; b2: 1=non-8042 kb controller
; b1: 1=data streaming supported
; b0: reserved
;--------------------------------------------------------------------------
            db      44h
;--------------------------------------------------------------------------
; Feature byte 3
; b7: not used
; b6: reserved
; b5: reserved
; b4: POST supports ROM-to-RAM enable/disable
; b3: SCSI on system board
; b2: info panel installed
; b1: Initial Machine Load (IML) system - BIOS on disk
; b0: SCSI supported in IML
;--------------------------------------------------------------------------
            db      0
;--------------------------------------------------------------------------
; Feature byte 4
; b7: IBM private
; b6: EEPROM present
; b5-3: ABIOS presence (011 = not supported)
; b2: private
; b1: memory split above 16Mb supported
; b0: POSTEXT directly supported by POST
;--------------------------------------------------------------------------
            db      0
;--------------------------------------------------------------------------
; Feature byte 5 (IBM)
; b1: enhanced mouse
; b0: flash EPROM
;--------------------------------------------------------------------------
            db      0                                                


; --------------------- INT 16h - keyboard interface ----------------
;       AH      Description
;       --      ------------------------------------------------
;       00h     Get a key from the keyboard, return code in AX.
;       01h     Test for available key, ZF=1 if none, ZF=0 and
;               AX contains next key code if key available.
;       02h     Get shift status. Returns shift key status in AL.
;       03h     Set Autorepeat rate. BH=0,1,2,3 (delay time in quarter seconds), BL=0..1Fh for 30 char/sec to 2 char/sec repeat rate.
;       05h     Store scan code (in CX) in the type ahead buffer.
;       10h     Get a key (same as 00h in this implementation).
;       11h     Test for key (same as 01h).
;       12h     Get extended key status. Returns status in AX.

AltKpd          equ     <ds:[19h]>
HeadPtr         equ     <ds:[1ah]>
TailPtr         equ     <ds:[1ch]>
Buffer          equ     <ds:[80h]>;1eh
EndBuf          equ     <ds:[82h]>;3eh

int16 proc near
        push    ds
        push    si
        push    40h
        pop     ds
        xchg    al, ah          ;shorter opcodes for al than ah
        dec     ax
        test    al, 0EFh        ;Check for 01h and 11h
        jz      short TestKey   ;TestKey does not need cld
        inc     ax
        cld
        test    al, 0EFh        ;Check for 0h and 10h
        jz      short GetKey
        cmp     al, 3           ;Check for 02h and 03h
        jb      short GetStatus
        je      short SetAutoRpt   
        cmp     al, 5           ;Check for StoreKey function.
        je      short StoreKey
        cmp     al, 9           ;Get KB functionality
        je      short kbfunc     
        cmp     al, 12h         ;Extended status call
        je      short ExtStatus
        cmp     al, 92h         ;stupid keyb.com 
        jne     short Exit
kbfunc:
        mov     al, 24h         ;AL=20h (fn 10h, 12h supported, set typematic supported)        
Exit:        
        pop     si
        pop     ds
        iret                    ; unknown function, Restores flags.

GetKey1:                        ; wait for interrupt
        hlt
GetKey: ; ----------- fn 00h, 10h
        mov     ah, 11h
        int     16h             ;See if key is available (IF becomes 1 after this int)
        jz      short GetKey1   ;Wait for keystroke.
        cli                     ;Critical region! Ints off.
        mov     si, HeadPtr     ;Ptr to next character.
        lodsw                   ;Get the character, Bump up HeadPtr
        cmp     si, EndBuf
        jb      short noWrap
        mov     si, Buffer
noWrap:             
        mov     HeadPtr, si
        jmp     short Exit

TestKey: ; ---------- fn 01h
        mov     si, HeadPtr
        cmp     si, TailPtr     ;ZF=1, if empty buffer
        lodsw                   ;BIOS returns avail keycode.
        sti                     ;Ints back on.
        pop     si
        pop     ds
        retf    2               ;Pop flags (ZF is important!)

StoreKey: ; ---------- fn 05h - Inserts the value in CX into the type ahead buffer.  
        mov     si, TailPtr     ;Address where we can put next key code.
        mov     [si], cx        ;Store the key code away
        inc     si
        inc     si              ;Move on to next entry in buf
        cmp     si, EndBuf
        jb      short NoWrap1
        mov     si, Buffer
 NoWrap1:
        mov     al, 1           ;no room
        cmp     si, HeadPtr     ;Data overrun?
        je      short Exit      ;if so, ignore key entry.
        mov     TailPtr, si
        dec     ax              ;al=0
        jmp     short Exit       

ExtStatus: ; ------- fn 12h - Retrieve the extended keyboard status and return it in AH, and the standard keyboard status in AL.    
        mov     al, KbdFlags2
        and     al, 01110111b   ;Clear final sysreq field, and final right alt bit.
        test    al, 100b        ;Test cur sysreq bit.
        jz      short NoSysReq  ;Skip if it's zero.
        sub     al, 10000100b   ;Set final sysreq bit, clear final right ctl bit.
NoSysReq:
        mov     ah, KbdFlags3
        and     ah, 1100b       ;Grab rt alt/ctrl bits.
        or      ah, al          ;Merge into AH.

GetStatus: ; --------- fn 02h     
        mov     al, KbdFlags1   ;Just return Std Status.
Exit1:
        jmp     short Exit

SetAutoRpt: ; ------ fn 03h
        cmp     ah, 5
        jne     short Exit
        push    dx
        shl     bh, 5
        and     bl, 1fh
        or      bl, bh
        and     bl, 7fh
        mov     ah, 0           ; wait LED update progress to finalize
        call    WaitFlag        ; leaves with IF=0
        jc      short timeout
        or      byte ptr KbdFlags4, SetRepeat    ; set auto repeat in progress
        mov     ah, 0f3h        ; set typematic rate and delay
        push    bx
        xor     bl, bl          ; send to kb
        call    sendps2byte
        pop     bx
        jc      short timeout1  ; send timeout
        mov     ah, SetRepeat or AckReceived ; test if ACK received
        call    WaitFlag
        jc      short timeout1
        mov     ah, bl
        xor     bl, bl          ; send to kb
        call    sendps2byte     ; send data
timeout1:
        and     byte ptr KbdFlags4, not SetRepeat   
timeout:
        pop     dx
        jmp     short Exit1


WaitFlag:   ; ah = desired KbdFlags4 & (AckReceived | LEDUpdate | SetRepeat)
        mov     dx, 3dah
        mov     bh, 8*25    ; wait for max 25 * VGA frame time
wf_loop:
        cli
        mov     al, KbdFlags4
        and     al, AckReceived or LEDUpdate or SetRepeat
        cmp     al, ah
        je      short wf_ok ; flag ok, CF=0
        sti
        in      al, dx      ; get vblank
        xor     al, bh
        and     al, 8h
        sub     bh, al
        jnc     short wf_loop     ; IBF - buffer full, no timeout
wf_ok:
        ret
int16 endp

; --------------------- INT 18h - BIOS Basic ------------------
int18 proc near
        push    cs
        pop     es
        mov     si, offset booterrmsg
        call    prts
		call	flush

;-------------- RS232 bootstrap
        mov     al, 0b4h
        out     43h, al
        mov     ax, 0f000h
        out     42h, al
        out     42h, al      ; 18Hz PIT CH2
		out		1, ax		; disable auto flush on vblank (bit0)
	  mov ds,ax
	  mov es,ax

        mov si,100h
	  call srecb
        cli
	  mov bh,ah
	  call srecb
	  mov bl,ah
sloop:	
	  call srecb
	  mov [si],ah
	  inc si
	  dec bx
	  jnz short sloop
	  db 0eah
        dw 100h,0f000h

booterrmsg db   'No boot device available, waiting on RS232 (115200bps, f000:100) ...', 13, 10, 0
int18 endp

; --------------------- INT 19h - OS Bootstrap loader ------------------
int19 proc near
        mov     ax, 201h
        mov     cx, 1
        mov     dx, 80h
        push    0
        pop     es
        mov     bx, 7c00h
        int     13h
        jc      int19err
        db      0eah
        dw      7c00h, 0     ; jmp far 0000h:7c00h
int19err:
        int     18h
int19 endp


; --------------------- INT 1ah - Get System Time ------------------
int1a proc near
        push    ds
        push    40h
        pop     ds
        cmp     ah, 1
        ja      clockexit
        je      setclock
        mov     dx, ds:[6ch]    ; read clock
        mov     cx, ds:[6eh]
        mov     al, ds:[70h]
clockexit1:
        mov     byte ptr ds:[70h], 0
clockexit:
        cmc     ; CF = 1 on error
        pop     ds
        sti
        retf    2

setclock:
        mov     ds:[6ch], dx
        mov     ds:[6eh], cx
        stc
        jmp     short clockexit1    
int1a endp


; --------------------- INT 70h - RTC ------------------
int70 proc near
        push    ds
        push    40h
        pop     ds
        test    byte ptr UWaitFlag, 1    ; is wait in progress?
		jz      short exit1
        sub     word ptr WaitCount[0], 1000
        sbb     word ptr WaitCount[2], 0
        jnc     short exit
        mov     byte ptr UWaitFlag, 0
        push    bx
        lds     bx, UFPtr
        or      byte ptr [bx], 80h
        pop     bx
exit1:
		push	ax
		xor		ax, ax
		out		70h, ax	; stop RTC
		pop		ax
exit: 
        pop     ds
        iret
int70 endp

        
; --------------------- INT 74h - mouse ------------------
int74 proc near
        cld
        pusha
        push    ds
        push    40h
        pop     ds
        mov     ah, 0
        in      al, 60h
        mov     bx, ax
        inc     byte ptr DataCounter
        mov     al, DataCounter
        mov     si, ax
        sub     al, 3
        ja      short docall
        mov     DataBuffer[si-1], bl
        cmp     al, PacketSize
        jne     short nocall
        mov     bl, 0
docall:
        mov     byte ptr DataCounter, bh    ; BH=0
        mov     si, offset DataBuffer-2
        lodsw
        or      ax, [si-4]
        jz      short nocall
        sti
        push    es
        mov     ah, 0
        lodsb
        push    ax
        lodsb
        push    ax
        lodsb
        push    ax
        push    bx
        call    far ptr [si-7]
        add     sp, 8
        pop     es
nocall:        
        pop     ds
        popa
        iret
int74 endp


; ----------------  serial receive byte 115200 bps --------------
srecb:  mov     ah, 80h
        mov     dx, 3dah
        mov     cx, -5aeh ; (half start bit)
srstb:  in      al, dx
	  shr     al, 2
	  jc      short srstb
        in      al, 42h ; lo counter
        add     ch, al
        in      al, 42h ; hi counter, ignore
l1:
        call    dlybit
	  in      al, dx
        shr     al, 2
	  rcr     ah, 1
	  jnc     short l1
dlybit:
        sub     cx, 0a5bh  ;  (full bit)
dly1:
        in      al, 42h
        cmp     al, ch
        in      al, 42h
        jnz     short dly1
        ret

; -------------------- KB/Mouse access ----------------
sendps2byte proc near   ; ah=data, bl!=0 for mouse, 0 for kb. returns cf=1 if timeout (al = 8)
; changes BH, AL
        push    dx
        mov     dx, 3dah
        mov     bh, 8*5
sps2b2:
        in      al, 64h
        test    al, 2
        jz      short sps2b1; buffer empty
        in      al, dx      ; get vblank
        xor     al, bh
        and     al, 8h
        sub     bh, al
        jnc     short sps2b2; IBF - buffer full, no timeout
        jmp     short exit  ; timeout, CF=1
sps2b1:
        test    bl, bl      ; CF=0
        jz      short sps2_kb
        mov     al, 0d4h    ; next mouse
        out     64h, al
sps2_kb:
        mov     al, ah
        out     60h, al     ; send byte
exit:        
        pop     dx
        ret
sendps2byte endp

getps2byte proc near    ; returns al=data, zf=0 for mouse, 1 for kb, cf=1 if timeout (al=8)
; changes BH, DX, AL
        mov     dx, 3dah
        mov     bh, 8*5
gps2b2:
        in      al, 64h
        test    al, 1
        jnz     short gps2b1     ; OBF (buffer full), continue
        in      al, dx     ; get vblank
        xor     al, bh
        and     al, 8
        sub     bh, al
        jnc     short gps2b2     ; buffer empty, no timeout
        ret                ; timeout, CF=1
gps2b1:
        test    al, 20h    ; CF=0, ZF <- !MOBF
        in      al, 60h    ; read byte (if IF=1, this data may be invalid)
        ret
getps2byte endp

sendcmd proc near     ; ah = command, CF=1 for mouse, CF=0 for kb. returns CF=1 on error
        sbb     bl, bl      ; bl <- CF
        call    sendps2byte 
        jc      short exit
retry:        
        call    getps2byte
        jc      short exit        
        cmp     al, 0fah    ; ack (returns CF=1 on error, when al=8)
        jne     short retry
exit:
        ret
sendcmd endp

enableKbIfPresent proc near ; input DS = 40h
; modify AL, flags
        test    byte ptr KbdFlags3, 10h
        jz      short noenablekb
        mov     al, 0aeh
        out     64h, al     ; enable kb interface
noenablekb:        
        ret
enableKbIfPresent endp

; ----------------------- default interrupt handler ---------------
defint  proc near
        iret
defint  endp             

; ------------------------------- flush --------------------------
flush:
        pop     cs:flushret
flush_nostack:        
        mov     cs:flushbh, bx
        mov     bx, 4000h       ; flush all cache lines
flush1:        
        test    bl, cs:[bx + 0bf00h]
        sub		bx, 40h
        jnz     short flush1
        mov     bx, cs:flushbh
        jmp     word ptr cs:flushret
flushret dw 0
flushbh  dw 0          

; ------------------------------- misc --------------------------
dispAX: 
        push    dx
        xor     dx, dx
        div     word ptr cs:ten
        test    ax, ax
        jz      dispAX1
        call    dispAX
dispAX1:
        xchg    ax, dx
        add     ax, 0e00h + '0'
        int     10h
        pop     dx
        ret        
ten     dw      10

prts:   ; es:si = string
        mov     ah, 0eh    
        lodsb   es:[si]
        or      al, al
        jz      short prtse
        int     10h
        jmp     short prts
prtse:
        ret



;---------------------  read/write byte ----------------------
sdrb:   mov al,0ffh
sdsb:               ; in AL=byte, DX = 03dah, out AX=result
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        in      ax, dx
        ret

;---------------------  write block ----------------------
sdwblk proc near              ; in SI=data ptr, DX=03dah, CX=size
        shr     cx, 1
sdwblk1:
        lodsb
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        lodsb
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        add     ax, ax
        out     dx, al
        loop    short sdwblk1
        ret
sdwblk endp

;---------------------  read block ----------------------
sdrblk proc near              ; in DI=data ptr, DX=03dah, CX=size. Returns CF = 0
        mov     al, 0ffh
        out     dx, al
        shr     cx, 1         ; CF = 0
        out     dx, al
        jmp     short sdrblk2 
sdrblk1:
        out     dx, al
        mov     [di], ah
        out     dx, al
        inc     di
sdrblk2:
        out     dx, al
        nop
        out     dx, al
        nop
        out     dx, al
        nop
        out     dx, al
        nop
        out     dx, al
        nop
        out     dx, al
        in      ax, dx
        out     dx, al
        mov     [di], ah
        out     dx, al
        inc     di
        out     dx, al
        nop
        out     dx, al
        nop
        out     dx, al
        nop
        out     dx, al
        nop
        out     dx, al
        nop
        out     dx, al
        in      ax, dx
        loop    short sdrblk1
        mov     [di], ah
        inc     di
        ret
sdrblk endp

;---------------------  verify block ----------------------
sdvblk:              ; in DI=data ptr, DX=03dah, CX=size. Returns CF=1 on error
        push    bx
        xor     bl, bl
sdvblk1:
        call    sdrb
        sub     ah, [di]
        or      bl, ah
        inc     di
        loop    short sdvblk1
        neg     bl  ; CF=1 if BL != 0
        pop     bx
        ret

;---------------------  write command ----------------------
sdcmd8T:
        call    sdrb
sdcmd:              ; in SI=6 bytes cmd buffer, DX=03dah, out AH = 0ffh on error
        mov     cx, 6
        call    sdwblk
sdresp:
        xor     si, si
sdresp1:
        call    sdrb
        inc     si
        jz      short sdcmd1
        cmp     ah, 0ffh
        je      short sdresp1
sdcmd1: ret         

;---------------------  read ----------------------
sdverify:
        push    sdvblk
        jmp     short sdread1
sdread:   ; DX:AX sector, ES:BX buffer, CX=sectors. returns AX=read sectors
        push    sdrblk   ; push proc address (read or verify) on stack
sdread1:        
        push    ax
        mov     al, dl
        push    ax
        mov     dl, 51h  ; CMD17
        cmp     cx, 1
        je      short sdr1s
        inc     dx      ; CMD18 - multiple sectors
sdr1s:
        push    dx
        mov     si, sp 

        mov     dx, 3dah
        mov     ah, 1
        out     dx, ax       ; CS on
        mov     di, bx
        mov     bx, cx
        mov     bp, cx       ; save sectors number
        push    ss
        pop     ds
	mov	byte ptr [si+5], 0ffh ; checksum
        call    sdcmd
        add     sp, 6
        or      ah, ah
        jnz     short sdr11   ; error
        push    es
        pop     ds
sdrms:
        mov     ax, di
        shr     ax, 4
        mov     si, ds
        add     ax, si
        mov     ds, ax
        and     di, 15
        call    sdresp     ; wait for 0feh token
        cmp     ah, 0feh
        jne     short sdr11; read token error 
        mov     ch, 2      ; 512 byte sector
        pop     si
        call    si         ; sdrblk or sdvblk
        push    si
        pushf
        call    sdrb       ; ignore CRC
        call    sdrb       ; ignore CRC
        popf
        jc      short sdr3 ; verify error   
        dec     bx
        jnz     short sdrms; multiple sectors
sdr3:        
        cmp     bp, 1
        je      short sdr11; single sector
        mov     si, offset SD_CMD12 ; stop transfer
        push    cs
        pop     ds
        call    sdcmd
sdr2:
        shr     ah, 1
        jnc     short sdr11
        call    sdrb
        jmp     short sdr2
sdr11:
        pop     ax         ; remove proc address from stack
sdr1:       
        xor     ax, ax
        out     dx, ax
        call    sdrb       ; 8T
        mov     ax, bp
        sub     ax, bx
        ret     

;---------------------  write ----------------------
sdwrite:   ; DX:AX sector, ES:BX buffer, CX=sectors, returns AX=wrote sectors
        push    ax
        mov     al, dl
        push    ax
        mov     dl, 58h  ; CMD24
        cmp     cx, 1
        je      short sdw1s
        inc     dx      ; CMD25 - multiple sectors
sdw1s:
        push    dx
        mov     si, sp 

        mov     dx, 3dah
        mov     ah, 1
        out     dx, ax       ; CS on
        mov     bp, cx       ; save sectors number
        push    ss
        pop     ds
	mov	byte ptr [si+5], 0ffh ; checksum
        call    sdcmd
        add     sp, 6
        mov     si, bx
        mov     bx, bp
        or      ah, ah
        jnz     short sdr1   ; error
        push    es
        pop     ds
sdwms:
        mov     ax, si
        shr     ax, 4
        mov     di, ds
        add     ax, di
        mov     ds, ax
        and     si, 15
        mov     al, 0feh      ; start token
        cmp     bp, 1
        je      short sdw1s1
        mov     al, 0fch   ; multiple sectors
sdw1s1:        
        call    sdsb     
        mov     ch, 2      ; 512 byte sector
        call    sdwblk
        call    sdrb       ; ignore CRC
        call    sdrb       ; ignore CRC
        call    sdrb       ; read response byte xxx00101
        and     ah, 0eh
        cmp     ah, 4
        jne     short sdr1 ; write error
sdwwait:
        call    sdrb
        shr     ah, 1
        jnc     short sdwwait     ; wait write completion
        dec     bx
        jnz     short sdwms       ; multiple sectors

        cmp     bp, 1
        je      short sdr1
        mov     al, 0fdh     ; multiple end transfer
        call    sdsb 
		call	sdrb     
sdwwait1:
        call    sdrb
        shr     ah, 1
        jnc     short sdwwait1     ; wait write completion
        jmp     sdr1
        
;---------------------  init SD ----------------------
sdinit  proc near       ; returns AX = num kilosectors
        push    ds
        push    cx
        push    dx
        push    si
        push    di
        mov     dx, 3dah
        mov     cx, 10
sdinit1:                   ; send 80T
        call    sdrb
        loop    short sdinit1

        mov     ah, 1
        out     dx, ax       ; select SD

        mov     si, offset SD_CMD0
        push    cs
        pop     ds
        call    sdcmd
        dec     ah
        jnz     short sdexit ; error
        
        mov     si, offset SD_CMD8
        call    sdcmd8T
        dec     ah
        jnz     short sdexit ; error
        mov     cl, 4
        sub     sp, cx
        mov     di, sp
        push    ss
        pop     ds
        call    sdrblk
        pop     ax
        pop     ax
        cmp     ah, 0aah
        jne     short sdexit ; CMD8 error
repinit:        
        mov     si, offset SD_CMD55
        push    cs
        pop     ds
        call    sdcmd8T
        call    sdrb
        mov     si, offset SD_CMD41
        call    sdcmd
        dec     ah
        jz      short repinit
        
        mov     si, offset SD_CMD58
        call    sdcmd8T
        mov     cl, 4
        sub     sp, cx
        mov     di, sp
        push    ss
        pop     ds
        call    sdrblk
        pop     ax
        test    al, 40h     ; test OCR bit 30 (CCS)
        pop     ax
        jz      short sdexit; no SDHC

        mov     si, offset SD_CMD9 ; get size info
        push    cs
        pop     ds
        call    sdcmd8T
        or      ah, ah
        jnz     short sdexit
        call    sdresp     ; wait for 0feh token
        cmp     ah, 0feh
        jne     short sdexit
        mov     cl, 18       ; 16bytes + 2bytes CRC
        sub     sp, cx
        mov     di, sp
        push    ss
        pop     ds
        call    sdrblk
        mov     cx, [di-10]
        rol     cx, 8
        inc     cx
        mov     sp, di
sdexit: 
        xor     ax, ax       ; raise CS
        out     dx, ax
        call    sdrb
        pop     di
        pop     si
        pop     dx
        mov     ax, cx       
        pop     cx
        pop     ds
        ret
sdinit endp
    
SD_CMD0     db  40h, 0, 0, 0, 0, 95h
SD_CMD8     db  48h, 0, 0, 1, 0aah, 087h
SD_CMD9     db  49h, 0, 0, 0, 0, 0ffh
SD_CMD12    db  4ch, 0, 0, 0, 0, 0ffh
SD_CMD41    db  69h, 40h, 0, 0, 0, 0ffh
SD_CMD55    db  77h, 0, 0, 0, 0, 0ffh
SD_CMD58    db  7ah, 0, 0, 0, 0, 0ffh


Pal256:
		db  00h,00h,00h, 00h,00h,2ah, 00h,2ah,00h, 00h,2ah,2ah, 2ah,00h,00h, 2ah,00h,2ah, 2ah,15h,00h, 2ah,2ah,2ah 
		db  15h,15h,15h, 15h,15h,3fh, 15h,3fh,15h, 15h,3fh,3fh, 3fh,15h,15h, 3fh,15h,3fh, 3fh,3fh,15h, 3fh,3fh,3fh 
		db  00h,00h,00h, 05h,05h,05h, 08h,08h,08h, 0bh,0bh,0bh, 0eh,0eh,0eh, 11h,11h,11h, 14h,14h,14h, 18h,18h,18h 
		db  1ch,1ch,1ch, 20h,20h,20h, 24h,24h,24h, 28h,28h,28h, 2dh,2dh,2dh, 32h,32h,32h, 38h,38h,38h, 3fh,3fh,3fh 
		db  00h,00h,3fh, 10h,00h,3fh, 1fh,00h,3fh, 2fh,00h,3fh, 3fh,00h,3fh, 3fh,00h,2fh, 3fh,00h,1fh, 3fh,00h,10h 
		db  3fh,00h,00h, 3fh,10h,00h, 3fh,1fh,00h, 3fh,2fh,00h, 3fh,3fh,00h, 2fh,3fh,00h, 1fh,3fh,00h, 10h,3fh,00h 
		db  00h,3fh,00h, 00h,3fh,10h, 00h,3fh,1fh, 00h,3fh,2fh, 00h,3fh,3fh, 00h,2fh,3fh, 00h,1fh,3fh, 00h,10h,3fh 
		db  1fh,1fh,3fh, 27h,1fh,3fh, 2fh,1fh,3fh, 37h,1fh,3fh, 3fh,1fh,3fh, 3fh,1fh,37h, 3fh,1fh,2fh, 3fh,1fh,27h
		db	00h, 01h, 02h, 03h, 04h, 05h, 06h, 07h, 08h, 09h, 0ah, 0bh, 0ch, 0dh, 0eh, 0fh	; EGA palette registers

;		db  3fh,1fh,1fh, 3fh,27h,1fh, 3fh,2fh,1fh, 3fh,37h,1fh, 3fh,3fh,1fh, 37h,3fh,1fh, 2fh,3fh,1fh, 27h,3fh,1fh 
;		db  1fh,3fh,1fh, 1fh,3fh,27h, 1fh,3fh,2fh, 1fh,3fh,37h, 1fh,3fh,3fh, 1fh,37h,3fh, 1fh,2fh,3fh, 1fh,27h,3fh 
;		db  2dh,2dh,3fh, 31h,2dh,3fh, 36h,2dh,3fh, 3ah,2dh,3fh, 3fh,2dh,3fh, 3fh,2dh,3ah, 3fh,2dh,36h, 3fh,2dh,31h 
;		db  3fh,2dh,2dh, 3fh,31h,2dh, 3fh,36h,2dh, 3fh,3ah,2dh, 3fh,3fh,2dh, 3ah,3fh,2dh, 36h,3fh,2dh, 31h,3fh,2dh 
;		db  2dh,3fh,2dh, 2dh,3fh,31h, 2dh,3fh,36h, 2dh,3fh,3ah, 2dh,3fh,3fh, 2dh,3ah,3fh, 2dh,36h,3fh, 2dh,31h,3fh 
;		db  00h,00h,1ch, 07h,00h,1ch, 0eh,00h,1ch, 15h,00h,1ch, 1ch,00h,1ch, 1ch,00h,15h, 1ch,00h,0eh, 1ch,00h,07h 
;		db  1ch,00h,00h, 1ch,07h,00h, 1ch,0eh,00h, 1ch,15h,00h, 1ch,1ch,00h, 15h,1ch,00h, 0eh,1ch,00h, 07h,1ch,00h 
;		db  00h,1ch,00h, 00h,1ch,07h, 00h,1ch,0eh, 00h,1ch,15h, 00h,1ch,1ch, 00h,15h,1ch, 00h,0eh,1ch, 00h,07h,1ch 

;		db  0eh,0eh,1ch, 11h,0eh,1ch, 15h,0eh,1ch, 18h,0eh,1ch, 1ch,0eh,1ch, 1ch,0eh,18h, 1ch,0eh,15h, 1ch,0eh,11h 
;		db  1ch,0eh,0eh, 1ch,11h,0eh, 1ch,15h,0eh, 1ch,18h,0eh, 1ch,1ch,0eh, 18h,1ch,0eh, 15h,1ch,0eh, 11h,1ch,0eh 
;		db  0eh,1ch,0eh, 0eh,1ch,11h, 0eh,1ch,15h, 0eh,1ch,18h, 0eh,1ch,1ch, 0eh,18h,1ch, 0eh,15h,1ch, 0eh,11h,1ch 
;		db  14h,14h,1ch, 16h,14h,1ch, 18h,14h,1ch, 1ah,14h,1ch, 1ch,14h,1ch, 1ch,14h,1ah, 1ch,14h,18h, 1ch,14h,16h 
;		db  1ch,14h,14h, 1ch,16h,14h, 1ch,18h,14h, 1ch,1ah,14h, 1ch,1ch,14h, 1ah,1ch,14h, 18h,1ch,14h, 16h,1ch,14h 
;		db  14h,1ch,14h, 14h,1ch,16h, 14h,1ch,18h, 14h,1ch,1ah, 14h,1ch,1ch, 14h,1ah,1ch, 14h,18h,1ch, 14h,16h,1ch 
;		db  00h,00h,10h, 04h,00h,10h, 08h,00h,10h, 0ch,00h,10h, 10h,00h,10h, 10h,00h,0ch, 10h,00h,08h, 10h,00h,04h 
;		db  10h,00h,00h, 10h,04h,00h, 10h,08h,00h, 10h,0ch,00h, 10h,10h,00h, 0ch,10h,00h, 08h,10h,00h, 04h,10h,00h 

;		db  00h,10h,00h, 00h,10h,04h, 00h,10h,08h, 00h,10h,0ch, 00h,10h,10h, 00h,0ch,10h, 00h,08h,10h, 00h,04h,10h 
;		db  08h,08h,10h, 0ah,08h,10h, 0ch,08h,10h, 0eh,08h,10h, 10h,08h,10h, 10h,08h,0eh, 10h,08h,0ch, 10h,08h,0ah 
;		db  10h,08h,08h, 10h,0ah,08h, 10h,0ch,08h, 10h,0eh,08h, 10h,10h,08h, 0eh,10h,08h, 0ch,10h,08h, 0ah,10h,08h 
;		db  08h,10h,08h, 08h,10h,0ah, 08h,10h,0ch, 08h,10h,0eh, 08h,10h,10h, 08h,0eh,10h, 08h,0ch,10h, 08h,0ah,10h 
;		db  0bh,0bh,10h, 0ch,0bh,10h, 0dh,0bh,10h, 0fh,0bh,10h, 10h,0bh,10h, 10h,0bh,0fh, 10h,0bh,0dh, 10h,0bh,0ch 
;		db  10h,0bh,0bh, 10h,0ch,0bh, 10h,0dh,0bh, 10h,0fh,0bh, 10h,10h,0bh, 0fh,10h,0bh, 0dh,10h,0bh, 0ch,10h,0bh 
;		db  0bh,10h,0bh, 0bh,10h,0ch, 0bh,10h,0dh, 0bh,10h,0fh, 0bh,10h,10h, 0bh,0fh,10h, 0bh,0dh,10h, 0bh,0ch,10h 
;		db  00h,00h,00h, 00h,00h,00h, 00h,00h,00h, 00h,00h,00h, 00h,00h,00h, 00h,00h,00h, 00h,00h,00h, 00h,00h,00h

PalEGA:
		db	00h,00h,00h, 00h,00h,2ah, 00h,2ah,00h, 00h,2ah,2ah, 2ah,00h,00h, 2ah,00h,2ah, 2ah,15h,00h, 2ah,2ah,2ah
		db	00h,00h,00h, 00h,00h,2ah, 00h,2ah,00h, 00h,2ah,2ah, 2ah,00h,00h, 2ah,00h,2ah, 2ah,15h,00h, 2ah,2ah,2ah
		db	15h,15h,15h, 15h,15h,3fh, 15h,3fh,15h, 15h,3fh,3fh, 3fh,15h,15h, 3fh,15h,3fh, 3fh,3fh,15h, 3fh,3fh,3fh
		db	15h,15h,15h, 15h,15h,3fh, 15h,3fh,15h, 15h,3fh,3fh, 3fh,15h,15h, 3fh,15h,3fh, 3fh,3fh,15h, 3fh,3fh,3fh
		db	00h,00h,00h, 00h,00h,2ah, 00h,2ah,00h, 00h,2ah,2ah, 2ah,00h,00h, 2ah,00h,2ah, 2ah,15h,00h, 2ah,2ah,2ah
		db	00h,00h,00h, 00h,00h,2ah, 00h,2ah,00h, 00h,2ah,2ah, 2ah,00h,00h, 2ah,00h,2ah, 2ah,15h,00h, 2ah,2ah,2ah
		db	15h,15h,15h, 15h,15h,3fh, 15h,3fh,15h, 15h,3fh,3fh, 3fh,15h,15h, 3fh,15h,3fh, 3fh,3fh,15h, 3fh,3fh,3fh
		db	15h,15h,15h, 15h,15h,3fh, 15h,3fh,15h, 15h,3fh,3fh, 3fh,15h,15h, 3fh,15h,3fh, 3fh,3fh,15h, 3fh,3fh,3fh
		db	00h, 01h, 02h, 03h, 04h, 05h, 06h, 07h, 10h, 11h, 12h, 13h, 14h, 15h, 16h, 17h	; EGA palette registers

PalVGA:
		db	00h,00h,00h, 00h,00h,2ah, 00h,2ah,00h, 00h,2ah,2ah, 2ah,00h,00h, 2ah,00h,2ah, 2ah,2ah,00h, 2ah,2ah,2ah
		db	00h,00h,15h, 00h,00h,3fh, 00h,2ah,15h, 00h,2ah,3fh, 2ah,00h,15h, 2ah,00h,3fh, 2ah,2ah,15h, 2ah,2ah,3fh
		db	00h,15h,00h, 00h,15h,2ah, 00h,3fh,00h, 00h,3fh,2ah, 2ah,15h,00h, 2ah,15h,2ah, 2ah,3fh,00h, 2ah,3fh,2ah
		db	00h,15h,15h, 00h,15h,3fh, 00h,3fh,15h, 00h,3fh,3fh, 2ah,15h,15h, 2ah,15h,3fh, 2ah,3fh,15h, 2ah,3fh,3fh
		db	15h,00h,00h, 15h,00h,2ah, 15h,2ah,00h, 15h,2ah,2ah, 3fh,00h,00h, 3fh,00h,2ah, 3fh,2ah,00h, 3fh,2ah,2ah
		db	15h,00h,15h, 15h,00h,3fh, 15h,2ah,15h, 15h,2ah,3fh, 3fh,00h,15h, 3fh,00h,3fh, 3fh,2ah,15h, 3fh,2ah,3fh
		db	15h,15h,00h, 15h,15h,2ah, 15h,3fh,00h, 15h,3fh,2ah, 3fh,15h,00h, 3fh,15h,2ah, 3fh,3fh,00h, 3fh,3fh,2ah
		db	15h,15h,15h, 15h,15h,3fh, 15h,3fh,15h, 15h,3fh,3fh, 3fh,15h,15h, 3fh,15h,3fh, 3fh,3fh,15h, 3fh,3fh,3fh
		db	00h, 01h, 02h, 03h, 04h, 05h, 14h, 07h, 38h, 39h, 3ah, 3bh, 3ch, 3dh, 3eh, 3fh	; EGA palette registers

IFDEF SCANCODE1 ; use SCANCODE1
KeyIndex:
        db	0, 82, 49, 50, 52, 51, 54, 55    ;0-7
        db 56, 57, 60, 59, 65, 68, 72, 47    ;8-f
        db	1,  5,  9, 13, 12, 18, 21, 23    ;10-17
        db 24, 26, 67, 70, 69,  0,  4,  3    ;18-1f
        db	8, 11, 17, 16, 20, 22, 25, 64    ;20-27
        db 66, 48,  0, 71,  2,  7,  6, 10    ;28-2f
        db 15, 14, 19, 58, 61, 62,  0, 87    ;30-37
        db	0, 53,  0, 40, 41, 39, 46, 38    ;38-3f
        db 45, 90, 44, 79, 43,  0, 89, 29    ;40-47
        db 34, 36, 86, 28, 37, 33, 84, 27    ;48-4f
        db 32, 35, 30, 31,  0,  0,	0, 83    ;50-57  
        db 42
E0KeyList:
	db	35h, 1ch, 4fh, 4bh, 47h, 52h, 53h, 50h, 4dh, 48h, 51h, 49h 

ELSE    ; use SCANCODE2

KeyIndex:
        db	0, 79,  0, 38, 39, 40, 41, 42
        db	0, 43, 44, 45, 46, 47, 48,  0
        db	0,  0,  0,  0,  0,  1, 49,  0
        db	0,  0,  2,	3,  4,  5, 50,  0
        db	0,  6,  7,	8,  9, 51, 52,  0
        db	0, 53, 10, 11, 12, 13, 54,  0
        db	0, 14, 15, 16, 17, 18, 55,  0
        db	0,  0, 19, 20, 21, 56, 57,  0
        db	0, 58, 22, 23, 24, 59, 60,  0
        db	0, 61, 62, 25, 64, 26, 65,  0
        db	0,  0, 66,	0, 67, 68,	0,  0
        db	0,  0, 69, 70,  0, 71,	0,  0
        db	0,  0,  0,	0,  0,  0, 72,  0
        db	0, 27,  0, 28, 29,  0,	0,  0
        db 30, 31, 32, 37, 33, 34, 82,  0
        db 83, 84, 35, 86, 87, 36, 89,  0
        db	0,  0,  0,	90 	
E0KeyList:
	db	4ah, 5ah, 69h, 6bh, 6ch, 70h, 71h, 72h, 74h, 75h, 7ah, 7dh 

ENDIF

E0KeyIndex:
	db	63,  69,  73,  74,  75,  76,  77,  78,  80,  81,  85,  88

KeyCode:	  
; Keys affected by CapsLock
;		norm   shft   ctrl   alt
        dw	0000h, 0000h, 0000h, 0000h ;17 - <0>
        dw	1071h, 1051h, 1011h, 1000h ;15 - Q, (E0)PrevTrack <1>
        dw	2c7ah, 2c5ah, 2c1ah, 2c00h ;1a - Z <2>
        dw	1f73h, 1f53h, 1f13h, 1f00h ;1b - S <3>
        dw	1e61h, 1e41h, 1e01h, 1e00h ;1c - A <4>
        dw	1177h, 1157h, 1117h, 1100h ;1d - W <5>
        dw	2e63h, 2e43h, 2e03h, 2e00h ;21 - C, (E0)Volume Down <6>
        dw	2d78h, 2d58h, 2d18h, 2d00h ;22 - X <7>
        dw	2064h, 2044h, 2004h, 2000h ;23 - D, (E0)Mute <8>
        dw	1265h, 1245h, 1205h, 1200h ;24 - E <9>
        dw	2f76h, 2f56h, 2f16h, 2f00h ;2a - V <10>
        dw	2166h, 2146h, 2106h, 2100h ;2b - F, (E0)Calculator <11>
        dw	1474h, 1454h, 1414h, 1400h ;2c - T <12>
        dw	1372h, 1352h, 1312h, 1300h ;2d - R <13>
        dw	316eh, 314eh, 310eh, 3100h ;31 - N <14>
        dw	3062h, 3042h, 3002h, 3000h ;32 - B, (E0)Volume Up <15>
        dw	2368h, 2348h, 2308h, 2300h ;33 - H <16>
        dw	2267h, 2247h, 2207h, 2200h ;34 - G, (E0)Play/Pause <17>
        dw	1579h, 1559h, 1519h, 1500h ;35 - Y <18>
        dw	326dh, 324dh, 320dh, 3200h ;3a - M, (E0)WWW Home <19>
        dw	246ah, 244ah, 240ah, 2400h ;3b - J, (E0)Stop <20>
        dw	1675h, 1655h, 1615h, 1600h ;3c - U <21>
        dw	256bh, 254bh, 250bh, 2500h ;42 - K <22>
        dw	1769h, 1749h, 1709h, 1700h ;43 - I <23>
        dw	186fh, 184fh, 180fh, 1800h ;44 - O <24>
        dw	266ch, 264ch, 260ch, 2600h ;4b - L <25>
        dw	1970h, 1950h, 1910h, 1900h ;4d - P, (E0)Next Track <26>
; keys affected by NumLock	
        dw	4f00h, 4f31h, 7500h, 0002h ;69 - KP1 <27>
        dw	4b00h, 4b34h, 7300h, 0005h ;6b - KP4 <28>
        dw	4700h, 4737h, 7700h, 0008h ;6c - KP7 <29>
        dw	5200h, 5230h, 9200h, 0001h ;70 - KP0 <30>
        dw	5300h, 532eh, 9300h, 0000h ;71 - KP. <31>
        dw	5000h, 5032h, 9100h, 0003h ;72 - KP2 <32>
        dw	4d00h, 4d36h, 7400h, 0007h ;74 - KP6 <33>
        dw	4800h, 4838h, 8d00h, 0009h ;75 - KP8 <34>
        dw	5100h, 5133h, 7600h, 0004h ;7a - KP3 <35>
        dw	4900h, 4939h, 8400h, 000ah ;7d - KP9 <36>
        dw	4c00h, 4c35h, 8f00h, 0006h ;73 - KP5 --- on VMWare, it does not send 4c00 <37>
; keys unaffected by CapsLock or N
        dw	3f00h, 5800h, 6200h, 6c00h ;03 - F5 <38>
        dw	3d00h, 5600h, 6000h, 6a00h ;04 - F3 <39>
        dw	3b00h, 5400h, 5e00h, 6800h ;05 - F1 <40>
        dw	3c00h, 5500h, 5f00h, 6900h ;06 - F2 <41>
        dw	8600h, 8800h, 8a00h, 8c00h ;07 - F12 <42>	
        dw	4400h, 5d00h, 6700h, 7100h ;09 - F10 <43>
        dw	4200h, 5b00h, 6500h, 6f00h ;0a - F8 <44>
        dw	4000h, 5900h, 6300h, 6d00h ;0b - F6 <45>
        dw	3e00h, 5700h, 6100h, 6b00h ;0c - F4 <46>
        dw	0f09h, 0f00h, 9400h, 0000h ;0d - TAB <47>	
        dw	2960h, 297eh, 0000h, 2900h ;0e - ` ~ <48>	
        dw	0231h, 0221h, 0000h, 7800h ;16 - 1 ! <49>	
        dw	0332h, 0340h, 0300h, 7900h ;1e - 2 @ <50>	
        dw	0534h, 0524h, 0000h, 7b00h ;25 - 4 $ <51>
        dw	0433h, 0423h, 0000h, 7a00h ;26 - 3 # <52>
        dw	3920h, 3920h, 3920h, 3920h ;29 - SPC <53>	
        dw	0635h, 0625h, 0000h, 7c00h ;2e - 5 % <54>
        dw	0736h, 075eh, 071eh, 7d00h ;36 - 6 ^ <55>
        dw	0837h, 0826h, 0000h, 7e00h ;3d - 7 & <56>
        dw	0938h, 092ah, 0000h, 7f00h ;3e - 8 * <57>
        dw	332ch, 333ch, 0000h, 3300h ;41 - , < <58>
        dw	0b30h, 0b29h, 0000h, 8100h ;45 - 0 ) <59>
        dw	0a39h, 0a28h, 0000h, 8000h ;46 - 9 ( <60>
        dw	342eh, 343eh, 0000h, 3400h ;49 - . > <61>
        dw	352fh, 353fh, 0000h, 3500h ;4a - / ? <62>
        dw	0e02fh, 0e02fh, 9500h, 0a400h ;4a - (e0)KP/ <63>
        dw	273bh, 273ah, 0000h, 2700h ;4c - ; : <64>
        dw	0c2dh, 0c5fh, 0c1fh, 8200h ;4e - - _ <65>
        dw	2827h, 2822h, 0000h, 2800h ;52 - ’ “ <66>
        dw	1a5bh, 1a7bh, 1a1bh, 1a00h ;54 - [ { <67>
        dw	0d3dh, 0d2bh, 0000h, 8300h ;55 - = + <68>	
        dw	1c0dh, 1c0dh, 1c0ah, 1c00h ;5a - Enter, (E0)KPEnter <69>
        dw	1b5dh, 1b7dh, 1b1dh, 1b00h ;5b - ] } <70>
        dw	2b5ch, 2b7ch, 2b1ch, 2b00h ;5d - \ | <71>
        dw	0e08h, 0e08h, 0e7fh, 0e00h ;66 - BKSP <72>
        dw	4f00h, 4f00h, 7500h, 9f00h ;69 - (E0)END <73>
        dw	4b00h, 4b00h, 7300h, 9b00h ;6b - (E0)LEFT <74>
        dw	4700h, 4700h, 7700h, 9700h ;6c - (E0)HOME <75>
        dw	5200h, 5200h, 9200h, 0a200h ;70 - (E0)INS <76>
        dw	5300h, 5300h, 9300h, 0a300h ;71 - (E0)DEL <77>
        dw	5000h, 5000h, 9100h, 0a000h ;72 - (E0)DOWN <78>
        dw	4300h, 5c00h, 6600h, 7000h ;01 - F9 <79>
        dw	4d00h, 4d00h, 7400h, 9d00h ;74 - (E0)RIGHT <80>
        dw	4800h, 4800h, 8d00h, 9800h ;75 - (E0)UP <81>
        dw	011bh, 011bh, 011bh, 0100h ;76 - ESC <82>
        dw	8500h, 8700h, 8900h, 8b00h ;78 - F11 <83>
        dw	4e2bh, 4e2bh, 9000h, 4e00h ;79 - KP+ <84>
        dw	5100h, 5100h, 7600h, 0a100h ;7a - (E0)PGDN <85>
        dw	4a2dh, 4a2dh, 8e00h, 4a00h ;7b - KP- <86>
        dw	372ah, 372ah, 9600h, 3700h ;7c - KP* --- on VMWare, it does not send 3710h with CTL <87>
        dw	4900h, 4900h, 8400h, 9900h ;7d - (E0)PGUP <88>
        dw	4600h, 4600h, 4600h, 4600h ;7e - SCRL <89>
        dw	4100h, 5a00h, 6400h, 6e00h ;83 - F7 <90>

; ------------------------- POWER ON RESET -----------------------
        org     0fff0h
        
        db      0eah
        dw      coldboot, 0f000h
        db      '15/03/18'
        db      0ffh, 0ffh, 0
end bios