|K'~ LK250.BCK LK250.BCK'BACKUP/INTER [.KIT...]*.* LK250.BCK/SAV SYSTEM spV5.4 _BATMAN::  _$1$DUA7: V5.3 ~  **[PCSA.APPLICATION.KEYBRD.KIT]DECKEYB.ASM;1+,p>.T/ 4OTT-:0123KPWOU56 An7@ 89GHJ ; DECKEYB.ASMN; A disassembled, annotated, and enhanced version of DECKEYB.COM from PCSA V4.?; Reverse engineering by Nick Brown, 1991. NO rights reserved.D; Please Uncle Ken, don't sue me; I just want to use less memory and0; get some extra functionality from my LK250...;; Revision history:;; 07-NOV-1991 V0.41N; Removed Shift-Alt and Ctrl-Shift tables; now same as unshifted (FEWER_MAPS).I; In this case, we read the file into a separate, non-resident data area.J; An added bonus is that we don't keep 126 unnecessary bytes from the .KEY; file header in the TSR.H; Removed extra data areas, not needed for STDxx.KEY files (STDXX_ONLY).;; 24-OCT-1991 V0.405; First public release. Version number is arbitrary.$; Enhancements to Digital's version:F; Close open file handles and deallocate environment before going TSR.,; Fixed location bug (segment address 0EEh).L; Otherwise I have so far not done much to this program, compared to KEYBRD.;!; DECKEYB has two main functions:J; - to load an alternate key map, and allow switching between this and the; default map.K; - to handle compose characters, via the post-processing call from KEYBRD.>; When run with the /D option, DECKEYB does not stay resident.L; When run when another copy is already resident, the tables selected by the2; second run overwrite those loaded by the first.;G; Set the next variable to 1 to assemble the file just like the DigitalK; original version. All other conditional assembly variables should go toK; zero when this is done. As a check, after assembling with this variableG; set to 1, dump both this version and the Digital-supplied version ofE; DECKEYB.COM into separate files using DEBUG; the files should then7; compare identically. The DEBUG commands to use are: ; U100L3; DCS:103L1224 ; U1325L0E0C ; DCS:2131L1;!IFDEF ORIGINAL ;TASM /dORIGINAL/DEC_original EQU 1 ;Assemble just like DigitalELSEDEC_original EQU 0ENDIF ;IFDEF ORIGINAL;8; Select smaller instructions, suppression of NOPs, etc.;%SMALLER EQU 1 AND (NOT DEC_original); ; Fix bugs.;$BUGFIX EQU 1 AND (NOT DEC_original);; Assemble debug stuff.;#DEBUG EQU 1 AND (NOT DEC_original);5; Suppress data areas not needed for STDxx.KEY files.;'STDXX_ONLY EQU 1 AND (NOT DEC_original);; Assemble VAXmate code.O; Omitting this does not save much space in the TSR, but it's less to go wrong.;VAXMATE_CODE EQU DEC_original;=; Save space by making ShiftAlt == Alt and CtrlShift == Ctrl.D; Not tested on VAXmate, so assume it doesn't work on that hardware.;>FEWER_MAPS EQU 1 AND (NOT DEC_ORIGINAL) AND (NOT VAXMATE_CODE);; Debug macro definitions.;%NOLIST include gasp.inc%LIST;!; Keyboard scan code definitions.;%NOLIST include scancode.inc%LIST;L; Assembly options. This program has been tested with Turbo Assembler V2.0.G; If using DEC_original, you may need 2 passes to get the more marginal4; 8-bit relative jumps to work exactly like before.; LOCALSIFE (DEC_ORIGINAL) JUMPSENDIF ;IFE (DEC_ORIGINAL) .MODEL TINYIF (DEC_ORIGINAL);N; Macro to force the LEA instruction to be assembled as such; for some reason,*; Turbo Assembler always generates a MOV.I; This is needed to get the comparison with the original Digital-supplied; version to work.;LEA_AX EQU 06HLEA_DI EQU 3EHxLEA MACRO reg, addr DB 8DH DB LEA_®& DW OFFSET addr ENDMENDIF ;IF (DEC_ORIGINAL) _TEXT SEGMENT WORD PUBLIC 'CODE' ASSUME CS:_TEXT ASSUME DS:_TEXTSegTop: ORG 100H ;this is a .COM fileStart: JMP Start2DataStart EQU $ ;was at 0103;H; This is the resident data area; principally, key and compose mappings.=; The original DECKEYB read the file into this area directly.H; If we use FEWER_MAPS, we read the file to a separate area (because theE; resident area is not big enough) and copy just the maps we need toG; this resident area. The buffer is defined just after the end of the+; TSR-resident code and data area (below).;IFE (FEWER_MAPS)BufferStart EQU $ DB 08H DUP (0) ;was at 0103ENDIF ;IFE (FEWER_MAPS)>FileCheckBytes EQU BufferStart ;wherever BufferStart might beFileFlags DB 0 ;was at 010B;<; Flag set to 1 if using standard map, 0 if using alternate.;AlternateMap EQU 0StandardMap EQU 1%WhichMap DB StandardMap ;was at 010CIFE (FEWER_MAPS);4; Rest of first 128 bytes of file is just rubbish...; DB 76H DUP (0) ;was at 010DENDIF ;IFE (FEWER_MAPS);; Alternate translation tables.;&AltKeys DB 0D2H DUP (0) ;was at 0183AltKeys_S EQU $ - AltKeys&CtrlKeys DB 0D2H DUP (0) ;was at 0255CtrlKeys_S EQU $ - CtrlKeys+UnshiftedKeys DB 0D2H DUP (0) ;was at 0327%UnshiftedKeys_S EQU $ - UnshiftedKeys(KeypadKeys DB 01AH DUP (0) ;was at 03F9KeypadKeys_S EQU $ - KeypadKeys'ShiftKeys DB 0D2H DUP (0) ;was at 0413ShiftKeys_S EQU $ - ShiftKeys*CapsLockKeys DB 0D2H DUP (0) ;was at 04E5#CapsLockKeys_S EQU $ - CapsLockKeys)CtrlAltKeys DB 0D2H DUP (0) ;was at 05B7!CtrlAltKeys_S EQU $ - CtrlAltKeys*ShiftAltKeys DB 0D2H DUP (0) ;was at 0689#ShiftAltKeys_S EQU $ - ShiftAltKeysIF (FEWER_MAPS) ORG ShiftAltKeysENDIF ;IF (FEWER_MAPS)+CtrlShiftKeys DB 0D2H DUP (0) ;was at 075B%CtrlShiftKeys_S EQU $ - CtrlShiftKeysIF (FEWER_MAPS) ORG ShiftAltKeysENDIF ;IF (FEWER_MAPS)(ShiftIsCap DB 035H DUP (0) ;was at 082D*ShiftIsCap_S EQU $ - ShiftIsCap ;not used;@; I think the next byte is just to round out to a even number...; DB 0 ;was at 0862;$; Serial numbers for mapping tables.;N_UnshiftedKeys EQU 0"N_CtrlKeys EQU N_UnshiftedKeys + 1N_AltKeys EQU N_CtrlKeys + 1N_ShiftKeys EQU N_AltKeys + 1 N_KeypadKeys EQU N_ShiftKeys + 1#N_CapsLockKeys EQU N_KeypadKeys + 1$N_CtrlAltKeys EQU N_CapsLockKeys + 1$N_ShiftAltKeys EQU N_CtrlAltKeys + 1&N_CtrlShiftKeys EQU N_ShiftAltKeys + 1$N_ShiftIsCap EQU N_CtrlShiftKeys + 1;; Compose area.; ComposeArea EQU $ ;was at 0863,CommonCompose DB 152H DUP (0) ;accents, etcIFE (STDXX_ONLY)) DB 69CH DUP (0) ;other compose stuff ?ENDIF ;IFE (STDXX_ONLY);,; Limit of area which can be read from file.+; STDxx.KEY files stop after CommonCompose.;IFE (FEWER_MAPS)BufferEnd EQU $ENDIF ;IFE (FEWER_MAPS);L; Limit of data area that is copied between resident and non-resident copies; of this program.;DataEnd EQU $;@; Next chunk of memory apparently not used (maybe on VAXmate ?).;IF (VAXMATE_CODE) DB 0D2H DUP (0) ;was at 1051ENDIF ;IF (VAXMATE_CODE)ComposeSize EQU $ - ComposeArea;.OldVec DD 0 ;old INT 19H vector ;was at 1123OurCodeSeg DW 0 ;was at 1127 DB_Code DB 'CODE' ;was at 1129!ComposePending DB 0 ;was at 112D$ComposeFirst DW 0FFFFH ;was at 112E SavedPostCall DD 0 ;was at 1130;;; Routine called when a character code has been translated.9; AH=scan code AL=translated character BL=shift status.;PostCall: ;was at 1134 PUSH DS PUSH SI PUSH CX PUSH CS POP DS CMP [WhichMap], AlternateMap IF (SMALLER) JNZ @@L_11BAELSE JZ @@L_1143 JMP @@L_11BA NOP @@L_1143:ENDIF ;IF (SMALLER)9 TEST [FileFlags], 20H ;does file support 3-key compose ? JZ @@L_115D ;go if not" CMP AX, Mapped_compose ;compose ? JNZ @@L_115D ;go if not2 MOV [ComposePending], 1 ;remember compose pending5 MOV [ComposeFirst], 0FFFFH ;no compose character yet JMP @@return @@L_115D:+ CMP [ComposePending], 1 ;compose pending ? JNZ @@L_11AB ;go if not# CMP AX, Mapped_rubout ;backspace ? JNZ @@L_1177 ;go if not) MOV [ComposePending], 00 ;forget compose MOV [ComposeFirst], 0FFFFH JMP @@return @@L_1177:: CMP [ComposeFirst], -01 ;any previous compose character ? JNZ @@L_1184 ;go if so* MOV [ComposeFirst], AX ;this is the first JMP @@return @@L_1184:8 MOV AH, BYTE PTR [ComposeFirst] ;get previous char code( CALL MapCompose ;map compose character5 MOV [ComposePending], 00 ;forget compose in progress, MOV [ComposeFirst], 0FFFFH ;can probably go$ CMP AX, 0FFFFH ;did compose work ? JNZ @@L_11F2 ;go if so;+; Invalid compose sequence - ring the bell.; PUSH AX PUSH BX PUSHF MOV AH, 0EH MOV AL, 07H MOV BL, 07H INT 10H POPF POP BX POP AX; JMP @@return;D; Not compose key - maybe it's the first of a two-character compose.; @@L_11AB:! CMP AH, Flag_deadkey ;dead key ? JNZ @@L_11BA ;go if not MOV [ComposePending], 1" MOV [ComposeFirst], AX ;save code JMP @@return;7; Come here if not compose (2 or 3 character sequence).; @@L_11BA: TEST BL, 08H ;Alt set ? JZ @@L_11F2 ;go if not TEST BL, 04H ;Ctrl set ? JZ @@L_11F2 ;go if not CMP AX, Mapped_alt_f2 ;F2 ? JZ @@CtrlAltF2 ;go if so CMP AX, Mapped_alt_f3 ;F3 ? JNZ @@L_11F2 ;go if not6 CALL DoAlternateMap ;select alternate keypad mapping MOV [WhichMap], AlternateMap CMP AL, AL JMP @@return @@CtrlAltF2: MOV AH, 0D6H MOV AL, 1 MOV CX, 00FFH- INT 16H ;reset to default keyboard mapping MOV AH, 0D3H XOR AL, AL INT 16H MOV [WhichMap], StandardMap CMP AL, AL JMP @@return;9; If there was a previous PostCall routine, call it here.; @@L_11F2:# CMP WORD PTR CS:[SavedPostCall], 0 JNZ @@L_1209% CMP WORD PTR CS:[SavedPostCall+2], 0 JNZ @@L_1209 MOV CX, 0001H OR CX, CX JMP @@return @@L_1209: POP CX POP SI POP DS! PUSH WORD PTR CS:[SavedPostCall]# PUSH WORD PTR CS:[SavedPostCall+2] RETF @@return: POP CX POP SI POP DS RETF;E; Convert the current character (AL) and previous character (AH) to a; composed character.;MapCompose: ;was at 121B PUSH SI PUSH CX AND AH, 7FH AND AL, 7FH; CMP AH, ' ' ;must be printable JL BadScene ;go if not CMP AL, ' ' JL BadScene; MOV SI, OFFSET ComposeArea PUSH AX= XCHG AH, AL ;spectacular, but MOV AL, AH is just as good...% XOR AH, AH ;...since we zap AH here SUB AX, ' ' SHL AX, 12 ADD SI, AX ;SI is now offset for first character POP AX; MOV SI, [SI]4 OR SI, SI ;any compose entry for first character ? JZ BadScene ;go if not;I; SI is now the offset (in the whole translation table data structure) ofC; the count of valid compose sequences for this initial character.; ADD SI, OFFSET DataStartIF (FEWER_MAPS);H; Correct the offset to take into account the fact that the compose area+; is further up the data area than before.; The offset is:A; 126 byPtes (first record of file, less FileFlags and WhichMap) +#; (size of omitted CtrlShiftKeys) + ; (size of omitted ShiftAltKeys);1 SUB SI, (126 + CtrlShiftKeys_S + ShiftAltKeys_S)ENDIF ;IF (FEWER_MAPS) MOV CL, [SI] INC SI ;point past count@@loop: OR CL, CL ;any left to try ? JZ BadScene ;go if not% CMP AL, [SI] ;match second character& JZ MatchFound ;go if there is a match DEC CL ;no match - try again- ADD SI, 2 ;skip this match/translation pair JMP @@loop MatchFound:' INC SI ;next character in sequence...! MOV AL, [SI] ;...is translation# XOR AH, AH ;no emulated scan code JMP MapCompReturn BadScene: MOV AX, 0FFFFH ;no match foundMapCompReturn: POP CX POP SI RETD_1264 DD 0 ;was at 1264DoAlternateMap: ;was at 1268 PUSH BX PUSH ES MOV AH, 0D3H MOV AL, [FileFlags] INT 16H;F; Using multiple INT 16H AH=0D6H 0<=CL<=9 calls, set translation table%; pointers to values in this routine.; MOV AX, CS MOV ES, AX MOV WORD PTR [D_1264+2], AX MOV BX, OFFSET D_1264 MOV AL, 01H MOV AH, 0D6H XOR CL, CL, MOV WORD PTR [D_1264], OFFSET UnshiftedKeys INT 16H INC CL' MOV WORD PTR [D_1264], OFFSET CtrlKeys INT 16H INC CL& MOV WORD PTR [D_1264], OFFSET AltKeys INT 16H INC CL( MOV WORD PTR [D_1264], OFFSET ShiftKeys INT 16H INC CL) MOV WORD PTR [D_1264], OFFSET KeypadKeys INT 16H INC CL+ MOV WORD PTR [D_1264], OFFSET CapsLockKeys INT 16H INC CL* MOV WORD PTR [D_1264], OFFSET CtrlAltKeys INT 16H INC CLIF (FEWER_MAPS)& MOV WORD PTR [D_1264], OFFSET AltKeysELSE+ MOV WORD PTR [D_1264], OFFSET ShiftAltKeysENDIF ;IF (FEWER_MAPS) INT 16H INC CL;N; Interestingly, even Digital's original version had CtrlShift == Ctrl here...;6 MOV WORD PTR [D_1264], OFFSET CtrlKeys ;CtrlShiftKeys INT 16H INC CL) MOV WORD PTR [D_1264], OFFSET ShiftIsCap INT 16H; POP ES POP BX RET; ; Interrupt handler for INT 19H.D; DECKEYB hooks this when it becomes resident, so that if run again,%; it sees that it are already there.N; When the interrupt is called, it puts the old vector back (using a DOS call,; which is a VERY bad idea).;Int19H: PUSH DS PUSH AX MOV AX, WORD PTR CS:[OldVec+2] MOV DS, AX MOV DX, WORD PTR CS:[OldVec] MOV AH, 25H MOV AL, 19H INT 21H POP AX POP DS JMP CS:[OldVec];; End of TSR resident code.;ResidentEnd EQU $( DW 0 ;what's this for ?? ;was at 1300;K; If we use less resident space, we must have a buffer large enough to holdJ; the entire .KEY file. We then move parts of this file to corresponding; resident data areas.;IF (FEWER_MAPS)BufferStart EQU $" DB 08H DUP (?) ;file check bytesB_FileFlags DB ?B_WhichMap DB ?1 DB 76H DUP (?) ;rest of first 128 bytes of file;; Alternate translation tables.;B_AltKeys DB AltKeys_S DUP (?) B_CtrlKeys DB CtrlKeys_S DUP (?)*B_UnshiftedKeys DB UnshiftedKeys_S DUP (?)$B_KeypadKeys DB KeypadKeys_S DUP (?)"B_ShiftKeys DB ShiftKeys_S DUP (?)(B_CapsLockKeys DB CapsLockKeys_S DUP (?)&B_CtrlAltKeys DB CtrlAltKeys_S DUP (?)FB_ShiftAltKeys DB ShiftAltKeys_S DUP (?) ;not used in resident versionHB_CtrlShiftKeys DB CtrlShiftKeys_S DUP (?) ;not used in resident version$B_ShiftIsCap DB ShiftIsCap_S DUP (?) DB ? ;end of mapping tablesB_CommonCompose DB 152H DUP (?)IFE (STDXX_ONLY)) DB 69CH DUP (?) ;other compose stuff ?ENDIF ;IFE (STDXX_ONLY)BufferEnd EQU $IF (STDXX_ONLY)>ERRIF ((BufferEnd - BufferStart) NE 2226d) ;check on file sizeENDIF ;IF (STDXX_ONLY)ENDIF ;IF (FEWER_MAPS);SetUpPostCall: ;was at 1302 PUSH AX PUSH BX PUSH ES MOV AH, 0D0H/ MOV AL, 82H ;get address of PostCall routine INT 16H MOV AX, ES! MOV WORD PTR [SavedPostCall], AX# MOV WORD PTR [SavedPostCall+2], BX MOV AX, CS MOV ES, AX5 MOV BX, OFFSET PostCall ;set PostCall to our routine MOV AH, 0D0H MOV AL, 0FEH INT 16H POP ES POP BX POP AX RET;; Program starts here.;Start2: ;was at 1325 MOV AX, CS- MOV SS, AX ;stack just before code segment" MOV SP, OFFSET StackTop ;was 2132, MOV AX, OFFSET Banner ;announcement message MOV [Message], AX CALL ShowMessage CALL CheckDOS CMP AL, 0FFH JZ L_1391 ;wrong DOS, so exit IFE (SMALLER) CALL R_1394 JB L_1391ENDIF ;IFE (SMALLER) CALL CheckKEYBRD JB L_1391 ;no KEYBRD, so exit;2 MOV AX, 0081H ;offset of command line parameters MOV [CLIptr], AX MOV BX, AX XOR AX, AX) DEC BX ;address of command line length# MOV AL, [BX] ;command line length MOV [CLIsize], AX CALL R_1590 CMP AL, 0FFH JZ L_1391 CALL R_15FD CMP AL, 0FFH JZ L_1391 CALL ReadFile JB L_1391 CMP AL, 0EEH JZ L_1384 CMP AL, 0FFH JZ L_1391IF (VAXMATE_CODE) CMP [VAXmate], 1 JNZ @@skip CALL R_1789@@skip:ENDIF ;IF (VAXMATE_CODE) CALL ShowSuccess CALL SetUpPostCall CALL BecomeTSR ;Won't returnL_1384:IF (VAXMATE_CODE) CMP [VAXmate], 1 JNZ @@skip CALL R_1789@@skip:ENDIF ;IF (VAXMATE_CODE) CALL ShowSuccessL_1391: CALL Closedown ;Won't return IFE (SMALLER);/; Silly routine - suppose it did something once;R_1394: PUSH AX PUSH DX PUSH SI PUSH DS CLC POP DS POP SI POP DX POP AX RETENDIF ;IFE (SMALLER)CheckKEYBRD: ;was at 139E PUSH AX PUSH BX PUSH DX' MOV AL, 0FFH ;see if KEYBRD is loaded, MOV AH, 0D4H ;INT 16H FUN 0D4H SUBFUN 0FFH INT 16H( CMP AL, 0F0H ;... should return < 0F0H JB L_13B5;m MOV DX, OFFSET No_KEYBRDe MOV AH, 09H INT 21H STC JMP L_13B6eL_13B5:i CLCL_13B6:  POP DX9 POP BXg POP AXv RET;a-; Routine to open and read keyboard map file.l; ReadFile: ;was at 13BA PUSH CS POP DSr MOV DX, OFFSET D_1AC0 MOV AH, 3DH MOV AL, 00H INT 21H MOV [FileHandle], AX- JB L_13CD JMP FileIsOpenw IFE (SMALLER)d NOPENDIF ;IFE (SMALLER);e; Come here if open failed.e;oL_13CD:t MOV BX, OFFSET D_1AC0 CMP BYTE PTR [BX], '\'e JNZ L_13D8s JMP L_1472oL_13D8:Y CMP BYTE PTR [BX], '/'S JNZ L_13E0 JMP L_1472e; B; Filename did not begin with backslash; try providing it for user;L_13E0:  MOV DX, OFFSET D_1ABF MOV AH, 3DH MOV AL, 00H INT 21H MOV [FileHandle], AX JB L_13F1 JMP FileIsOpen IFE (SMALLER)n NOPENDIF ;IFE (SMALLER)L_13F1:n CALL LookForFiles2 JNB L_13F9 ;Carry clear means we found the file JMP ReturnFailmL_13F9:K MOV AH, 3DH MOV AL, 00H MOV DX, OFFSET D_1AC0 INT 21H ;open the filep JB OpenFailed ;go on error MOV [FileHandle], AXl FileIsOpen:oIF (VAXMATE_CODE)c CMP [VAXmate], 1p% JNZ L_1419 ;go if not on a VAXmaten CLD MOV DI, OFFSET ComposeAreao XOR AX, AXn5 MOV CX, (ComposeSize/2) ;zero this block (was 0460H)h REPZ STOSWcL_1419:eENDIF ;IF (VAXMATE_CODE) MOV DX, OFFSET BufferStart" MOV CX, (BufferEnd - BufferStart) MOV BX, [FileHandle]  MOV AH, 3FH ;read from file INT 21H JB L_1492 ;go if errorr CALL CloseFilet MOV SI, OFFSET FileCheckBytes CALL CheckFile  JNZ L_14A4 ;go if bad fileIF (FEWER_MAPS)o;t<; Move required data from file buffer to resident data area.;i: mov ax, WORD PTR [B_FileFlags] ;includes WhichMap as well mov WORD PTR [FileFlags], axc;a mov ax, dse mov es, ax cld;1#; Move AltKeys through CtrlAltKeys.L; mov si, OFFSET B_AltKeys/ mov di, OFFSET AltKeysE3 mov cx, (OFFSET B_ShiftAltKeys - OFFSET B_AltKeys)l rep movsb;I$; Move ShiftIsCap and CommonCompose.;r mov si, OFFSET B_ShiftIsCap mov di, OFFSET ShiftIsCap* mov cx, (BufferEnd - OFFSET B_ShiftIsCap) rep movsb;NENDIF ;IF (FEWER_MAPS) MOV AH, 0D3Hg MOV AL, [FileFlags] INT 16H- CALL SetUpTables ;set up translation tableso CMP AL, 0EEHD JZ ReturnOK;F; The next call would be redundant if SetUpTables were always to work.G; Apparently, it does not want to set up the tables if it needs to hookwB; the interrupt vector, for reasons I don't currently understand.;S CALL R_14B7 MOV AL, 00H JMP ReturnOKo IFE (SMALLER)t NOPENDIF ;IFE (SMALLER);h OpenFailed:+ MOV BX, [FilenameLen] ;length of filenameN DEC BXX: MOV BYTE PTR [BX+BadPath], '$' ;terminate filename with $ IFE (SMALLER) NOPENDIF ;IFE (SMALLER);o MOV AH, 09H MOV DX, OFFSET FileNotFound INT 21H;L MOV AH, 09H MOV DX, OFFSET BadPathr INT 21H MOV DL, 0DH MOV AH, 02H INT 21H MOV DL, 0AH MOV AH, 02H INT 21H JMP ReturnFail IFE (SMALLER) NOPENDIF ;IFE (SMALLER)L_1472:l MOV AX, OFFSET FileNotFound MOV [Message], AX CALL ShowMessageE MOV DX, OFFSET D_1AC0 CALL ShowZTString MOV DL, 0DH MOV AH, 02H INT 21H MOV DL, 0AH MOV AH, 02H INT 21H MOV AL, 0FFHo JMP ReturnFailm IFE (SMALLER)a NOPENDIF ;IFE (SMALLER)L_1492:  CALL CloseFilehIF (DEC_ORIGINAL)p xLEA AX, [FileReadError]ELSE LEA AX, [FileReadError]ENDIF ;IF (DEC_ORIGINAL) MOV [Message], AX CALL ShowMessaged MOV AL, 0FFHI JMP ReturnOKI IFE (SMALLER)M NOPENDIF ;IFE (SMALLER)L_14A4:T CALL CloseFileTIF (DEC_ORIGINAL)0 xLEA AX, [NotMapFile]ELSE LEA AX, [NotMapFile]aENDIF ;IF (DEC_ORIGINAL) MOV [Message], AX CALL ShowMessage MOV AL, 0FFHy ReturnOK:e CLC RET ReturnFail:l STC RETR_14B7:i PUSH ES PUSH DI MOV AX, CS MOV ES, AXS;e MOV DI, OFFSET UnshiftedKeyse MOV CL, N_UnshiftedKeys CALL R_1510;  MOV DI, OFFSET CtrlKeys MOV CL, N_CtrlKeyst CALL R_1510;. MOV DI, OFFSET AltKeysu MOV CL, N_AltKeys CALL R_1510;d MOV DI, OFFSET ShiftKeyso MOV CL, N_ShiftKeys CALL R_1510;  MOV DI, OFFSET KeypadKeys MOV CL, N_KeypadKeys( CALL R_1510;i MOV DI, OFFSET CapsLockKeys MOV CL, N_CapsLockKeysh CALL R_1510;  MOV DI, OFFSET CtrlAltKeys  MOV CL, N_CtrlAltKeys CALL R_1510; IF (FEWER_MAPS) MOV DI, OFFSET AltKeystELSE MOV DI, OFFSET ShiftAltKeysENDIF ;IF (FEWER_MAPS) MOV CL, N_ShiftAltKeyst CALL R_1510;eIF (FEWER_MAPS)  MOV DI, OFFSET CtrlKeysELSE MOV DI, OFFSET CtrlShiftKeysEENDIF ;IF (FEWER_MAPS) MOV CL, N_CtrlShiftKeys CALL R_1510;H MOV DI, OFFSET ShiftIsCap MOV CL, N_ShiftIsCap CALL R_1510;H POP DI  POP ES2 RETR_1510:U PUSH DS PUSH SI PUSH ES PUSH DI MOV [D_1A9E], DI2 MOV [D_1AA0], ES  MOV BX, OFFSET D_1A9E MOV AX, CSD MOV ES, AX) MOV AL, 01H MOV AH, 0D6HE INT 16H POP DI POP ESy POP SI  POP DS; RET;0; Check first 4 bytes of keymap file are 'KE$Y'.;DCheckFile: ;was at 152Ea MOV AX, CSE MOV ES, AXcIF (DEC_ORIGINAL)  xLEA DI, [KeySignature]ELSE LEA DI, [KeySignature]lENDIF ;IF (DEC_ORIGINAL) CLD MOV CX, 0004H REPZ CMPSBt RETR_153D:i MOV SI, [FilenameOffset]S MOV AX, CSf MOV ES, AXIIF (DEC_ORIGINAL) xLEA DI, [D_2020]ELSE LEA DI, [D_2020]ENDIF ;IF (DEC_ORIGINAL) CLD MOV CX, 0006H REPZ CMPSBR JZ L_1560 MOV AX, [FilenameOffset] MOV SI, AXDIF (DEC_ORIGINAL)a xLEA DI, [D_2026]ELSE LEA DI, [D_2026] ENDIF ;IF (DEC_ORIGINAL) CLD MOV CX, 000AH REPZ CMPSBvL_1560:. RETCloseFile: ;was at 1561 MOV BX, [FileHandle]r MOV AH, 3EH INT 21H RET;y; Check DOS version.;NCheckDOS: ;was at 156Ay MOV AH, 30H INT 21H& CMP BH, 16H ;OEM code for Digital ? JNZ L_1579eIF (VAXMATE_CODE)s MOV [VAXmate], 0001HELSE# MOV [Message], OFFSET NoVAXmateNowf CALL ShowMessageAENDIF ;IF (VAXMATE_CODE)L_1579:f CMP AL, 03H% JB L_1584 ;go if less than 3 (bad)$ JA L_158F ;go if 4 or higher (OK) CMP AH, 01H ;3.01 or higher ? JNB L_158F ;go if so (OK)L_1584:  MOV AX, OFFSET BadDOS MOV [Message], AX CALL ShowMessage  MOV AL, 0FFH ;failureL_158F:f RETR_1590:e CLD MOV SI, 0081HL_1594:i LODSB CMP AL, 0DH JZ L_15A3 CMP AL, 20H JZ L_1594 CMP AL, 09H JZ L_1594 JMP L_15C1L_15A3: IF (VAXMATE_CODE)c CMP [VAXmate], 1e JNZ L_15B6s" MOV [Message], OFFSET Help_with_S CALL ShowMessage MOV AL, 0FFHu RETL_15B6:pENDIF ;IF (VAXMATE_CODE) MOV [Message], OFFSET Help_no_S CALL ShowMessage MOV AL, 0FFHL_15C1:  RETR_15C2: MOV DI, OFFSET D_1AFC MOV CX, 0040H MOV AL, 00H STD REPZ SCASBw CLD INC DIC MOV AL, [DI]a CMP AL, 2EH JZ L_15FB DEC DIs MOV AL, [DI]s CMP AL, 2EH JZ L_15FB DEC DIs MOV AL, [DI]H CMP AL, 2EH JZ L_15FB DEC DI MOV AL, [DI] CMP AL, 2EH JZ L_15FB ADD DI, 4 MOV SI, OFFSET DOT_KEY MOV CX, 0004H ADD [FilenameLen], 4e CLD REPZ MOVSBL_15FB:t POP SIa RETR_15FD:  MOV DI, [CLIptr]  MOV CX, [CLIsize]L_1605:[ MOV AL, 20H REPZ SCASB DEC DIE MOV AL, [DI]B CMP AL, 09H JNZ L_1613P INC DI JMP L_16051L_1613:I MOV SI, DIR MOV AL, [SI]l CMP AL, 0DH JNZ L_1620p MOV AL, 0FFHs JMP L_1665_ IFE (SMALLER) NOPENDIF ;IFE (SMALLER)L_1620:Z MOV DI, OFFSET D_1AC0 CALL R_172C CALL R_1714 CMP AL, 0FFHp JZ L_163E CALL GetArgs, CMP AL, 0FFHp JZ L_163E CMP AL, 0EEHr JZ L_1663 CALL R_15C2 JMP L_1665g IFE (SMALLER)d NOPENDIF ;IFE (SMALLER)L_163E:P MOV AX, OFFSET InvParmp MOV [Message], AX CALL ShowMessageVIF (VAXMATE_CODE)  CMP [VAXmate], 1M JNZ L_165Ar MOV AX, OFFSET Help_with_S MOV [Message], AX CALL ShowMessagen JMP L_1663m IFE (SMALLER) NOPENDIF ;IFE (SMALLER)ENDIF ;IF (VAXMATE_CODE)L_165A:t MOV AX, OFFSET Help_no_S MOV [Message], AX CALL ShowMessage L_1663:c MOV AL, 0FFH L_1665:e MOV [CLIptr], SIa RET;["; Look for command line arguments.;pGetArgs: ;was at 166Ar MOV CX, 0040H MOV DI, OFFSET D_1AFB MOV AL, '/' STD' REPNZ SCASB ;look backwards for slashn CLD MOV AL, [DI+01]" CMP CX, 0 ;did we find a slash ? JZ L_16AD ;go if not INC DI ;point to slash CMP DI, OFFSET D_1AC0 JNZ L_168CP MOV AL, [DI+02] CMP AL, 00H JZ L_168FL_168C:b JMP L_1711rL_168F:w# INC DI ;point to option character MOV AL, [DI]e CMP AL, [Letter_D]J JZ Slash_D;IF (VAXMATE_CODE)p CMP AL, [Letter_S][ENDIF ;IF (VAXMATE_CODE) JNZ L_1711rIF (VAXMATE_CODE)f;tB; Here if /S option was specified. This is only valid on VAXmate.;  CMP [VAXmate], 1_ JNZ L_1713n CALL ShowFilename MOV AL, 0EEHJ JMP L_1713g IFE (SMALLER), NOPENDIF ;IFE (SMALLER)ENDIF ;IF (VAXMATE_CODE)L_16AD:l CALL FindNameExtL CALL R_153D JNZ L_1713o;e@; Here if /D option was specified. This resets the default map.;eSlash_D: MOV AH, 0D6H@ MOV AL, 01H MOV CX, 00FFH, INT 16H ;reset to default keyboard mapping;; MOV AH, 0D3Ht XOR AL, AL ;reset keyboard INT 16H;, PUSH DS PUSH ES! MOV AH, 35H ;get INT 19H vectorA MOV AL, 19H INT 21H MOV DI, OFFSET DB_Code MOV AX, CSi MOV DS, AXe MOV SI, OFFSET DB_CodeP MOV CX, 0004H REPZ CMPSB 2 JZ L_16DF ;go if INT 19H hooks this code segment POP ESJ* PUSH ES ;else retrieve this code segmentL_16DF:r POP DS2IF (VAXMATE_CODE)S CMP [VAXmate], 1 $ JNZ L_16F2 ;go if not on a VAXmate CLD MOV DI, OFFSET ComposeArea XOR AX, AX5 MOV CX, (ComposeSize/2) ;zero this block (was 0460H)h REPZ STOSWrL_16F2:)ENDIF ;IF (VAXMATE_CODE) MOV ES:[WhichMap], StandardMap POP ESoIF (VAXMATE_CODE)P CMP [VAXmate], 1A JNZ L_1703A CALL R_18A7L_1703:,ENDIF ;IF (VAXMATE_CODE) MOV AX, OFFSET DefaultLoaded, MOV [Message], AX CALL ShowMessage MOV AL, 0EEHP JMP L_1713 IFE (SMALLER)l NOPENDIF ;IFE (SMALLER)L_1711: MOV AL, 0FFH.L_1713:  RETR_1714:B MOV DI, SIL MOV CX, 0014H CLDL_171A: MOV AL, 20H REPZ SCASB MOV AL, [DI-01] CMP AL, 09H JZ L_171A CMP AL, 0DH JZ L_172B MOV AL, 0FFHJL_172B:e RETR_172C: MOV CX, [FilenameLen]L_1730:o LODSB CMP AL, 0DH JZ L_1754 CMP AL, 20H JZ L_1751 CMP AL, 09H JNZ L_1743h MOV BYTE PTR [SI], 20H JMP L_1751F IFE (SMALLER) NOPENDIF ;IFE (SMALLER)L_1743:s CMP AL, 41H JL L_174D CMP AL, 7AH JG L_174D AND AL, 5FHL_174D:e INC CXa STOSB JMP L_1730 L_1751:: CALL R_1714L_1754:c DEC SIl MOV [FilenameLen], CX RETShowSuccess: ;was at 175Ai MOV DX, OFFSET D_1AC0 CALL ShowZTString MOV AX, OFFSET Successl MOV [Message], AX CALL ShowMessageI RETShowMessage: ;was at 176AC MOV DX, [Message] MOV AH, 09H INT 21H RET; 5; Routine to become a TSR; we won't return from here.h;cBecomeTSR: ;was at 1773uIFE (DEC_ORIGINAL);h!; Close file handles 0 through 4.A;S mov bx, 4 loop_close:a mov ah, 3Eh( int 21h ;No check to see if it went OK dec bxt jge loop_close;V; Deallocate our environment.;R mov ah, 62h int 21h ;get our PSP to BX mov es, bxS) mov es, es:[002Ch] ;ES = our environmenta& mov ah, 49h ;deallocate memory at ES int 21hENDIF ;IFE (DEC_ORIGINAL)l;tH; Calculate minimum size for TSR. Note that we do not allow extra spaceH; for the PSP, as we are a .COM file and begin at 100H (after the PSP).;s;;IF (SMALLER)&;; MOV DX, ((ResidentEnd + 0FH) SHR 4);;ELSE MOV DX, OFFSET ResidentEnd ADD DX, 0FH MOV CL, 04H SHR DX, CL4;;ENDIF ;IF (SMALLER), MOV AX, 3100H INT 21HClosedown: ;was at 1782i MOV AH, 4CH MOV AL, 00 INT 21H IFE (SMALLER)F RETENDIF ;IFE (SMALLER)IF (VAXMATE_CODE) R_1789: ;was at 1789 PUSH DS PUSH SI PUSH ES PUSH DI;] CALL FindNameExt;T<; Get FAR address of system's copy of keyboard filename from$; DOS country-specific information.;  MOV DX, OFFSET FilenameAddH MOV AH, 38H MOV AL, 0FFH1 MOV BX, 8000H INT 21H;T! MOV AX, WORD PTR [FilenameAdd+2] PUSH AX POP ES6 MOV DI, WORD PTR [FilenameAdd]R SUB DI, +1AH  PUSH CS POP DS MOV SI, [FilenameOffset] MOV CX, 000CH REPZ MOVSBe;t POP DIe POP ES' POP SIl POP DSh RETENDIF ;IF (VAXMATE_CODE);VF; Scan backwards in name of open file for start of NAME.EXT component.;VFindNameExt: ;was at 17B8i MOV DI, OFFSET CRLF STD SUB DI, 3 MOV AL, 00H$ REPZ SCASB ;scan backwards while 0 XCHG SI, DIL_17C5:  LODSB ;back one character CMP AL, '\'% JZ L_17CE ;break loop for backslash CMP AL, ':'" JNZ L_17C5 ;break loop for colon*L_17CE: ;come here when delimiter found INC SIi INC SI ;point past delimiter MOV [FilenameOffset], SI  CLD RETIF (VAXMATE_CODE)V;,>; Routine to get currently loaded file; only valid on VAXmate.;AGetCurrentFile: ;was at 17D6 PUSH DS PUSH SI PUSH ES PUSH DI;iM; Get FAR address of keyboard filename from DOS country-specific information.s;s MOV DX, OFFSET FilenameAddu MOV AH, 38H MOV AL, 0FFH  MOV BX, 8000H INT 21H;e PUSH CS POP ESs MOV DI, OFFSET CurrentFiles MOV SI, WORD PTR [FilenameAdd]_ SUB SI, +1AHt! MOV AX, WORD PTR [FilenameAdd+2]c PUSH AX POP DSl MOV CX, 000CH REPZ MOVSB  POP DIP POP ESt POP SI  POP DS  RETENDIF ;IF (VAXMATE_CODE);o; Set up translation tables.;eSetUpTables: ;was at 1801t PUSH DS PUSH SI PUSH ES PUSH DIIF (VAXMATE_CODE) CMP [VAXmate], 1p JNZ @@skip( CALL R_1789@@skip:tENDIF ;IF (VAXMATE_CODE) MOV AH, 35H! MOV AL, 19H ;get INT 19H vectorr INT 21H;PF; See if the INT 19H vector is hooked by another copy of this program.;n MOV DI, OFFSET DB_CodeS MOV AX, CSP MOV DS, AXd MOV SI, OFFSET DB_Codeh MOV CX, 0004f REPZ CMPSB(! JNZ L_1893 ;need to hook vectore;H; This program is already resident; move our data area to resident copy.I; Although the code which follows looks like to a move to itself, it willFJ; actually install a new language file on top of a previously loaded one,E; since the source segment is in this program and the destination isl8; in the resident version (which may be already there).J; This allows the user to load STDFR, then STDNO, say. This will normally; be used for debugging.t;l PUSH ES MOV DI, OFFSET DataStart2 PUSH DI MOV SI, OFFSET DataStartM MOV CX, (DataEnd - DataStart)/2 REPZ MOVSWt POP DIV POP ES;M ADD DI, (AltKeys - DataStart) MOV CL, N_AltKeys CALL R_1510 ADD DI, Alt$f\#~ LK250.BCKp>:*[PCSA.APPLICATION.KEYBRD.KIT]DECKEYB.ASM;1OT(X.>Keys_S; MOV CL, N_CtrlKeyst CALL R_1510 ADD DI, CtrlKeys_S ;a MOV CL, N_UnshiftedKeys CALL R_1510 ADD DI, UnshiftedKeys_S;V MOV CL, N_KeypadKeyso CALL R_1510 ADD DI, KeypadKeys_S;L MOV CL, N_ShiftKeys CALL R_1510 ADD DI, ShiftKeys_S;; MOV CL, N_CapsLockKeys CALL R_1510 ADD DI, CapsLockKeys_SN;  MOV CL, N_CtrlAltKeys CALL R_1510IF (FEWER_MAPS)Y MOV DI, OFFSET AltKeys,ELSE ADD DI, CtrlAltKeys_SENDIF ;IF (FEWER_MAPS);, MOV CL, N_ShiftAltKeysA CALL R_1510IF (FEWER_MAPS)m MOV DI, OFFSET CtrlKeysELSE ADD DI, ShiftAltKeys_SMENDIF ;IF (FEWER_MAPS);5 MOV CL, N_CtrlShiftKeys CALL R_1510IF (FEWER_MAPS) MOV DI, OFFSET ShiftIsCapELSE ADD DI, CtrlShiftKeys_SENDIF ;IF (FEWER_MAPS);J MOV CL, N_ShiftIsCap_ CALL R_1510;m MOV AL, 0EEH@ JMP L_1896 IFE (SMALLER) NOPENDIF ;IFE (SMALLER)L_1893:u CALL HookVector IF (BUGFIX)L;eB; This routine returns AL=0EEH if the program is already resident.D; Unfortunately, it does not set AL to a value other than 0EEH here.L; So it exits with AL=low byte of segment address of this program (left overG; from installation check, above). One time in 256, this will be 0EEH,?; and this program will not go TSR. The fix is very simple...;P XOR AL, ALA;ENDIF ;IF (BUGFIX)L_1896:h POP DI: POP ESa POP SIP POP DSP RETIF (VAXMATE_CODE)F;;A; Routine to output currently loaded file; only valid on VAXmate.F;ShowFilename: ;was at 189B  CALL GetCurrentFile PUSH CS POP DSV MOV DX, OFFSET CurrentFileIs, CALL ShowZTString RETENDIF ;IF (VAXMATE_CODE)IF (VAXMATE_CODE)P;B:; VAXmate only; move keyboard file name to known location.;aR_18A7: PUSH DS PUSH SI PUSH ES PUSH DI MOV SI, OFFSET STDUS_KEY_ MOV [FilenameOffset], SI,;HM; Get FAR address of keyboard filename from DOS country-specific information.R; MOV DX, OFFSET FilenameAdd MOV AH, 38H MOV AL, 0FFHd MOV BX, 8000H INT 21H;F! MOV AX, WORD PTR [FilenameAdd+2]e PUSH AX POP ESP MOV DI, WORD PTR [FilenameAdd] SUB DI, +1AHZ# PUSH CS ;!! shouldn't be needed ??e POP DSb MOV SI, [FilenameOffset]o MOV CX, 000CH REPZ MOVSB0 POP DID POP ES  POP SIM POP DSH RETENDIF ;IF (VAXMATE_CODE);a@; Output a zero-terminated string at DS:DX with CR and LF after.;IShowZTString: ;was at 18DA PUSH AX PUSH BX MOV BX, DX L_18DE:e CMP BYTE PTR [BX], 00He JZ L_18E6 INC BX_ JMP L_18DEAL_18E6:M MOV BYTE PTR [BX], 24HE MOV AH, 09H INT 21H MOV BYTE PTR [BX], 00Hl MOV DL, 0DH MOV AH, 02H INT 21H MOV DL, 0AH MOV AH, 02H INT 21H POP BXA POP AXp RET;1#; Set interrupt vector for INT 19H.M;DHookVector: ;was at 18FF PUSH DS PUSH ES PUSH AX PUSH DX PUSH BX CLI MOV AH, 35H MOV AL, 19H INT 21H/ MOV WORD PTR CS:[OldVec], BX ;save old vectorr MOV WORD PTR CS:[OldVec+2], ESt MOV AX, CS, MOV DS, AX 0 MOV CS:[OurCodeSeg], AX ;save our code segment MOV DX, OFFSET Int19H MOV AH, 25H ;set new vectori MOV AL, 19H INT 21H STI POP BX1 POP DX  POP AXl POP ES( POP DSS RETLookForFile: ;was at 192De PUSH DS PUSH ES CLD MOV DI, [FilenameLen] MOV BYTE PTR [DI+D_1AC0], 00H IFE (SMALLER)v NOPENDIF ;IFE (SMALLER) ADD [FilenameLen], 1, MOV CX, [FilenameLen] MOV DI, OFFSET BadPaths MOV SI, OFFSET D_1AC0 MOV AL, [SI]v CMP AL, '\' JNZ L_1955c INC SIT SUB [FilenameLen], 1TL_1955:s REPZ MOVSBb;; Ask DOS for our PSP.;n MOV AH, 62H INT 21H ;get our PSP to BXm MOV ES, BX / MOV AX, ES:[002CH] ;get our environment to ESs MOV ES, AXv CALL GetPathI% JCXZ L_19BB ;go if PATH= not foundA MOV AX, [D_1B80]T OR AX, AX JZ L_19BB MOV AL, ES:[DI] OR AL, AL JZ L_19AAL_1976:e CALL R_1A1F JCXZ L_19E1 CALL R_1A79 CMP AX, 0040H JG L_19E1 CALL FindFile CMP [FileError], 0 JZ L_19A6 ;go if file found OK CMP [FileError], 2t JZ L_19C5 CMP [FileError], 3I JZ L_19C5 CMP [D_1B7D], 0 JZ L_19EB= CMP AL, 12H ;ghastly code - this is left over from FindFileI JZ L_1976 ;go if no more files; ; Come here if file was found OK;lL_19A6:D CLC JMP L_19EBT IFE (SMALLER)$ NOPENDIF ;IFE (SMALLER)L_19AA:A' STC ;relies on DOS preserving this ! MOV AH, 09H MOV DX, OFFSET CantFindFile INT 21H MOV AH, 09H MOV DX, OFFSET D_1AC0 INT 21H JMP L_19EB,L_19BB:V' STC ;relies on DOS preserving this !, MOV AH, 09H MOV DX, OFFSET NoPath INT 21H JMP L_19EBIL_19C5:M' STC ;relies on DOS preserving this !o MOV BX, [FilenameLen] DEC BX  MOV BYTE PTR [BX+BadPath], 24HA IFE (SMALLER)t NOPENDIF ;IFE (SMALLER) MOV AH, 09H MOV DX, OFFSET NoSuchFile INT 21H MOV AH, 09H MOV DX, OFFSET BadPath  INT 21H JMP L_19EB L_19E1:L' STC ;relies on DOS preserving this !( MOV AH, 09H MOV DX, OFFSET PathTooLong INT 21H IFE (SMALLER)o JMP L_19EB ENDIF ;IFE (SMALLER)L_19EB:  POP ES  POP DSg RET;,; Look for path in environment.A;RGetPath: ;was at 19EE XOR DI, DI4 XOR AX, AXs MOV CX, 8000HL_19F5:L MOV AL, 'P' REPNZ SCASB JCXZ L_1A1E MOV [D_1B80], CXI CMP CX, 7FFFH JZ L_1A0F MOV AL, ES:[DI-02] OR AL, AL JZ L_1A0F JMP L_19F5KL_1A0F: PUSH CX MOV SI, OFFSET PathEquals SUB DI, 1 MOV CX, 0005H REPZ CMPSBM POP CXS JNZ L_19F5,L_1A1E:n RETR_1A1F:V XOR SI, SIt MOV CX, 0040HL_1A24: MOV AL, ES:[DI] OR AL, AL JZ L_1A39 CMP AL, 3BH JZ L_1A39 MOV [SI+D_1AC0], AL INC SI, INC DIs LOOP L_1A24 JMP L_1A4B,L_1A39:h MOV [D_1B7D], ALN CMP BYTE PTR [SI+D_1ABF], '\' JZ L_1A4A MOV BYTE PTR [SI+D_1AC0], '\' IFE (SMALLER)0 NOPENDIF ;IFE (SMALLER) INC SIVL_1A4A:p INC DIhL_1A4B:_ RETFindFile: ;was at 1A4C PUSH DI;, MOV DX, OFFSET D_1B52 MOV AH, 1AH% INT 21H ;set disk transfer address MOV DX, OFFSET D_1AC0 MOV CX, 0027H MOV AH, 4EH INT 21H ;find filet JNB L_1A6F ;go if no error( MOV [FileError], AX;FH; The code that follows is worse than useless; it just confirms that theI; error code we got back was one of the known ones. This code will stopJ; working if DOS adds error codes in a later version, as codes other than6; the ones it knows about are converted to success...;D CMP AL, 02H JZ L_1A77 CMP AL, 03H JZ L_1A77 CMP AL, 12H JZ L_1A77L_1A6F:H MOV [FileError], 0000HH IFE (SMALLER)E JMP L_1A77ENDIF ;IFE (SMALLER)L_1A77:s POP DI  RETR_1A79:e PUSH DI MOV BX, SI: MOV SI, OFFSET BadPath, MOV AX, [FilenameLen] ADD AX, BXL CMP AX, 0040H JG L_1A9D MOV CX, [FilenameLen] PUSH AX PUSH ES MOV AX, DS) MOV ES, AXV MOV DI, OFFSET D_1AC0 ADD DI, BX5 REPZ MOVSB, POP ESe POP AX POP DICL_1A9D:E RETD_1A9E DW 0 ;was at 1A9EID_1AA0 DW 0 ;was at 1AA0_;"; Address of message to be output.>; For some reason this is not passed to message routine in DX.;Message DW 0 ;was at 1AA2D_1AA4 DW 0 ;was at 1AA4 ;??LCLIsize DW 0 ;was at 1AA6D_1AA8 DW 0 ;was at 1AA8 ;??0&STDUS_KEY DB 'STDUS.KEY' ;was at 1AAAD_1AB3 DW 0 ;was at 1AB3 ;??iD_1AB5 DW 0 ;was at 1AB5 ;??H!DOT_KEY DB '.KEY' ;was at 1AB7CLIptr DW 0 ;was at 1ABByD_1ABD DW 0 ;was at 1ABD ;??BD_1ABF DB '\' ;was at 1ABF$D_1AC0 DB 3BH DUP (0) ;was at 1AC0D_1AFB DB 0 ;was at 1AFBVD_1AFC DW 0 ;was at 1AFCo DW 0$CRLF DB 0DH, 0AH, '$' ;was at 1B00IF (VAXMATE_CODE)0FilenameAdd DD 0 ;was at 1B03 ENDIF ;IF (VAXMATE_CODE)"FilenameOffset DW 0 ;was at 1B07D_1B09 DW 0 ;was at 1B09 D_1B0B DW 0 ;was at 1B0BM$PathEquals DB 'PATH=' ;was at 1B0D%BadPath DB 40H DUP (0) ;was at 1B12i$D_1B52 DB 2BH DUP (0) ;was at 1B52D_1B7D DB 1 ;was at 1B7DB DW 0D_1B80 DW 0 ;was at 1B80FilenameLen DW 0 ;was at 1B825FileError DW 0 ;was at 1B84FIF (VAXMATE_CODE)PVAXmate DW 0 ;was at 1B86'CurrentFileIs DB 0DH, 0AH ;was at 1B88h DB 'DECKEYB FILE = '(CurrentFile DB 0CH DUP (0) ;was at 1B99 DB 0DH, 0AH, '$'ENDIF ;IF (VAXMATE_CODE)Letter_S DB 'S' ;was at 1BA8Letter_D DB 'D' ;was at 1BA9!BadDOS DB 0DH, 0AH ;was at 1BAAA& DB 'Incorrect Dos Version', 0DH, 0AH* DB 'This program requires MS-DOS V3.10/'+ DB 'PC-DOS V3.10 or later', 0DH, 0AH, '$' !Banner DB 0DH, 0AH ;was at 1BFEP1 DB 'Keyboard Map Loader Version 2.20', 0DH, 0AHP) DB '(C)Copyright 1985-1989 by Digital 'O DB 'Equipment Corporation'IFE (DEC_original) DB 0DH, 0AHP9 DB 'Modified version 0.41 by Nick Brown, November 1991'LENDIF ;IFE (DEC_original) DB 0DH, 0AH, 0DH, 0AH, '$'IF (VAXMATE_CODE)V%Help_with_S DB 0DH, 0AH ;was at 1C5E( DB 'DECKEYB uses the following format' DB 0DH, 0AH, 0DH, 0AHP# DB 'DECKEYB [filename] [/D] [/S]'s DB 0DH, 0AH, 0DH, 0AHA+ DB '[/D] loads the default keyboard map'Z DB 0DH, 0AHE: DB '[/S] displays the name of the current keyboard map' DB 0DH, 0AH, 0DH, 0AHP DB 'Example:'_ DB 0DH, 0AH0 DB ' DECKEYB STDUK.KEY'J DB 0DH, 0AHF DB ' DECKEYB /s' DB 0DH, 0AH, '$'ELSE;NoVAXmateNow DB 'This version of DECKEYB is not supported 's DB ' on a VAXmate.'E DB 0DH, 0AH, '$'ENDIF ;IF (VAXMATE_CODE)#Help_no_S DB 0DH, 0AH ;was at 1D3AX( DB 'DECKEYB uses the following format' DB 0DH, 0AH, 0DH, 0AHI DB 'DECKEYB [filename] [/D]' DB 0DH, 0AH, 0DH, 0AHV+ DB '[/D] loads the default keyboard map'C DB 0DH, 0AH  DB 'Example:'A DB 0DH, 0AH5 DB ' DECKEYB STDUK.KEY'[ DB 0DH, 0AH, '$'/FileNotFound DB 'File not found $' ;was at 1DC7V2FileReadError DB 'Error reading file' ;was at 1DD7 DB 0DH, 0AH, '$'4NotMapFile DB 'Not a keyboard map file' ;was at 1DEC DB 0DH, 0AH, '$' IFE (SMALLER)D; Next one never referenced...7D_1E06 DB 'Loading keyboard maps is not ' ;was at 1E06I DB 'supported on this machine' DB 0DH, 0AH, '$'ENDIF ;IFE (SMALLER)'DefaultLoaded DB 0DH, 0AH ;was at 1E3F, DB 'The default keyboard map "STDUS.KEY" '# DB 'has been successfully loaded'I DB 0DH, 0AH, '$'"InvParm DB 0DH, 0AH ;was at 1E85 DB 'Invalid Parameter' DB 0DH, 0AH, 0DH, 0AH, '$'"Success DB 0DH, 0AH ;was at 1E9D# DB 'Has been successfully loaded' DB 0DH, 0AH, '$'$No_KEYBRD DB 'Please ' ;was at 1EBE DB 'run the KEYBRD program.' DB 0DH, 0AH, '$' IFE (SMALLER)e; Next one never referenced...+D_1EDF DB 'DECKEYB requires ' ;was at 1EDFs DB 'an LK250 keyboard.'e DB 0DH, 0AH, '$'; Next one never referenced...)D_1F05 DB 'DECKEYB is not ' ;was at 1F05p DB 'supported on this system.' DB 0DH, 0AH, '$'ENDIF ;IFE (SMALLER)$NoSuchFile DB 0DH, 0AH ;was at 1F30% DB 'The specified file is invalid 'D DB 'or does not exist: $'C!D_1F64 DB 0DH, 0AH ;was at 1F64C!NoPath DB 0DH, 0AH ;was at 1F66J DB 'The PATH= string was not ' DB 'found in the environment'S DB 0DH, 0AH, '$'&CantFindFile DB 0DH, 0AH ;was at 1F9C( DB 'The path is not set and the file '* DB 'was not found in either the current' DB 0DH, 0AHs DB 'or the root directory $'!D_1FFB DB 0DH, 0AH ;was at 1FFB0%PathTooLong DB 0DH, 0AH ;was at 1FFD ! DB 'The path entry is too long'] DB 0DH, 0AH, '$'%KeySignature DB 'KE$Y' ;was at 201C]%D_2020 DB 'STDUS', 00H ;was at 2020,(D_2026 DB 'STDUS.KEY', 00H ;was at 2026FileHandle DW 0 ;was at 2030# DB 100H DUP (0) ;stack goes hereStackTop EQU $ ;was 2132 _TEXT ENDS END Start**[PCSA.APPLICATION.KEYBRD.KIT]DECKEYB.COM;1+,3r./ 44-:0123 KPWO56,7;n89GHJICODEVQ>u{ t=u>uH=u>u&=uXPS[Xmu ]t3t.=it=ju$:=ְ2:%.>u.>u Y^.6.6Y^VQ$ |;< |7AP2- X4 t!" F t:tɃF2Y^SӠȎ2{g9 [P..%!X..PSаȎа[XÌȎм4Ȏ_^ÌȎË6#Ȏ"t#( Ë2>!ô0!u <rws < t < t< t dÿ@G<.t&O<.tO<.tO<.t^Ë> O< uG< u1!K}b!&,I!u 1!L! <\t<:uFF6#VW5!ȎؾuhWH_ypg^VMD<4  ,,2_^PSڀ?tC$ ! ! ![XPRS5!..Ȏ.[ %![ZX>ƅ.<\uF.b!&,R tK& t3d=@\>t>t0>t)>tE<t> ! !- h!#KƇ.$ 2! .! !33P#t &E tQ)Yu3@& t<;t FG\tƄ\FGWn!'N!s<t<t <t_W޾.=@P؎X_STDUS.KEY.KEY\ $PATH=SD Incorrect Dos Version This program requires MS-DOS V3.10/PC-DOS V3.10 or later $ Keyboard Map Loader Version 2.20 (C)Copyright 1985-1989 by Digital Equipment Corporation Modified version 0.42 by Nick Brown, November 1991 $This version of DECKEYB is not supported on a VAXmate. $ DECKEYB uses the following format DECKEYB [filename] [/D] [/D] loads the default keyboard map Example: DECKEYB STDUK.KEY $File not found $Error reading file $Not a keyboard map file $ The default keyboard map "STDUS.KEY" has been successfully loaded $ Invalid Parameter $ Has been successfully loaded $Please run the KEYBRD program. $ The specified file is invalid or does not exist: $ The PATH= string was not found in the environment $ The path is not set and the file was not found in either the current or the root directory $ The path entry is too long $KE$YSTDUSSTDUS.KEY'*[PCSA.APPLICATION.KEYBRD.KIT]GASP.INC;1+,$m./ 4J-:0123KPWO56MH7 89GHJG; GASP.INC - useful macros to enable TSRs to output strings and values.J; If GASP_SWITCH is defined, may use variable [Gasping] to control output.;+; Macro to output a (constant) text string.;Gasp MACRO string, wait5 LOCAL msg ;For some reason these must be defined...' LOCAL skip ;...outside the IF...ENDIF LOCAL myloop LOCAL return IF (DEBUG) jmp skipmsg DB &string&skip: pushfIFDEF GASP_SWITCHIF (GASP_SWITCH) cmp [Gasping], 0 je returnENDIF ;IF (GASP_SWITCH)ENDIF ;IFDEF GASP_SWITCH push ax push bx push cx mov cx, (skip - OFFSET msg) mov bx, OFFSET msgmyloop:, mov ah, 0EH ;;BIOS function to output byte mov al, cs:[bx] int 10H inc bx loop myloop IFNB xor ah, ah int 16HENDIF ;IFNB  pop cx pop bx pop axreturn: popfENDIF ;IF (DEBUG) ENDM;; Macro to output AL in hex.; GaspAL MACRO LOCAL g1 LOCAL g2 LOCAL return IF (DEBUG) pushfIFDEF GASP_SWITCHIF (GASP_SWITCH) cmp [Gasping], 0 je returnENDIF ;IF (GASP_SWITCH)ENDIF ;IFDEF GASP_SWITCH push ax;;;; High nibble to hex;; shr al, 1 shr al, 1 shr al, 1 shr al, 1 cmp al, 0AH jl g1 add al, 7g1: add al, '0' mov ah, 0EH int 10H pop ax push ax and al, 0FH cmp al, 0AH jl g2 add al, 7g2: add al, '0' mov ah, 0EH int 10H pop axreturn: popfENDIF ;IF (DEBUG) ENDM;+; Macro to output an 8-bit register in hex.;Gasp8 MACRO register IF (DEBUG) push ax mov al, register GaspAL pop axENDIF ;IF (DEBUG) ENDM;,; Macro to output an 16-bit register in hex.;Gasp16 MACRO register; IF (DEBUG) push ax mov ax, register mov al, ah GaspAL3 pop ax ;;needed if AX is the register in question push ax mov ax, register GaspAL pop ax;ENDIF ;IF (DEBUG) ENDM)*[PCSA.APPLICATION.KEYBRD.KIT]KEYBRD.ASM;1+,qJ./ 4O-:0123KPWO561E7,( 89GHJ ; KEYBRD.ASMM; A disassembled, annotated, and enhanced version of KEYBRD.EXE from PCSA V4.?; Reverse engineering by Nick Brown, 1991. NO rights reserved.D; Please Uncle Ken, don't sue me; I just want to use less memory and0; get some extra functionality from my LK250...;I; If you have a non-US keyboard, this file's scan-code mapping data areasL; should be kept in step with STD??.ASM (see sample STDFR.ASM, which should#; be lying around near this file).;; Revision history:;; 07-NOV-1991 V0.439; Ctrl = Ctrl-Shift and Alt = Shift-Alt for all mappings.5; Non-US base mappings have a message to this effect.5; Made extended BIOS check calls non-resident in TSR.D; (Apparently) fixed problem caused by call to INT 15h function C0h,K; which causes 2-key codes and sometimes shift-up to be missed on 386 PCs.;; 24-OCT-1991 V0.42"; Close file handles on going TSR.8; Added support for F11/F12 in native mode key mappings.N; Removed Shift-XX codes in native mode key mappings (added in error at 0.40).E; Most key mappings are now shared with STDXX.KEY in an include file.;; 23-OCT-1991 V0.41F; Added support for 132 column toggling (DECstation 316) via Ctrl-F20.N; Fixed problem whereby STD??.KEY files with flags byte 20H (allowing compose)); did not put keyboard into native mode.O; Added Ctrl-Home, Ctrl-End, Ctrl-PgUp, Ctrl-PgDn, and KP enter, in native mode; key mappings.;; 22-OCT-1991 V0.405; First public release. Version number is arbitrary.N; Features WP compose, native mode, ctrl-up/down arrows (dark grey and white),-; alt-arrows (dark grey only), ctrl-ins/del.J; Includes support for French key mappings. If this is assembled, it willO; not be possible to use ctrl-alt-F2/3 to toggle languages, unless DECKEYB.COMK; is used to load STDUS.KEY. Note that by default, DECKEYB.COM checks forH; that filename and refused to load it. So either rename it, or modify!; DECKEYB.COM (see DECKEYB.ASM).F; Some extended codes send 0E0H as low byte rather than 0; see PhoenixN; BIOS manual. 0 probably works OK as well. Perhaps I should systematically; replace more 0's by 0E0H's.;G; Set the next variable to 1 to assemble the file just like the DigitalK; original version. All other conditional assembly variables should go toK; zero when this is done. As a check, after assembling with this variableG; set to 1, dump both this version and the Digital-supplied version ofD; KEYBRD.EXE into separate files using DEBUG; the files should then1; compare identically. (DEBUG command: U0L16E1).;!IFDEF ORIGINAL ;TASM /dORIGINAL/DEC_original EQU 1 ;Assemble just like DigitalELSEDEC_original EQU 0ENDIF ;IFDEF ORIGINAL;*; Select enhancements to keyboard mapping.;)ENHANCED_MAP EQU 1 AND (NOT DEC_original);8; Select smaller instructions, suppression of NOPs, etc.;%SMALLER EQU 1 AND (NOT DEC_original);5; Select suppression of JMP on-the-spot instructions.K; Normally you probably don't want to do this. I included this possibilityI; while desperately trying to find why the LK250 has problems talking toF; moderately fast CPUs (eg 80386sx @ 16MHz, hardly Grand Prix stuff).;NO_WAITS EQU 0;G; Assemble code to allow LK250 to run in native mode wherever possible.;(NATIVE_MODE EQU 1 AND (NOT DEC_original);; Assemble debugging code.;$;DEBUG EQU 1 AND (NOT DEC_original) DEBUG EQU 0;8; Assemble code to enable SysReq function with Alt-F20 .;SYS_REQ EQU DEC_original;D; Assemble code to enable Wide Screen toggle function with Ctrl-F20.?; This assumes DECstation 316 hardware; your system may differ.;(WIDE_SCREEN EQU 1 AND (NOT DEC_original);H; Assemble code to map Compose (native mode) to Alt-C (for WordPerfect).?; We do this at our site; you may wish to choose another value.8; Shift-Compose will give the original code for Compose.;'WP_COMPOSE EQU 1 AND (NOT DEC_original);1; Assemble code to enable /X command line option.H; This is an undocumented feature of KEYBRD, allowing it to be tested inF; an AT environment while pretending that the PC has only an XT BIOS.;SLASH_X EQU DEC_original;<; Assemble code to make extended BIOS checking non-resident.G; This also eliminates the bug of missing halves of "double" scan codes<; in IBM mode, and missing shift-up characters, on 386 PCs.;(ATBIOS_SAVE EQU 1 AND (NOT DEC_original); ; Select national language code.N; If US is not selected, be aware that to do dead keys or 3-character compose,G; you must load another (or the same !) mapping with DECKEYB. To loadL; STDUS.KEY, either rename it, or modify DECKEYB, since DECKEYB filters theH; filename STDUS.KEY and refuses to load it (it assumes that KEYBRD has; the US mapping already).;3UNITED_STATES EQU 1 OR DEC_original ;US key mapping%FRENCH EQU 1 AND (NOT UNITED_STATES);COUNTRIES EQU 0 + \ UNITED_STATES + \ FRENCH + \ 0ERRIF (COUNTRIES NE 1);L; Assembly options. This program has been tested with Turbo Assembler V2.0.M; If using DEC_original, you may need up to 3 passes to get the more marginal4; 8-bit relative jumps to work exactly like before.; LOCALS DOSSEG;IFE (DEC_original) JUMPSENDIF ;IFE (DEC_original);; Debug macro definitions.;I; If you use the "Gasp" debug macros to look at the scan codes in Int09h,I; be aware that SETHOST hooks INT 09h and handles some scan codes itselfK; (without chaining to the KEYBRD INT 09h ISR). Typically, KEYBRD may seeL; only a key-up code for some keys, and for F12 and F13, even the key-up is; handled entirely by SETHOST.;%NOLIST1GASP_SWITCH EQU 1 ;Allow GASPing to be switchable include gasp.inc%LIST;!; Keyboard scan code definitions.;%NOLIST include scancode.inc%LIST;); National key mapping macro definitions.;%NOLIST include keymaps.inc%LIST)ROMbase EQU 0F000H ;BIOS ROMs start here$ROMid0 EQU 0FFFDh ;BIOS ROM ID byte$ROMid1 EQU 0FFFEh ;BIOS ROM ID byte9DEChere EQU 0FFh ;Reply from Digital-written INT handler,WeFail EQU 0FFh ;DOS exit status on failure+PSP_cli EQU 80H ;PSP CLI parameters offset;; Items in BIOS data area.;BiosData SEGMENT AT 40H; ORG 17hShiftStatus DB ? ;Shift status"ShiftExStat DB ? ;Extended status%DecimalAcc DB ? ;Decimal accumulator+KBhead DW ? ;Keyboard buffer head pointer+KBtail DW ? ;Keyboard buffer tail pointer ORG 49hDisplayMode DB ? ;Video mode ORG 65h6Mode3x8 DB ? ;Video 3x8 setting (whatever that does) ORG 71h-BreakByte DB ? ;Break pending flag (top bit)ResetWord DW ? ;Reset flag ORG 80H(KBstart DW ? ;Start of keyboard buffer$KBend DW ? ;End of keyboard buffer ORG 97h2AT_status DB ? ;Keyboard LEDs status (and others);; Flags in AT_status byte.;AT_l_scroll EQU (SCROLL SHR 4) AT_l_numlock EQU (NUMLOCK SHR 4)AT_l_caps EQU (CAPS SHR 4);bit 3 reservedAT_comm_ACKed EQU 10HAT_comm_NACKed EQU 20HAT_comm_busy EQU 40HAT_comm_error EQU 80H; BiosData ENDSBiosCode SEGMENT AT ROMbase; ORG 0E05BhIBMreset LABEL FAR ORG 0FFF0HCPUreset LABEL FAR; BiosCode ENDS;"; Shift and other KB status flags.;RSHIFT EQU 01hLSHIFT EQU 02h CTRL EQU 04h ALT EQU 08hSCROLL EQU 10HNUMLOCK EQU 20H CAPS EQU 40HINSERT EQU 80H8ALL_SHIFTS EQU (RSHIFT OR LSHIFT OR CTRL OR ALT OR CAPS);; Flags in ShiftExStat.G; These more or less correspond to the IBM definitions, except that ourN; bit 2 (mask 04h) is the insert mode flag, whereas IBM use bit 7 (mask 80H).,; We do not use IBM's bit 2 (SysReq pressed);ShExCTRL EQU (CTRL SHR 2)ShExALT EQU (ALT SHR 2)-ShExInsert EQU 04h ;Inserting in command line)ShExHold EQU 08h ;Hold screen in progressShExSCROLL EQU SCROLLShExNUMLOCK EQU NUMLOCKShExCAPS EQU CAPS;; I/O addresses.;INTA00 EQU 20HINTA01 EQU 21hKCdata EQU 60HKCcontrol EQU 61hKCstatus EQU 64h;/InterruptDone EQU 20H ;Reset 8259A (at INTA00);#; Keyboard controller status flags.;*KC_OutputFull EQU 01h ;output buffer full(KC_InputFull EQU 02h ;input buffer full;; Keyboard controller commands.;8KB_rd_command EQU 20H ;get command byte from controller:KB_wr_command EQU 60H ;next byte is command to controller+KB_test EQU 0ABh ;test keyboard interface&KB_disable EQU 0ADh ;disable keyboard$KB_enable EQU 0AEh ;enable keyboard;9; AT and/or LK250 enhanced keyboard commands and replies.;LK250_native EQU 0AChLK250_IBM EQU 0ADhAT_set_leds EQU 0EDhAT_autorepeat EQU 0F3hAT_enable EQU 0F4hAT_ACK EQU 0FAhAT_resend EQU 0FEh;I; Want to have your LK250 in native mode all the time ? Then you need toK; stop Digital programs resetting it. Firstly, assemble this program withL; NATIVE_MODE enabled. Then, patch the images which do not use the INT 16h2; FUN 0DxH functions of this program, as follows:+; SETHOST.EXE (V4.0.10, size 180655 bytes):A; - replace 0ADh at byte A501 of initial code sp5y~ LK250.BCKqJ:)[PCSA.APPLICATION.KEYBRD.KIT]KEYBRD.ASM;1Oegment with 0ACh.N; SEDT.EXE is OK, as it saves the keyboard state and restores it. I am tryingE; to find a way to monitor its output bytes to see how it does this. ; Others ?;E; Also look for LKNATIVE.ASM near this file; it is a short program toK; put the LK250 back into native mode, useful if you prefer to run that in(; a .BAT file rather than patch images.; IF (SLASH_X);;; Mask to convert a character to upper case (if alphabetic);UpperCase EQU 11011111BENDIF ;IF (SLASH_X);F; Macro to waste some time before resetting interrupt controller, etc.;WasteSomeTime MACROIFE (NO_WAITS) JMP $+2ENDIF ;IFE (NO_WAITS) ENDM;J; It's nicer (and makes for a smaller TSR) to have a real stack segment...;IFE (DEC_original)!_STACK SEGMENT PARA STACK 'STACK' DB 100H DUP (?) _STACK ENDSENDIF ;IFE (DEC_original);G; To allow us to check that we can still reproduce the original DigitalI; KEYBRD 4.0(2), warts and all, we define the TSR non-resident code hereK; in a large macro. By assembling this first, we get the original KEYBRD;<; by assembling it after the TSR code, we save nearly 1K...;NonResCode MACRO;Start: PUSH DS MOV CS:[PSP], ES* MOV DX, OFFSET Copyright ;(C) Digital etc CALL CSmsg;; Look for any parameters.9; Only one allowed is /X (pretend system has an XT BIOS).; MOV SI, PSP_cli MOV CL, ES:[SI] INC SI CALL GetParm! JNB ParmOK ;no parameters found IF (SLASH_X)& CMP BYTE PTR ES:[SI], '/' ;look for / JNZ ParmErr ;otherwise error MOV AL, ES:[SI+01]% AND AL, UpperCase ;look for /X or /x CMP AL, 'X' JNZ ParmErr ;otherwise error# SUB CL, 2 ;check nothing after /X JB ParmErr ;otherwise error ADD SI, 2" CALL GetParm ;check nothing left JB ParmErr ;otherwise error MOV AL, 0FFh, MOV CS:[SlashXFlag], AL ;remember /X option JMP ParmOKENDIF ;IF (SLASH_X);; Come here on parameter error.;ParmErr: ;was at 013D* MOV DX, OFFSET InvParm ;Invalid parameter CALL CSmsg MOV AL, WeFail JMP Finish;=; Subroutine to search for (and skip) spaces in command line.;GetParm: ;was at 0147@@loop: CMP CL, 0 CLC JZ GetParm_ret CMP BYTE PTR ES:[SI], ' ' STC JNZ GetParm_ret DEC CL INC SI JMP @@loop GetParm_ret: RET;?; Come here after processing parameters - check not on VAXmate.;ParmOK: ;was at 015A MOV AX, ROMbase MOV DS, AX MOV SI, ROMid0 CMP BYTE PTR [SI], 0D2h JNZ ChkDOS) MOV DX, OFFSET NoVAXmate ;not on VAXmate CALL CSmsg MOV AL, WeFail JMP Finish;; Check for DOS 3.1 or higher;ChkDOS: ;was at 0171 MOV AH, 30H INT 21h CMP AL, 3# JB BadVersion ;less than 3 is bad JA DOS_OK ;4 or higher is OK# CMP AH, 0AH ;3.10 or less is bad JNB DOS_OKBadVersion: ;was at 0180' MOV DX, OFFSET BadDOS ;Bad DOS version CALL CSmsg MOV AL, WeFail JMP Finish;+; Code to see if KEYBRD is already running.E; First there is a check to see if INT 16h is redirected. This takesI; the form of a direct check on the segment part of the vector; if it isE; not F000, we assume it has been redirected away from the ROM BIOS.;DOS_OK: ;was at 018A XOR AX, AX MOV DS, AX MOV SI, 005Ah CMP WORD PTR [SI], ROMbase JZ L_01AD;G; Something else has grabbed INT 16h; see if it's KEYBRD, by calling it0; with function 0DEh and seeing what comes back.; MOV AX, 0DE00H INT 16h& CMP AL, DEChere ;KEYBRD returns 0FFh JNZ L_01AD4 MOV DX, OFFSET AlreadyLoaded ;KEYBRD already loaded CALL CSmsg MOV AL, WeFailFinish: POP DS MOV AH, 4Ch INT 21h;;; Come here after startup stuff seems to more or less work.;;8; See if INT 14h has been redirected away from ROM BIOS.;L_01AD: XOR AX, AX MOV DS, AX MOV SI, 0052h CMP WORD PTR [SI], ROMbase JZ L_01E5;9; INT 14h has not been hooked... see if INT 17h has been.; XOR AX, AX MOV DS, AX MOV SI, 005Eh CMP WORD PTR [SI], ROMbase JZ L_01E5;F; Either INT 14h (serial port) or 17h (parallel port) has been hooked.B; If it was by a Digital program, we can (must) use that program'sH; storage area, which an INT call will return in BX. This allows us to/; share space with the TSR version of DECMODE.; MOV AX, 0040H MOV DS, AX MOV AX, 0DE00H INT 14h0 CMP AL, DEChere ;Does DEC program own INT 14h ? JZ L_01DE ;Jump if so MOV AX, 0DE00H INT 17h0 CMP AL, DEChere ;Does DEC program own INT 17h ? JNZ L_01DE ;Jump if not;9; Come here if a Digital program owns INT 14h or INT 17h.*; If so, BX contains address of work area.;L_01DE: MOV CS:[WorkPtr], BX JMP L_0200;H; Come here when INTs 14h and 17h are both still hooked by the ROM BIOS.;L_01E5: MOV BX, OFFSET WorkArea MOV CX, 4 SHR BX, CL MOV AX, CS ADD AX, BX' MOV ES, AX ;ES points to 1K data area MOV CS:[WorkPtr], AX MOV AX, 0040H MOV ES:[BDAseg], AX MOV DS, AX;; Save current INT 09h vector.;L_0200: MOV AH, 35h MOV AL, 09h INT 21h MOV WORD PTR CS:[VEC09], BX MOV WORD PTR CS:[VEC09+2], ES;; Save current INT 16h vector.; MOV AH, 35h MOV AL, 16h INT 21h MOV WORD PTR CS:[VEC16], BX MOV WORD PTR CS:[VEC16+2], ES;7; Output success message (apparently we can't fail now);, MOV DX, OFFSET Success ;Successfully loaded CALL CSmsg CALL GetBDA;!; Get address of work area to ES.; PUSH DS MOV DS, CS:[WorkPtr] MOV AX, DS MOV ES, AX POP DSIF (ATBIOS_SAVE);7; See if we are on an AT-class machine (parts 1 and 2);); we will used the stored results later.; CALL Check_ATbus JNC @@skip1 MOV ES:[ATbus_seen], 1 MOV ES:[AT_any_seen], 1@@skip1: CALL Check_AT_BIOS JNC @@skip2 MOV ES:[AT_BIOS_seen], 1 MOV ES:[AT_any_seen], 1@@skip2:ENDIF ;IF (ATBIOS_SAVE);,; Keyboard reset routine (for AT BIOS only).;IF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JZ L_0254ELSE CALL Check_ATbus JB L_023E CALL Check_AT_BIOS JNB L_0254L_023E:ENDIF ;IF (ATBIOS_SAVE);F; Wait 20 * 81920 clock cycles (200 ms at 8MHz) looking for character.; MOV CX, 0014h@@outer: IN AL, KCstatus4 TEST AL, KC_OutputFull ;anything in output buffer ? JZ L_0254 ;exit loop if not CALL ClearKBbuffers PUSH CX MOV CX, 2800H IFE (SMALLER) NOPENDIF ;IFE (SMALLER)@@inner:# LOOP @@inner ;waste 81920 clocks POP CX LOOP @@outer;); Come here when keyboard has been reset.;L_0254: CLI* CALL InitBuf ;reset buffer and pointers PUSH DS; PUSH ES POP DS; CALL SetUpTable XOR AX, AX MOV ES:[KBmagic], AL POP DS ;DS is now BDA again; MOV [ResetWord], AX MOV [DecimalAcc], AL MOV [BreakByte], AL;+; Hook vectors 09h and 16h to this program.; PUSH DS; PUSH CS POP DS; MOV DX, OFFSET Int09h MOV AH, 25h MOV AL, 09h INT 21h MOV DX, OFFSET Int16h MOV AH, 25h MOV AL, 16h INT 21h; POP DS; STIIF (ATBIOS_SAVE) CMP ES:[ATbus_seen], 0 JNZ L_0297M CMP ES:[AT_BIOS_seen], 0e JZ L_029CELSE CALL Check_ATbus JB L_0297 CALL Check_AT_BIOSr JNB L_029CrENDIF ;IF (ATBIOS_SAVE)e IN AL, INTA016 AND AL, 0FDh ;reset bits 0/1 of interrupt controller WasteSomeTime OUT INTA01, ALtL_0297:  MOV AL, KB_enable CALL DoKBstatusL_029C:t POP DSsIFE (DEC_ORIGINAL);r!; Close file handles 0 through 4.h;D mov bx, 4 loop_close:A mov ah, 3Eh( int 21h ;No check to see if it went OK dec bxi jge loop_closeENDI6"F ;IFE (DEC_ORIGINAL) ;=; Deallocate our environment. ;  MOV ES, CS:[PSP]o) MOV ES, ES:[002CH] ;ES = our environmentf& MOV AH, 49h ;deallocate memory at ES INT 21h;n; Become a TSR.n;)% MOV DX, TextLen ;our size (was 17E0)h! ADD DX, 10H ;round up paragraphe MOV CL, 04o# SHR DX, CL ;convert to paragraphsP ADD DX, 10H ;space for PSP MOV AX, 3100H ;TSR call% INT 21h ;see you under interrupt...1;n; Display string at CS:DX ;oCSmsg: ;was at 02BD PUSH DS PUSH CS POP DSe MOV AH, 09h INT 21h POP DSm RET ;was at 02C6*Copyright DB 0Dh, 0AH, 'KEYBRD.EXE V4.02 '! DB '(C)Copyright 1987-1991 by 'r$ DB 'Digital Equipment Corporation'IFE (DEC_original) DB 0Dh, 0Ahh9 DB 'Modified version 0.43 by Nick Brown, November 1991')IFE (UNITED_STATES)o DB 0Dh, 0Ah  DB 'Contains ' IF (FRENCH)- DB 'French' ENDIF ;IF (FRENCH) DB ' keyboard map by default'iENDIF ;IFE (UNITED_STATES)ENDIF ;IFE (DEC_original)  DB 0Dh, 0AH, 0AH, '$'y ;was at 0314)Success DB 'KEYBRD successfully loaded.'  DB 0Dh, 0AH, '$' ;was at 0332)AlreadyLoaded DB 'KEYBRD already loaded.'d DB 0Dh, 0AH, '$' ;was at 034B7NoVAXmate DB 'KEYBRD cannot be installed on a VAXmate.'  DB 0Dh, 0AH, '$' ;was at 0376#BadDOS DB 'Incorrect DOS version.'d DB 0Dh, 0AH, '$' ;was at 038F InvParm DB 'Invalid parameter.' DB 0Dh, 0AH, '$'M;was at 03A4 - don't know what it does (maybe it needs to go before Int16h ?) D_03A4 DB '0201KBD';00; End of the TSR non-resident code (macro form).;a ENDM0;aG; The extended BIOS check calls do not need to be resident; we can just'I; call them once and store the results. We will not suddenly acquire ank; extended BIOS at runtime.I; The macros defined here allow us to make them resident for the originalnD; version, or to have them non-resident and just consult variables.;tExtended_BIOS MACROe;oJ; Return carry clear if /X was specified, or if we do not have an AT BIOS.?; The latter is defined by a byte of 0FCh or 0FAh at F000:FFFE.c3; Olivetti (DECstation) BIOSes are extended (0FCH).I;Check_AT_BIOS: ;was at 12FC  PUSH SI PUSH DS IF (SLASH_X)( CMP CS:[SlashXFlag], 0FFh ;/X option ? CLC# JZ Check_AT_BIOS_return ;go if soENDIF ;IF (SLASH_X)D MOV SI, ROMbase MOV DS, SIt MOV SI, ROMid1i0 CMP BYTE PTR [SI], 0FCh ;0FCh at end of BIOS ? STC# JZ Check_AT_BIOS_return ;go if sor0 CMP BYTE PTR [SI], 0FAH ;0FAh at end of BIOS ? STC# JZ Check_AT_BIOS_return ;go if son CLCCheck_AT_BIOS_return:w POP DSe POP SIy RET; 3; See if /X was specified, or if we have an AT bus. ;tCheck_ATbus: ;was at 131Fr PUSH AX PUSH BX PUSH ES IF (SLASH_X)' CMP CS:[SlashXFlag], 0FFh ;/X option ?n JZ L_133A ;go if soENDIF ;IF (SLASH_X)M3 MOV AH, 0C0H ;get address of System Config Paramsi INT 15h1 JB L_133A ;INT 15h function 0C0H not supportedU7 TEST BYTE PTR ES:[BX+05h], 2 ;see if there's an AT bust JZ L_133A ;go if notD STC JMP L_133B L_133A:c CLCL_133B:i POP ES  POP BXn POP AXh RET;.3; End of the extended BIOS check code (macro form). ;  ENDM _TEXT SEGMENT WORD PUBLIC 'CODE' ASSUME CS:_TEXT ASSUME DS:BiosDataC ASSUME ES:WorkSegSegTop:fIF (DEC_original)e; D; Digital started their KEYBRD.EXE with a stack in the code segment.<; Here it is; ignore the "No Stack" message from the linker.1; By a miracle, DOS sets the SS register up OK...o;a* DB 100H DUP (?) ;Stack in code segment !;cI; Digital also had the first chunk of code (the bit which initialises thetG; program and doesn't need to be in the TSR) here, which makes the TSRo(; almost 1K bigger than it needs to be.;d NonResCodeg;nENDIF ;IF (DEC_original);t; Handler for INT 16h.;sInt16h:l STI PUSH CX PUSH DX PUSH DS PUSH ES PUSH BP;t IFE (SMALLER)." PUSH CX ;will be popped at POP 1ENDIF ;IFE (SMALLER) MOV DX, ESg CALL GetBDA; !; Get address of work area to ES.o;a IF (SMALLER) MOV ES, CS:[WorkPtr]mELSE PUSH DS MOV DS, CS:[WorkPtr]g MOV CX, DS. MOV ES, CX  POP DSYENDIF ;IF (SMALLER)r;f CALL CheckLightsC IFE (SMALLER) POP CX ;POP 1.ENDIF ;IFE (SMALLER)+ CMP AH, 0DEh ;AH=0DEh -> test (return FF)p JNZ L_03D4 MOV AL, DEChere IF (SMALLER) MOV BX, ESeELSE: MOV BX, CS:[WorkPtr] ;return address of work area as wellENDIF ;IF (SMALLER)_ JMP L_041DFL_03D4:, CMP AH, 0DFh ;AH=0DFh -> close down KEYBRD JNZ L_03DET CALL R_1397 ;unhook vectors$ JMP L_041D ;return from interruptL_03DE:y" PUSH CX ;will be popped at POP 2 XOR CH, CHa MOV CL, AHr CMP CL, 13h ;less than 13h ? IFE (SMALLER)L NOPENDIF ;IFE (SMALLER) JB L_0409 ;if so, dispatch itE SUB CL, 0D0H ;less than 0D0H ?  JB L_03F8 ;if so, return& ADD CL, 13h ;between 0D0H and 0D6h ? IFE (SMALLER)n NOPENDIF ;IFE (SMALLER) CMP CL, 1Ah IFE (SMALLER)m NOPENDIF ;IFE (SMALLER) JB L_0409 ;if so, dispatch ithL_03F8:y POP CX ;POP 2a% POP BP ;pop saved registers to exity POP ESr POP DSF POP DXt POP CX ! CMP AH, 0F0H ;0F0H or greater ?. JGE L_0404 ;if so, let other handler have it IRET ;otherwise returneL_0404:' JMP CS:[VEC16] ;go to original handlern;s'; Come here to jump via dispatch table.;SL_0409: SHL CL, 1) MOV BP, CX ;BP = byte offset into table POP CX ;POP 2s" CMP WORD PTR CS:[BP+Dispatch], -1 JZ L_03F8 CALL WORD PTR CS:[BP+Dispatch]S% JB L_0423 ;return, preserving flagsR; (; Come here to return from INT 16h call.;rL_041D:  POP BP POP ESU POP DSO POP DXa POP CXi IRET;l:; Come here to return from INT 16h call, preserving flags.;aL_0423: POP BPa POP EST POP DS POP DX  POP CXf RETF 2B;S&; Dispatch table for INT 16h functions;t Dispatch:a DW Int16h_00_or_10c DW Int16h_01_or_11W DW Int16h_02u DW Int16h_03e DW 0FFFFh ;AH = 04ho DW Int16h_05  DW 0FFFFh ;AH = 06h DW 0FFFFh ;AH = 07ho DW 0FFFFh ;AH = 08ho DW 0FFFFh ;AH = 09h  DW 0FFFFh ;AH = 0Ahd DW 0FFFFh ;AH = 0Bha DW 0FFFFh ;AH = 0Chi DW 0FFFFh ;AH = 0Dhe DW 0FFFFh ;AH = 0Eh DW 0FFFFh ;AH = 0Fh  DW Int16h_00_or_10o DW Int16h_01_or_11 DW Int16h_12o DW Int16h_0D0 ;AH = 0D0HT DW Int16h_0D1 ;AH = 0D1hs DW Int16h_0D2 ;AH = 0D2h  DW Int16h_0D3 ;AH = 0D3h DW Int16h_0D4 ;AH = 0D4hH DW Int16h_0D5 ;AH = 0D5hL DW Int16h_0D6 ;AH = 0D6hC;HJ; Routine to beep the speaker; I suppose we could get the BIOS to do this.;_DoBeep: ;was at 045F PUSH AX PUSH CX;a& IN AL, KCcontrol ;get KB control port PUSH AX ;PUSH 1 MOV CX, 0080H ;loop 128 timesL_0467:t PUSH CX ;PUSH 2s2 AND AL, 0FCh ;turn off speaker and channel 2 gate OUT KCcontrol, AL! MOV CX, 00E0H ;waste 1920 clocksL_046F:0 LOOP L_046F6 OR AL, 02 ;turn on speaker, channel 2 gate stays off OUT KCcontrol, AL! MOV CX, 00E0H ;waste 1920 clocks)L_0478: LOOP L_0478 POP CX ;POP 2 - outer loop LOOP L_0467;I POP AX ;POP 1x: OUT KCcontrol, AL ;restore original control port settings;e POP CXu POP AX( RET;#; Routine for INT 16h AH=01h or 11hr;eInt16h_01_or_11: MOV AX, [KBhead]h5 CMP AX, [KBtail] ;Z flag now says if there's a charn PUSH DI PUSH ES MOV DI, [KBhead]r PUSH ES:[KBbufseg]x POP ESU, MOV AX, ES:[DI] ;get any char there may be POP ES POP DI  STC RET;N.; Routine to remove character from input queue;GetChar: ;was at 049B PUSH DI; PUSH ES MOV DI, [KBhead]e; $ PUSH ES:[KBbufseg] ;KB buffer to ES POP ES ;t MOV AX, ES:[DI] ;get next char  POP ESp;b ADD DI, 2 ;skip to nextU& CMP DI, [KBend] ;past end of buffer ? JB L_04BA- MOV DI, [KBstart] ;if so, loop back to startoL_04BA:e. MOV [KBhead], DI ;next char pointer in KBhead POP DIm RET;tL; Translate shift key (etc) in AL to flags in AH using ModKeys and ModFlags.#; Return with carry set on success.e;lTranShift: ;was at 04C0 PUSH BX PUSH CX PUSH DI PUSH SI PUSH ES; PUSH CS POP ES0; MOV DI, OFFSET ModKeyss MOV CX, 0008h CLD* REPNZ SCASB ;look for scan code in table JNZ ScanNotFounds MOV BX, 0007 SUB BX, CX  MOV SI, OFFSET ModFlags6 MOV AH, ES:[BX+SI] ;found it - get corresponding flag STC JMP ScanFound ScanNotFound:m CLC ScanFound: POP ESN POP SIE POP DI  POP CXt POP BXa RET;o;; See if there is room for another character in the buffer.aB; Return with carry clear and AX=byte offset of space if there is.;0AnyRoom: ;was at 04E7a MOV AX, [KBtail]  CMP AX, [KBhead]i JNB L_04F5a ADD AX, ES:[KBbufsize]dL_04F5: 4 SUB AX, [KBhead] ;AX now offset of next free space ADD AX, 0002 1 CMP AX, ES:[KBbufsize] ;will one more overflow ?  JZ Overflow ;go if sol CLC JMP AnyRoomReturn Overflow:t STCAnyRoomReturn: DEC AX  DEC AX  RET;t$; Routine for INT 16h AH=00H or 10H.); Wait for character and return it in AX.;aInt16h_00_or_10:' CALL Int16h_01_or_11 ;any character ? JNZ GoGetIt ;go if there isIF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JZ WaitForChartELSE CALL Check_ATbus JB L_0519 CALL Check_AT_BIOSF, JNB WaitForChar ;skip if not extended BIOSL_0519:ENDIF ;IF (ATBIOS_SAVE)e, MOV AX, 9002h ;entering keyboard busy code INT 15h WaitForChar:@@loop:A CALL CheckLightsC/ CALL Int16h_01_or_11 ;any character waiting ?  STI JZ @@loop ;loop if notaGoGetIt:5 CALL GetChar ;character waiting, get it and return CLC RET;); Routine for INT 16h AH=05h.T8; Puts char at CX into typeahead buffer and returns AX=0; Returns AX=1 if no room.;i Int16h_05:* CALL AnyRoom ;Room for any characters ? JB NoRoom ;Jump if noto MOV AX, CXO CALL CharToBuffer XOR AX, AXP IF (SMALLER) RETELSE JMP Int16h_05_retENDIF ;IF (SMALLER)NoRoom:f MOV AX, 1 ;No roomnInt16h_05_ret: RET;n; Routine for INT 16h AH=03h.>; If AL=05h, BL is autorepeat rate and BH is autorepeat delay.; Only valid on ATs and higher.;F Int16h_03:IF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JZ L_057DELSE CALL Check_ATbus: JB L_0548 CALL Check_AT_BIOSk' JNB L_057D ;skip if no extended BIOS.rL_0548:iENDIF ;IF (ATBIOS_SAVE)e CMP AL, 05h JNZ L_057Dm CMP BL, 20H( JNB L_057D ;max autorepeat rate is 1Fh CMP BH, 04h JNB L_057D ;t PUSH CX MOV CL, 05h SHL BH, CLC OR BL, BH POP CXe;b MOV AL, AT_autorepeat CALL AT_command TEST [AT_status], AT_comm_error JNZ L_0578% MOV AL, BL ;selected rate and delayF CALL AT_command TEST [AT_status], AT_comm_error IF (SMALLER) JZ L_057DELSE JNZ L_0578 JMP L_057DnENDIF ;IF (SMALLER)mL_0578: MOV AL, AT_enable CALL AT_commandL_057D:0 RET; ; Check for control-break.>; If found, return with carry set after calling interrupt 1BH.;oCtrlBreak: ;was at 057E TEST [ShiftStatus], CTRLs# JZ NotCtrlBreak ;go if not controle CMP AL, Scan_kp_pf3" JNZ NotCtrlBreak ;go if not break;" PUSH AX ;will be popped at POP1, MOV AX, [KBstart] ;clear type-ahead buffer MOV [KBhead], AX MOV [KBtail], AXV. MOV [BreakByte], 80H ;here's a break for BIOS; ; Enable keyboard.; IF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JNZ L_05ACaELSE CALL Check_ATbus ! JB L_05AC ;go if extended BIOSO CALL Check_AT_BIOS. JB L_05ACENDIF ;IF (ATBIOS_SAVE)V IN AL, INTA016 AND AL, 0FDh ;reset bits 0/1 of interrupt controller WasteSomeTime OUT INTA01, ALs JMP L_05B1oL_05AC:  MOV AL, KB_enable CALL DoKBstatusL_05B1:s INT 1Bh ;BIOS Ctrl-Break XOR AX, AX ! CALL CharToBuffer ;zero scancodec POP AX ;POP1;e STC IF (SMALLER) RETELSE JMP CtrlBreak_retENDIF ;IF (SMALLER)c NotCtrlBreak:  CLCCtrlBreak_ret: RET;t<; Handler for INT 09h (hardware interrupt when key pressed).; Int09h:b PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH DS PUSH ES PUSH BP;J; In this routine, BP != 0 says a grey key causes us to temporarily forget; a currently active NumLock.;e XOR BP, BPJ;L; Disable keyboard.E;lIF (ATBIOS_SAVE) MOV ES, CS:[WorkPtr] ;!!s CMP ES:[AT_any_seen], 0 JNZ L_05DDDELSE CALL Check_ATbush JB L_05DD CALL Check_AT_BIOS JB L_05DDENDIF ;IF (ATBIOS_SAVE) IN AL, INTA01. OR AL, 02 ;set bit 1 of interrupt controller WasteSomeTime OUT INTA01, ALD JMP L_05E2SL_05DD: MOV AL, KB_disableO CALL DoKBstatusL_05E2: IF (ATBIOS_SAVE) CMP ES:[AT_BIOS_seen], 0a JNZ L_05EBELSE CALL Check_AT_BIOS JB L_05EBENDIF ;IF (ATBIOS_SAVE)O MOV AL, InterruptDone OUT INTA00, AL(L_05EB:r STI CALL GetBDA;t IF (SMALLER) MOV ES, CS:[WorkPtr]DELSE PUSH DS MOV DS, CS:[WorkPtr]p MOV AX, DSs MOV ES, AXw POP DSNENDIF ;IF (SMALLER) ; XOR CX, CXuL_05FC:sIF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JZ L_060EELSE CALL Check_ATbusV JB L_0606 CALL Check_AT_BIOSD JNB L_060EDL_0606:mENDIF ;IF (ATBIOS_SAVE)E IN AL, KCstatus1 AND AL, KC_InputFull ;wait for input buffer fullp LOOPNZ L_05FC WasteSomeTimeL_060E:i' IN AL, KCdata ;here's the scan code !oIF (DEBUG) AND (SMALLER);;; D;;; See if we have overwritten anything in the extended work area...;;;B ;; PUSH AX ;; PUSH CX ;; PUSH DI ;; PUSH ES;;; ;; CLD ;; MOV AX, CSe ;; MOV ES, AXO;; MOV DI, OFFSET WorkDummyF;; MOV CX, WorkDummySize ;; XOR AL, ALM ;; REPE SCASBD;; JZ debug_skip;;;;;; Gasp 'OW at ' ;; Gasp16 DI;; Gasp ' *', 1 ;;debug_skip:H;;;V ;; POP ES] ;; POP DID ;; POP CX ;; POP AXrENDIF ;IF (DEBUG) AND (SMALLER)IF ((=ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JNZ L_062AEELSE CALL Check_ATbusS JB L_062A CALL Check_AT_BIOSe JB L_062AENDIF ;IF (ATBIOS_SAVE);A8; On a non-enhanced BIOS, just do what the IBM XT did...;  PUSH AX IN AL, KCcontrolp MOV AH, ALs OR AL, 80Ht OUT KCcontrol, AL MOV AL, AHD OUT KCcontrol, AL POP AXl JMP L_0651L;S(; Come here if we have an extended BIOS.;rL_062A:  PUSH AX MOV AL, InterruptDone OUT INTA00, AL, POP AXVIFE (NATIVE_MODE);(I; Allow the BIOS intercept routine to handle this scan code, if required.I; This is meaningless in native mode, as the scan codes do not corresponds/; to anything recognisable in the AT standard.e;, MOV AH, 4Fh INT 15h$ JB L_0639 ;go if not fully handled JMP CleanupL_0639::ENDIF ;IFE (NATIVE_MODE) CMP AL, AT_ACK_ JNZ L_0645 OR [AT_status], AT_comm_ACKed JMP CleanupL_0645:T CMP AL, AT_resend JNZ L_0651, OR [AT_status], AT_comm_NACKed_ JMP Cleanup;EH; Come here when the keyboard and interrupt controllers have been reset.;L_0651:2 TEST ES:[KBmagic], PreCallOKa JZ L_066D; 4; Call the pre-processing routine for any scan code.;t MOV AH, [ShiftStatus] CALL ES:[PreCall]4 CMP AL, AT_ACK ;returns AT_ACK to ignore this code JNZ PreProcessedS JMP Cleanup PreProcessed:S MOV [ShiftStatus], AHL_066D:E CALL CheckLightsO CMP AL, 00w JNZ L_0677o JMP ZeroScanL_0677:u TEST AL, 80H ;key released ?r JZ L_067E ;skip if not2 IF (DEBUG) Gasp '-' Gasp8 ALf Gasp '-'nENDIF ;IF (DEBUG) JMP L_0770DL_067E:C IF (DEBUG) Gasp '+'A Gasp8 AL: Gasp '+'AENDIF ;IF (DEBUG)s IFE (SMALLER)# CALL R_133F ;some forgotten callAENDIF ;IFE (SMALLER);], CALL CtrlAltDel ;won't return if it was... IF (SYS_REQ);SJ; Look for Alt-F20 (SysReq), which isn't very useful in most DOS programs.;,% CMP AL, Scan_f20 ;ignore if not F20 JNZ L_069F4 TEST [ShiftExStat], ShExInsert ;ignore if inserting JZ L_0692 JMP CleanupL_0692:  TEST [ShiftStatus], ALT$ JZ L_069F ;ignore if Alt not down CALL Alt_F20_down JMP Enabled_kb L_069F:BENDIF ;IF (SYS_REQ)NIF (WIDE_SCREEN);L; Look for Ctrl-F20, to toggle 132 column mode on DECstation 316 family PCs.;o% CMP AL, Scan_f20 ;ignore if not F20V JNZ Not_ctrl_f20L TEST [ShiftStatus], CTRLP* JZ Not_ctrl_f20 ;ignore if Ctrl not down;n; Handle Ctrl-F20 here.mL; For now, just toggle between whatever was there before, and 132x25 colour.J; Later, we may become more sophisticated, and look up the new value based; on the current one.;V MOV AH, 0Fh" INT 10H ;get current video mode0 CMP AL, VideoCO132X25 ;already in 132 columns ? JNZ SetVideo132 ;go if not2 MOV AL, ES:[SavedVideo] ;otherwise get saved mode JMP SetVideoMode ;a'; Come here to put PC into 132 columns.r;o SetVideo132:: MOV ES:[SavedVideo], AL ;save current non-132 column mode. MOV AL, VideoCO132X25 ;set up for 132 columns; '; Merge here to set video mode (in AL).D;P SetVideoMode:S XOR AH, AHM INT 10H JMP Cleanup;D Not_ctrl_f20:sENDIF ;IF (WIDE_SCREEN)hIFE (NATIVE_MODE).3 CALL HackGrey ;pretend grey key is on numeric padaENDIF ;IFE (NATIVE_MODE) CALL TranShift)- JNB NoModif ;go if not a special charactery' CMP AH, ALT ;Alt, Ctrl, or a Shift ?_ JA ToggleDown ;go if not+ OR [ShiftStatus], AH ;OR into shift status( MOV AH, [ShiftStatus] ;get shift status( AND AH, (CTRL OR ALT) ;Control or Alt ? JZ no_c_a ;go if not + SHR AH, 1 ;OR into extended shift status SHR AH, 1 OR [ShiftExStat], AHano_c_a: JMP Cleanup;H; A mode toggle key (SCROLL, NUMLOCK, CAPS, or INSERT) has been pressed.;' ToggleDown:3 CMP AL, Hack_ins ;Insert ? JNZ Not_Insert ;go if not - TEST [ShiftStatus], ALT ;Alt being pressed ?B JNZ L_0731 ;go if sod TEST ES:[KBmagic], KBmagic_10H3 JNZ L_073ED* TEST [ShiftStatus], NUMLOCK ;NumLock on ? JNZ L_073E ;go if soo Not_Insert:s4 TEST AH, [ShiftExStat] ;is this toggle already on ? JNZ L_06F1 TEST ES:[KBmagic], KBmagic_40He JNZ L_06F9. CALL R_09D7 JNB L_06F4xL_06F1:O JMP CleanupL_06F4:e CALL CtrlBreak JB L_06F1L_06F9:  TEST ES:[KBmagic], KBmagic_10H JZ L_0705 CMP AL, Scan_capslock JNZ L_073ESL_0705:m XOR [ShiftStatus], AH OR [ShiftExStat], AHt CMP AL, Hack_inse JZ L_073E CALL SetLights JMP CleanupNoModif: TEST ES:[KBmagic], KBmagic_40Hx JNZ L_0731A CALL R_09D7 JB L_06F1 CALL CtrlBreake JB L_06F1 CALL PrtScr JNB L_0731. JMP Enabled_kb L_0731:y TEST ES:[KBmagic], KBmagic_20HF JNZ L_073Et CALL AltNum ;Alt-number key ?* JB L_06F1 ;go if so (already processed)L_073E:  PUSH AX CALL AnyRoom_ POP AXC! JNB L_074B ;go if there's room;CF; Come here if there's a zero scan code (shouldn't happen), or no room4; in typeahead buffer, before resetting interrupts.;S ZeroScan:0 CALL DoBeepL_0748:S JMP CleanupL_074B:e/ CALL LookUpChar ;look up in translation table  CMP AX, 0FFFFh JZ L_0748 TEST ES:[KBmagic], PostCallOK JZ L_076B- OR BP, BP ;have we been ignoring NumLock ?  JZ skipnumlock ;go if noto OR [ShiftStatus], NUMLOCK XOR BP, BP ;forget grey key skipnumlock:8 CALL PostCallCaller ;someone wants to look at it first) JZ Cleanup ;go if they zeroed the byteIL_076B:A% CALL CharToBuffer ;insert characterf JMP Cleanup;r2; Come here if the scan code (AL) has top bit set.%; This means a key has been released.5; L_0770: ' AND AL, 7Fh ;reduce to key down codeo IF (SYS_REQ);3K; Look for Alt-F20 (which does something on VAXmates, but isn't very usefulf; in most DOS programs).d;m CMP AL, Scan_f20E JNZ L_0784  TEST [ShiftExStat], 04h JNZ L_077F_ JMP CleanupL_077F:t CALL Alt_F20_up JMP Enabled_kbL_0784:_ENDIF ;IF (SYS_REQ)iIFE (NATIVE_MODE)B CALL HackGreyENDIF ;IFE (NATIVE_MODE) CALL TranShifte( JNB Cleanup ;go if not a special char CMP AH, ALT NOT AHs& JA ToggleUp ;go if a mode toggle key;P8; Come here if Alt, or some combination of shifts + Ctrl;r. AND [ShiftStatus], AH ;clear other shift bits MOV AH, [ShiftStatus] AND AH, (CTRL OR ALT) SHR AH, 1 SHR AH, 1/ AND [ShiftExStat], (NOT (ShExCTRL OR ShExALT)) OR [ShiftExStat], AH  CMP AL, Scan_alt JNZ Cleanup;o*; Come here if Alt has just been released.; 8 TEST [DecimalAcc], 0FFh ;decimal accumulator non-zero ? JZ Cleanup ;go if still zeroN/ MOV AL, [DecimalAcc] ;get decimal accumulator XOR AH, AHd TEST ES:[KBmagic], PostCallOK JZ L_07C8 CALL PostCallCaller JZ L_07CBL_07C8: CALL CharToBufferL_07CB:C1 MOV [DecimalAcc], 00 ;clear decimal accumulatorr JMP Cleanup;cH; A mode toggle key (SCROLL, NUMLOCK, CAPS, or INSERT) has been pressed.;, ToggleUp:00 AND [ShiftExStat], AH ;forget this key's status;e9; Fall through here to Cleanup after a key up/down event.r;rCleanup:IF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JNZ Enable_kb_ATELSE CALL Check_ATbus; JB Enable_kb_AT CALL Check_AT_BIOSD JB Enable_kb_ATENDIF ;IF (ATBIOS_SAVE)0 IN AL, INTA016 AND AL, 0FDh ;reset bits 0/1 of interrupt controller WasteSomeTime OUT INTA01, ALC JMP Enabled_kb Enable_kb_AT:F MOV AL, KB_enable CALL DoKBstatus Enabled_kb:4- OR BP, BP ;have we been ignoring NumLock ?h JZ @@return9 OR [ShiftStatus], NUMLOCK ;if so, restore NUMLOCK statusD @@return:  POP BPn POP ESN POP DS( POP DI POP SI, POP DXF POP CXR POP BX POP AXE IRETE;D; Check lights match shift status byte and update them if necessary.;;CheckLights: ;was at 0802 PUSH AX PUSH CX MOV AL, [ShiftStatus]7 MOV CL, 04h ;get light-mapped flags to low three bitso SHR AL, CLh0 XOR AL, [AT_status] ;knock out bits already lit8 AND AL, (AT_l_scroll OR AT_l_numlock OR AT_l_caps) ;07h" JZ @@skip ;skip if lights are OK CALL SetLights@@skip:C POP CXb POP AXt RET;; Handle Alt-keypad numerics; AltNum: ;was at 0819 PUSH AX PUSH BX;P) TEST [ShiftStatus], ALT ;Alt held down ?p JZ NoAltNum ;go if not5 CMP AL, Scan_kp_7 ;within range of keypad numbers ?P" JB NoAltNum ;go if not (too low) CMP AL, Scan_kp_0 JA NoAltNum ;too highr6 CMP AL, Scan_kp_minus ;minus and comma are in the way JZ NoAltNum ;go if minus CMP AL, Scan_kp_comma JZ NoAltNum ;go if comma MOV BL, ALf XOR BH, BH& SUB BL, Scan_kp_7 ;get in range 0-117 MOV AL, BYTE PTR CS:[BX+KPnumTable] ;convert to numberh;A PUSH AX ;save digit/ MOV AL, [DecimalAcc] ;get decimal accumulator MOV AH, 0Ah MUL AH POP BX ;restore digit;F) ADD AL, BL ;add to decimal accumulatorD0 MOV [DecimalAcc], AL ;save decimal accumulator STC JMP AltNum_ret NoAltNum:  CLC AltNum_ret:0 POP BX POP AXh RET;; Routine for INT 16h AH=0D0H;3; AL=1 or 2: turn off bit enabling PreCall/PostCall2<; AL=81h or 82h: return vector for PreCall/PostCall in DX:BX;; AL=0FEh or 0FFh: set vector for PreCall/PostCall to DX:BXC;Int16h_0D0: ;was at 0853 PUSH DX PUSH DI PUSH BP MOV BP, SPo, ADD BP, 8 ;result returned on top of stack TEST AL, 0FFh JNZ L_0867o;g; Come here if AL=0P; 8 AND ES:[KBmagic], (NOT (PreCallOK OR PostCallOK)) ;0FCh JMP Int16h_0D0_retDL_0867: CMP AL, 0FFhe JNZ L_0876 ;a; Come here if AL=0FFh;M OR ES:[KBmagic], PreCallOKk MOV DI, OFFSET PreCallF JMP L_08832L_0876:n CMP AL, 0FEhe JNZ L_0890s;f; Come here if AL=0FEh;V OR ES:[KBmagic], PostCallOK MOV DI, OFFSET PostCall;P ; Merge here for AL=0FEh or 0FFh4; DX:BX contains new vector for PreCall or PostCall.; L_0883:c PUSH AX MOV ES:[DI], BX MOV AX, DXA MOV ES:[DI+02], AXt POP AXN JMP Int16h_0D0_retL_0890:h CMP AL, 01h JNZ L_089Cd;; Come here if AL=1 ;g" AND ES:[KBmagic], (NOT PreCallOK) JMP Int16h_0D0_retDL_089C:d CMP AL, 02h JNZ L_08A8P;E; Come here if AL=2 ;t# AND ES:[KBmagic], (NOT PostCallOK)P JMP Int16h_0D0_retL_08A8:t CMP AL, 81h JNZ L_08B9m;p; Come here if AL=81h ;s TEST ES:[KBmagic], PreCallOKS JZ L_08CA MOV DI, OFFSET PreCallB J0~ LK250.BCKqJ:)[PCSA.APPLICATION.KEYBRD.KIT]KEYBRD.ASM;1OAxQMP L_08D0uL_08B9:S CMP AL, 82h JNZ Int16h_0D0_rete;e; Come here if AL=82h;D TEST ES:[KBmagic], PostCallOK JZ L_08CA MOV DI, OFFSET PostCall JMP L_08D0,L_08CA:];f XOR BX, BXk MOV DX, BX_ JMP L_08D7[L_08D0:D MOV BX, ES:[DI] MOV DX, ES:[DI+02]mL_08D7: MOV [BP+02], DXInt16h_0D0_ret:A POP BPs POP DIi POP DXs CLC RET; ; Routine for INT 16h AH=02h; Returns shift status in AL.4; Int16h_02: MOV AL, [ShiftStatus] CLC RET;; Routine for INT 16h AH=12h*; Returns extended (?) shift status in AX.;Z Int16h_12: PUSH CX;d4; Convert LK250 extended shift status to IBM format.;C MOV AL, [ShiftExStat] AND AL, ShExInsert+' MOV CL, 5 ;IBM insert flag is top bitS SHL AL, CLF MOV AH, [ShiftExStat] AND AH, 73h ;binary 01110011 OR AH, AL MOV AL, [ShiftStatus]; POP CXe CLC RET;m7; Routine to insert character (AX) in typeahead buffer.r;lCharToBuffer: ;was at 08FD PUSH AX PUSH DI;R PUSH ES MOV DI, [KBtail],;B PUSH ES:[KBbufseg]e POP ESB;0 MOV ES:[DI], AX POP ESe; ADD DI, 2 CMP DI, [KBend] JB L_0922 MOV AX, [KBstart] CMP AX, [KBhead]C JZ L_0926 MOV DI, AXlL_0922:e MOV [KBtail], DIvL_0926:gIF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JZ CTB_retoELSE CALL Check_ATbusA JB L_0930 CALL Check_AT_BIOS JNB CTB_retL_0930:aENDIF ;IF (ATBIOS_SAVE)r, MOV AX, 9102h ;finished keyboard busy code INT 15hCTB_ret: POP DI POP AXt RET; O; Routine for INT 16h AH=0D1h - returns count of characters in typeahead bufferC; Int16h_0D1: ;was at 0938' CALL AnyRoom ;returns 2 * chars in AXi SHR AX, 1 CLC RET;5.; Routine to bring keyboard lights up to date.;eSetLights: ;was at 093F PUSH AX PUSH CX TEST [AT_status], AT_comm_busy JNZ L_097C ;go if not busy' OR [AT_status], AT_comm_busy ;busy nowp5 MOV AL, [ShiftStatus] ;convert shift status flags.. MOV CL, 04h! SHR AL, CL ;...to light codeso8 AND AL, (AT_l_scroll OR AT_l_numlock OR AT_l_caps) ;07h. MOV AH, AL ;AH = CAPS, NUMLOCK, and SCROLL. MOV AL, AT_set_leds ;set keyboard lights... CALL AT_command$ MOV AL, AH ;...to current values CALL AT_command0 TEST [AT_status], AT_comm_error ;go if no error JZ L_096E! MOV AL, AT_enable ;clear errorM CALL AT_commandL_096E:h;_); Clear error, busy, and all shift flags.0; IF (SMALLER) AND [AT_status], (NOT (\B& (AT_comm_error OR AT_comm_busy) OR \4 (AT_l_scroll OR AT_l_numlock OR AT_l_caps))) ;038hELSE AND [AT_status], (NOT \( (AT_comm_error OR AT_comm_busy)) ;3Fh AND [AT_status], (NOT \3 (AT_l_scroll OR AT_l_numlock OR AT_l_caps)) ;0F8hIENDIF ;IF (SMALLER)3 OR [AT_status], AH ;remember current shift lights L_097C:o POP CXt POP AX RET;h; Test for PrintScreen;XPrtScr: ;was at 097F' TEST [ShiftStatus], (LSHIFT OR RSHIFT)A JZ NoPrtScr ;go if no shift CMP AL, Scan_kp_pf4 JNZ NoPrtScr ;go if not PF4A;B PUSH AX PUSH CXIF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JNZ L_09A0rELSE CALL Check_ATbus5 JB L_09A0 CALL Check_AT_BIOS7 JB L_09A0ENDIF ;IF (ATBIOS_SAVE) IN AL, INTA016 AND AL, 0FDh ;reset bits 0/1 of interrupt controller WasteSomeTime OUT INTA01, ALw JMP L_09A5 L_09A0:l MOV AL, KB_enable CALL DoKBstatusL_09A5:E POP CXS POP AXs;C$ PUSH AX ;user pressed PrintScreen INT 05h ;get BIOS to do itZ POP AXr STC ;say we did it JMP L_09AF; NoPrtScr:p CLCL_09AF:V RET;r7; Check for Ctrl-Alt-Del (or Ctrl-Alt-KP7 -> cold boot)B;lCtrlAltDel: ;was at 09B0 TEST [ShiftStatus], CTRL JZ @@return TEST [ShiftStatus], ALT JZ @@return;A CMP AL, Scan_kp_periodA JZ @@reboot CMP AL, Scan_kp_7 JNZ @@return JMP CPUreset @@reboot:_ MOV AX, 1234h MOV [ResetWord], AX JMP IBMreset @@return: RETR_09D7:e TEST [ShiftExStat], ShExHoldo JZ L_09F7 TEST ES:[KBmagic], KBmagic_08hL JZ L_09EB CALL PrtScr JB L_0A41L_09EB:B CALL TranShift JB L_0A41" AND [ShiftExStat], (NOT ShExHold) JMP L_0A41;L_09F7:c TEST [ShiftStatus], CTRLS JZ L_0A44 CMP AL, Scan_kp_pf2 JNZ L_0A44r OR [ShiftExStat], ShExHoldo;r PUSH AXIF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JNZ L_0A1CrELSE CALL Check_ATbusp JB L_0A1C CALL Check_AT_BIOS JB L_0A1CENDIF ;IF (ATBIOS_SAVE)I IN AL, INTA016 AND AL, 0FDh ;reset bits 0/1 of interrupt controller WasteSomeTime OUT INTA01, ALr JMP L_0A21L_0A1C:e MOV AL, KB_enable CALL DoKBstatusL_0A21:i POP AXb;d CMP [DisplayMode], 07h JZ L_0A34;r PUSH AX PUSH DX MOV DX, 03D8h MOV AL, [Mode3x8] OUT DX, AL_ POP DXJ POP AX;LL_0A34:T TEST [ShiftExStat], ShExHoldA JNZ L_0A34$ AND ES:[KBmagic], (NOT KBmagic_08h)L_0A41:e STC IF (SMALLER) RETELSE JMP R_09D7_returnENDIF ;IF (SMALLER)L_0A44:_ CLCR_09D7_return: RET;2L; Check various shift status codes and select appropriate translation table.;cLookUpChar: ;was at 0A46 PUSH BX PUSH DI PUSH ES TEST [ShiftStatus], ALT JZ NoALT5 TEST [ShiftStatus], CTRLt JZ Alt_no_CtrlM JMP Ctrl_AltP Alt_no_Ctrl:' TEST [ShiftStatus], (LSHIFT OR RSHIFT)D JZ @@skip JMP Alt_Shift@@skip: JMP Alt_ONLYNoAlt: TEST [ShiftStatus], CTRLS& JZ no_Ctrl_or_Alt ;go if not Control' TEST [ShiftStatus], (LSHIFT OR RSHIFT)6 JZ @@skip JMP Ctrl_ShiftL@@skip:_ JMP L_0AF2 no_Ctrl_or_Alt:E' TEST [ShiftStatus], (LSHIFT OR RSHIFT)l JZ L_0AB8 ;go if not Shift TEST ES:[KBmagic], KBmagic_80H JNZ L_0AA7  TEST [ShiftStatus], CAPSn0 JZ L_0AA7 ;go if not Caps lock (negates shift) CMP AL, Scan_slasht JA L_0AA7 ;go if unshiftable;e9; Here, we have Shift + CAPS LOCK + a shiftable character;  PUSH ES LES BX, ES:[A_ShiftIsCap] XOR AH, AH, MOV DI, AXu DEC DIM TEST BYTE PTR ES:[BX+DI], 0FFhA POP ESP;C3 JNZ L_0AD2 ;Go if it's shift + caps lock + letters* JMP L_0B12 ;Go if it's another white key;:; Come here if Shift is pressed, but key is not shiftable.;GL_0AA7:A( CMP AL, Scan_kp_7 ;on numeric keypad ?" JB L_0B12 ;go if not (too small) CMP AL, Scan_kp_periodA JA L_0B12 ;go if not (too big) TEST [ShiftStatus], NUMLOCK+ JNZ L_0AD2 ;go if numeric and Num Lock one JMP L_0AC7.; 7; Come here if no form of shift or Caps Lock is active.t;OL_0AB8:r TEST [ShiftStatus], NUMLOCK JZ L_0ACB( CMP AL, Scan_kp_7 ;on numeric keypad ?" JB L_0ACB ;go if not (too small) CMP AL, Scan_kp_periodI JA L_0ACB ;go if not (too big);AN; Come here with a numeric keypad key (possibly shifted, makes no difference).;nL_0AC7:  SUB AL, (Scan_kp_7 - 1) JMP L_0B0BtL_0ACB:  TEST [ShiftStatus], CAPSr JNZ L_0B00 ;go if Caps Lock on; $; Jump or fall through to here with:0; - a numeric keypad key (not PF) and NumLock on6; - an unshifted (or shift + caps lock) alphabetic key;,L_0AD2:3 LES BX, ES:[A_UnshiftedKeys] ;Unshifted keys tableeIFE (NATIVE_MODE)A3 CMP AL, Scan_kp_5 ;dead if not NumLock or shiftedN JZ L_0B19ENDIF ;IFE (NATIVE_MODE) JMP L_0B27r Alt_ONLY:r' LES BX, ES:[A_AltKeys] ;Alt-keys table6 JMP L_0B27S Ctrl_Alt: ; LES BX, ES:[A_CtrlAltKeys] ;= Alt-keys table in US version JMP L_0B27c Alt_Shift:< LES BX, ES:[A_ShiftAltKeys] ;= Alt-keys table in US version JMP L_0B27tL_0AF2:t LES BX, ES:[A_CtrlKeys] JMP L_0B27n Ctrl_Shift:s LES BX, ES:[A_CtrlShiftKeys]_ JMP L_0B27 L_0B00:t LES BX, ES:[A_CapsLockKeys]IFE (NATIVE_MODE)63 CMP AL, Scan_kp_5 ;dead if not NumLock or shifted; JZ L_0B19ENDIF ;IFE (NATIVE_MODE) JMP L_0B27GL_0B0B: LES BX, ES:[A_KeypadKeys] JMP L_0B27E;A; Come here with a shifted character (not cancelled by Caps Lock)s;L_0B12:L) LES BX, ES:[A_ShiftKeys] ;Shifted table JMP L_0B27MIFE (NATIVE_MODE)l;D.; Come here with a potentially dead key (KP5).;L_0B19:r MOV DI, ESR POP ESh PUSH ES TEST ES:[KBmagic], NativeKeysOK JNZ L_0B4Aa JMP BadCodeENDIF ;IFE (NATIVE_MODE);S+; Merge here to look up key in known table.i;L_0B27:2 MOV DI, ESp POP ES  PUSH ES;tD; In permanent native mode, allow all keys here, without any checks.H; Otherwise, if not original DEC, always allow compose, even if we don'tG; think the LK250 is in native mode, since the user can switch to thatC+; mode at any time without us finding out.V;JIFE (NATIVE_MODE)T' CMP AL, Scan_compose ;is it compose ?0IF (DEC_original) % JNZ Not_compose ;go if not composeo: TEST ES:[LK250status], NativeBit ;are we in native mode ? JNZ L_0B4A ;go if so  JMP BadCode ;else invalid Not_compose:ELSE JZ L_0B4A ;go if composenENDIF ;IF (DEC_original) CMP AL, Scan_kp_periode& JBE L_0B4A ;go if an IBM-valid code TEST ES:[KBmagic], NativeKeysOK JNZ L_0B4ABadCode:1 MOV AX, 0FFFFh ;invalid scan code and characteri JMP L_0B57L_0B4A:rENDIF ;IFE (NATIVE_MODE) MOV ES, DId XOR AH, AHE DEC ALi SHL AX, 1 MOV DI, AXn7 MOV AX, ES:[BX+DI] ;get scan code and char from tablecL_0B57: IF (DEBUG) ;; Gasp ' (' ;; Gasp16 AX ;; Gasp ')'oENDIF ;IF (DEBUG)MIF (DEBUG AND GASP_SWITCH) CMP AL, 'z' JNE @@skipW NOT [Gasping]@@skip:V!ENDIF ;IF (DEBUG AND GASP_SWITCH)g POP ESo POP DI  POP BXI RET;IB; Call the post-processing routine when a key has been translated.;cPostCallCaller: ;was at 0B5B MOV BL, [ShiftStatus] CALL ES:[PostCall]O MOV [ShiftStatus], BL IF (DEBUG) ;; Gasp '*', ;; Gasp16 AX ;; Gasp '*'tENDIF ;IF (DEBUG)L RET IF (SYS_REQ);1; Come here when Alt-F20 pressed to call INT 15h.e;dAlt_F20_down: ;was at 0B69 OR [ShiftExStat], ShExInsert MOV AX, 8500H JMP L_0B7BgAlt_F20_up: ;was at 0B73$ AND [ShiftExStat], (NOT ShExInsert) MOV AX, 8501hL_0B7B:c MOV BX, AXtIF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JNZ L_0B91;ELSE CALL Check_ATbusZ JB L_0B91 CALL Check_AT_BIOSB JB L_0B91ENDIF ;IF (ATBIOS_SAVE)T IN AL, INTA016 AND AL, 0FDh ;reset bits 0/1 of interrupt controller WasteSomeTime OUT INTA01, ALi IF (SMALLER) RETELSE JMP Alt_F20_retENDIF ;IF (SMALLER)iL_0B91:Z MOV AL, KB_enable CALL DoKBstatus$ MOV AX, BX ;SysReq up or down code INT 15h Alt_F20_ret: RETENDIF ;IF (SYS_REQ)i;B; Routine for INT 16h AH=0D2h.0; AL=0 -> replace original DOS typeahead buffer.; Otherwise return buffer info:A#; DX is segment address of buffer.C6; BX is segment address of head/tail/start of buffer.; CX is length of buffer.;Int16h_0D2: ;was at 0B9B TEST AL, 0FFh JNZ L_0BA4C; ; Come here if AL=0.;P) CALL InitBuf ;reset buffer and pointers  JMP INT16h_0D2_ret0L_0BA4:L PUSH AX MOV AX, DXk MOV ES:[KBbufseg], AX MOV AX, BX  MOV [KBhead], AX  MOV [KBtail], AXn MOV [KBstart], AX MOV ES:[KBbufsize], CXs ADD AX, CX  MOV [KBend], AX POP AX INT16h_0D2_ret:n CLC RET; ; Routine for INT 16h AH=0D3h.*; AL=0 -> reset keyboard to initial state.-; AL between 1 and 3Fh -> set keyboard flags.); AL=80H -> return keyboard status flags.a; Int16h_0D3: ;was at 0BC3 OR AL, AL JNZ L_0BDCo;a-; Come here if AL=0 - reset to initial state.nJ; In Digital's version, this also puts the LK250 into IBM-compatible mode.;R, AND ES:[KBmagic], (PreCallOK OR PostCallOK)IF (DEC_original)m& AND ES:[LK250status], (NOT NativeBit)ENDIF ;IF (DEC_original) PUSH AXIF (NATIVE_MODE) MOV AL, LK250_nativeeELSE MOV AL, LK250_IBMENDIF ;IF (NATIVE_MODE)( CALL Int16h_0D5 POP AXm JMP Int16h_0D3_returnL_0BDC: TEST AL, 80HD JZ L_0C01;u7; Come here if AL high bit is set; return status in AL.F!; AL bits 0-3 = KBmagic bits 4-7.s; AL bit 4 = KBmagic bit 3.iN; AL bit 5 = LK250 native mode bit. This is just what KEYBRD thinks, and does6; not necessarily reflect the actual keyboard status.;_ PUSH CX MOV AL, ES:[KBmagic]N MOV CX, 0004h SHR AL, CLT POP CXB;CIFE (NATIVE_MODE) TEST ES:[KBmagic], NativeKeysOK JZ L_0BF5 OR AL, 10HiL_0BF5:pENDIF ;IFE (NATIVE_MODE)IF (DEC_original)p! TEST ES:[LK250status], NativeBito JZ Int16h_0D3_return ENDIF ;IF (DEC_original)$IF ((NATIVE_MODE) OR (DEC_original))- OR AL, 20H ;we think we're in native mode.,(ENDIF ;((NATIVE_MODE) OR (DEC_original)) JMP Int16h_0D3_return;T=; Come here if AL high bit is clear; set KB magic byte to AL.J;CL_0C01:o PUSH CXIF (DEC_original)e TEST AL, 20H JZ NotNativel;]O; Come here if AL bit 5 is set; remember we are putting LK250 into native mode.,;e OR ES:[LK250status], NativeBito NotNative:ENDIF ;IF (DEC_original) MOV CX, 0004h AND AL, 1Fh SHL AL, CL POP CXBIFE (NATIVE_MODE)r* JNB L_0C18 ;go if input bit 4 was clear/ OR AL, NativeKeysOK ;set bit to remember bit 4 L_0C18:lENDIF ;IFE (NATIVE_MODE)& TEST AL, 10H ;go if AL bit 0 was set JZ L_0C26 AND [ShiftStatus], ALL_SHIFTS AND [ShiftExStat], 4FhtL_0C26:r OR ES:[KBmagic], AL;u3; Next line is silly - AL bit 2 must be clear here.EF; What we might want to test for is whether AL bit 5 was set on entry.C; In any case, if we are forcing native mode, we should do it here._;EIF (DEC_ORIGINAL)1 TEST AL, 04h  JZ L_0C36 ;skip if notpENDIF ;IF (DEC_original)$IF ((DEC_ORIGINAL) OR (NATIVE_MODE))1 MOV AL, LK250_native ;set LK250 into native modes CALL Int16h_0D5+ENDIF ;IF ((DEC_ORIGINAL) OR (NATIVE_MODE)) JMP L_0C38OL_0C36:t XOR AL, ALKL_0C38:r CALL SetLightsaInt16h_0D3_return: CLC RET;; Routine for INT 16h AH=0D4hP;DInt16h_0D4: ;was at 0C3DIF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JNZ L_0C4CaELSE CALL Check_ATbusr JB L_0C4C CALL Check_AT_BIOS0 JB L_0C4CENDIF ;IF (ATBIOS_SAVE)f( MOV AL, 02h ;no extended BIOS, so exit JMP L_0CE0rL_0C4C: PUSH CX CLI MOV CX, 2800HL_0C51:  TEST [AT_status], AT_comm_busyT JZ L_0C5F LOOP L_0C51 STI MOV AL, 01h JMP L_0CC8sL_0C5F:g OR [AT_status], AT_comm_busy@ STI MOV AL, KB_disable CALL DoKBstatus XOR CX, CX L_0C6C: IN AL, KCstatus) TEST AL, (KC_OutputFull OR KC_InputFull)i LOOPNZ L_0C6CIF (ATBIOS_SAVE) CMP ES:[ATbus_seen], 0C JNZ L_0C7FpELSE CALL Check_ATbusa JB L_0C7FENDIF ;IF (ATBIOS_SAVE)t IN AL, INTA01/ OR AL, 02h ;set bit 1 of interrupt controllerS WasteSomeTime OUT INTA01, AL L_0C7F:y MOV AL, KB_enable CALL DoKBstatus CALL ClearKBbuffers MOV AL, KB_rd_command CALL DoKBstatus CALL R_0D0F JB L_0C95 MOV AH, 01h JMP L_0CC3AL_0C95:T MOV DL, ALm AND AL, 0BFh  CALL DoKBcommandS MOV DH, 03hL_0C9E:V CALL ClearKBbuffers MOV AL, KB_test OUT KCdata, ALh CALL R_0D0F IF (SMALLER) JNC L_0CB7AELSE JB L_0CAC JMP L_0CB7uENDIF ;IF (SMALLER)aL_0CAC:; CMP AL, AT_resend JZ L_0CB7 MOV BL, ALe CALL R_0D0F JB L_0CBFL_0CB7: DEC DH JNZ L_0C9ET MOV AL, 02h JMP L_0CC36L_0CBF:; MOV BH, AL: XOR AL, AL L_0CC3:P< AND [AT_status], (NOT (AT_comm_error OR AT_comm_busy)) ;3FhL_0CC8:n POP CX  PUSH AX MOV AL, DLc CALL DoKBcommandtIF (ATBIOS_SAVE) CMP ES:[ATbus_seen], 03 JNZ L_0CDCPELSE CALL Check_ATbus, JB L_0CDCENDIF ;IF (ATBIOS_SAVE)  IN AL, INTA016 AND AL, 0FDh ;reset bits 0/1 of interrupt controller WasteSomeTime OUT INTA01, ALKL_0CDC:a CALL ClearKBbuffers POP AXeL_0CE0:7 CLC RET;e&; Come here to clear keyboard buffers.;ClearKBbuffers: ;was at 0CE2 PUSH CX XOR CX, CXlL_0CE5:L" IN AL, KCstatus ;get status byte MOV AH, ALf0 TEST AH, KC_OutputFull ;char in output buffer ? JZ L_0CF2 ;go if not  WasteSomeTime IN AL, KCdata ;read it if soL_0CF2:o. TEST AH, KC_InputFull ;char in input buffer ? JZ L_0CF9 ;return if notD* LOOP L_0CE5 ;otherwise loop (64K times)L_0CF9:J POP CX_ RET;_ ; Send keyboard command (in AL).;DoKBcommand: ;was at 0CFBA PUSH AX MOV AL, KB_wr_command CALL DoKBstatus;+; Wait for keyboard to acknowledge command.o;h XOR CX, CX@@loop:E IN AL, KCstatus TEST AL, KC_InputFull LOOPNZ @@loop POP AXP WasteSomeTime OUT KCdata, ALe RETR_0D0F: XOR CX, CX[L_0D11:  IN AL, KCstatus TEST AL, KC_OutputFullC JNZ L_0D1C0 LOOP L_0D11 CLC IF (SMALLER) RETELSE JMP DoKBcommand_retENDIF ;IF (SMALLER)SL_0D1C:  IN AL, KCdata STCDoKBcommand_ret: RET;J; Routine for INT 16h AH=0D5h.+; Also called from INT 16h AH=0D3h routine.; AL=LK250 command byte.;:Int16h_0D5: ;was at 0D20IF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JNZ L_0D2ECELSE CALL Check_ATbus JB L_0D2E CALL Check_AT_BIOSf JB L_0D2EENDIF ;IF (ATBIOS_SAVE)V( MOV AL, 02h ;no extended BIOS, so exit JMP L_0D626L_0D2E: PUSH CX CLI;(J; Since the next section runs with interrupts disabled, it will just wasteI; time (about 250000 clocks, or 30msec on an 8MHz 80286) if the keyboards; busy flag is set.;s MOV CX, 2800HL_0D33:L TEST [AT_status], AT_comm_busy JZ L_0D41 LOOP L_0D33 STI MOV AL, 01h JMP L_0D61SL_0D41: OR [AT_status], AT_comm_busy  STI CALL AT_command TEST [AT_status], AT_comm_error JZ L_0D5A MOV AL, AT_enable CALL AT_command MOV AL, 02h JMP L_0D5CBL_0D5A:B XOR AL, ALBL_0D5C:< AND [AT_status], (NOT (AT_comm_error OR AT_comm_busy)) ;3FhL_0D61:n POP CX_L_0D62:V CLC RET ASSUME DS:WorkSeg;J; Routine for INT 16h AH=0D6h.H; If CL=0FFh and AL is non-zero, reset translation tables (Ctrl-Alt-F2).N; If CL is between 0 and 9, then ES:BX is address of a translation table, and:B; - if AL is zero, return current table address in DWORD at ES:BX.C; - if AL is non-zero, set current table address to DWORD at ES:BX.R;Int16h_0D6: ;was at 0D64 PUSH AX PUSH DI MOV DS, CS:[WorkPtr]p MOV ES, DXC CMP CL, 0FFh1 JZ L_0D96 CMP CL, 09h JA Int16h_0D6_ret;r%; Come here if CL is in range 0 to 9. ;t MOV DI, CXk AND DI, 00FFh SHL DI, 1 SHL DI, 1 OR AL, AL JZ L_0D9F;X5; Come here if AL > 0 to set translate table address.7;  MOV AX, ES:[BX]" MOV WORD PTR [DI+TransTables], AX MOV AX, ES:[BX+02]S$ MOV WORD PTR [DI+TransTables+2], AX JMP Int16h_0D6_retAL_0D96:;;t-; Come here if CL=0FFh; only valid if AL > 0.u;c OR AL, AL JZ Int16h_0D6_ret CALL SetUpTable JMP Int16h_0D6_retML_0D9F:_;_6; Come here if AL=0 to return translate table address.;A" MOV AX, WORD PTR [DI+TransTables] MOV ES:[BX], AX$ MOV AX, WORD PTR [DI+TransTables+2] MOV ES:[BX+02], AXAInt16h_0D6_ret:l CLC POP DIL POP AXa RET;h;; Set up scan code / char lookup table to default mappings.L; DS points to our work area.B; SetUpTable: ;was at 0DB2 MOV DI, OFFSET TransTablesT MOV AX, CS ;l8 MOV WORD PTR [DI+O_UnshiftedKeys], OFFSET UnshiftedKeys( MOV WORD PTR [DI+O_UnshiftedKeys+2], AX;u. MOV WORD PTR [DI+O_CtrlKeys], OFFSET CtrlKeys# MOV WORD PTR [DI+O_CtrlKeys+2], AX;, MOV WORD PTR [DI+O_AltKeys], OFFSET AltKeys" MOV WORD PTR [DI+O_AltKeys+2], AX;R0 MOV WORD PTR [DI+O_ShiftKeys], OFFSET ShiftKeys$ MOV WORD PTR [DI+O_ShiftKeys+2], AX;O2 MOV WORD PTR [DI+O_KeypadKeys], OFFSET KeypadKeys% MOV WORD PTR [DI+O_KeypadKeys+2], AX ;A6 MOV WORD PTR [DI+O_CapsLockKeys], OFFSET CapsLockKeys' MOV WORD PTR [DI+O_CapsLockKeys+2], AX ;c4 MOV WORD PTR [DI+O_CtrlAltKeys], OFFSET CtrlAltKeys& MOV WORD PTR [DI+O_CtrlAltKeys+2], AX;16 MOV WORD PTR [DI+O_ShiftAltKeys], OFFSET ShiftAltKeys' MOV WORD PTR [DI+O_ShiftAltKeys+2], AXL;A8 MOV WORD PTR [DI+O_CtrlShiftKeys], OFFSET CtrlShiftKeys( MOV WORD PTR [DI+O_CtrlShiftKeys+2], AX;p2 MOV WORD PTR [DI+O_ShiftIsCap], OFFSET ShiftIsCap% MOV WORD PTR [DI+O_ShiftIsCap+2], AXL RET ASSUME DS:BiosData9;V7; Wait for input buffer empty; then write byte in AL to>#; keyboard controller status port.a;9DoKBstatus: ;was at 0E07 MOV AH, ALu CLI XOR CX, CXt@@wait: IN AL, KCstatus TEST AL, KC_InputFull. LOOPNZ @@wait ;can take up to 1 megacycle... MOV AL, AHe WasteSomeTime OUT KCstatus, ALM STI RET;X1; Send the AT command byte in AL to the keyboard.S+; Handle resends, if requested by keyboard. ;[AT_command: ;was at 0E1AIF (ATBIOS_SAVE) CMP ES:[AT_any_seen], 0 JZ L_0E64ELSE CALL Check_ATbusf JB L_0E24 CALL Check_AT_BIOS0 JNB L_0E64cL_0E24:SENDIF ;IF (ATBIOS_SAVE)_ PUSH AX PUSH BX PUSH CX;0&; Waste about 10msec on an 8MHz 80286.;S MOV CX, 2800H LOOP $ MOV BX, 0003hL_0E2F: MOV AH, AL CLI;LL; Wait for up to a million clocks (with interrupt disabled) for the KB inputL; buffer to empty; on an XT this is nearly two million clocks or 400 msec !;r XOR CX, CXiL_0E34:I IN AL, KCstatus TEST AL, KC_InputFull LOOPNZ L_0E34? AND [AT_status], (NOT (AT_comm_NACKed OR AT_comm_ACKed)) ;0CFh MOV AL, AHr WasteSomeTime OUT KCdata, ALD STI MOV CX, 2800HL_0E49:, TEST [AT_status], AT_comm_ACKed JNZ L_0E61S! TEST [AT_status], AT_comm_NACKed JNZ L_0E59m LOOP L_0E49L_0E59:) DEC BXe JNZ L_0E2F( OR [AT_status], AT_comm_errorL_0E61: POP CX  POP BX POP AX4L_0E64: RETModKeys: ;was at 0E65c;a+; Lookup table for modifier key scan codes.t;r DB Scan_rshift DB Scan_lshift DB Scan_ctrlP DB Scan_alt DB Scan_kp_pf3T DB Scan_kp_pf2, DB Scan_capslockT DB Hack_insModFlags: ;was at 0E6D;J3; Mapping of modifier keys into shift status flags.I;O DB RSHIFT DB LSHIFT DB CTRL DB ALTk DB SCROLL DB NUMLOCKl DB CAPS DB INSERTERRIF (($ - ModFlags) NE 08h)gIFE (NATIVE_MODE)SGreyKeyTable: ;was at 0E75;T'; Lookup table for dark grey key codes._B; The value in the table is subtracted from the physical scan code$; to map the key to its equivalent.;S DB (Scan_home - Hack_home) DB (Scan_ins - Hack_ins)t DB (Scan_del - Hack_del)g DB (Scan_select - Hack_select))# DB (Scan_prevpage - Hack_prevpage)g# DB (Scan_nextpage - Hack_nextpage) ! DB (Scan_uparrow - Hack_uparrow)r% DB (Scan_leftarrow - Hack_leftarrow)i' DB (Scan_rightarrow - Hack_rightarrow)C% DB (Scan_downarrow - Hack_downarrow)!ERRIF (($ - GreyKeyTable) NE 0Ah)tENDIF ;IFE (NATIVE_MODE);D; Mapping for Alt-keys and (US version) Ctrl-Alt and Shift-Alt keys.;r ;was at 0E7FhiAltKeys EQU $ M_AltKeys;A<; Mapping for control keys and (US version) Ctrl-Shift keys.;m ;was at 0F51hSCtrlKeys EQU $ M_CtrlKeys;tI; Mapping for unshifted characters (or numeric keypad keys via Num Lock).u;o ;was at 1023h0UnshiftedKeys EQU $f M_UnshiftedKeysr; "; Mapping for numeric keypad keys.;S ;was at 10F5hMKeypadKeys EQU $ M_KeypadKeys; ; Mapping for shifted keys. ; ;was at 110FhlShiftKeys EQU $p M_ShiftKeys_; (; Mapping for characters with Caps Lock.;n ;was at 11E1heCapsLockKeys EQU $ M_CapsLockKeys;n"; Mapping for Ctrl-Alt characters.;JIF (UNITED_STATES)CtrlAltKeys EQU AltKeysPENDIF ;IF (UNITED_STATES)s IF (FRENCH) CtrlAltKeys EQU $h M_CtrlAltKeys ENDIF ;IF (FRENCH);o#; Mapping for Shift-Alt characters.d;rShiftAltKeys EQU AltKeys;k$; Mapping for Ctrl-Shift characters.;dCtrlShiftKeys EQU CtrlKeys;F>; Table of keys where shift converts to capital letters (0FFh).; or converts to a totally different key (0).;P ;was at 12B3hNShiftIsCap EQU $ M_ShiftIsCap;aE; Table to convert numeric keypad codes (starting at KP7) to decimal. ;l ;was at 12E8h KPnumTable DB 007h ; Scan_kp_7 DB 008h ; Scan_kp_8l DB 009h ; Scan_kp_9n DB 0 ; Scan_kp_minus DB 004h ; Scan_kp_4 DB 005h ; Scan_kp_5s DB 006h ; Scan_kp_6t DB 0 ; Scan_kp_comma DB 001h ; Scan_kp_1L DB 002h ; Scan_kp_2O DB 003h ; Scan_kp_3  DB 0 ; Scan_kp_0ERRIF (($ - KPnumTable) NE 0Ch)E;I; Get BIOS data area to DS.B;GetBDA: ;was at 12F4y PUSH AX MOV AX, 0040H MOV DS, AXa POP AXc RETIFE (ATBIOS_SAVE)C Extended_BIOSENDIF ;IFE (ATBIOS_SAVE);eK; The next routine obviously got conditionally assembled down to nothing...h;p IFE (SMALLER) R_133F:. RETENDIF ;IFE (SMALLER);P!; Initialise buffer and pointers. ;iInitBuf: ;was at 1340J PUSH AX;N- MOV ES:[KBbufsize], 0020H ;BIOS default sizeo MOV AX, DSk MOV ES:[KBbufseg], AX MOV AX, 001Eh MOV [KBhead], AX ;KB head  MOV [KBtail], AX ;KB tailo MOV [KBstart], AX ;KB starte ADD AX, 0020H MOV [KBend], AX ;KB enda;a POP AXo RETIFE (NATIVE_MODE)i;tG; Hack the dark grey keys and KP enter; these have different scan codest3; in LK250 native mode and IBM compatibility mode.OG; Called from INT 09h ISR; toggles BP to indicate that we should ignoreZD; any NumLock currently in progress. (Toggles BP, but should maybe4; explicitly set BP; currently chasing a bug here.);BHackGrey: ;was at 1362 PUSH BX;L TEST ES:[KBmagic], NativeKeysOK JNZ HackGrey_reti CMP AL, Scan_kp_enter JNZ NotEnterL MOV AL, Hack_kp_enter JMP HackGrey_ret[ NotEnter:a2 CMP AL, Scan_home ;between Home and Down arrow ? JB HackGrey_ret CMP AL, Scan_downarrow0 JA HackGrey_ret ;return if not XOR BX, BXV MOV BL, ALR% SUB BL, Scan_home ;get in range 0-9,& SUB AL, BYTE PTR CS:[BX+GreyKeyTable] TEST [ShiftStatus], NUMLOCK JZ HackGrey_ret! AND [ShiftStatus], (NOT NUMLOCK) $ NOT BP ;remember we came this way HackGrey_ret:) POP BX, RETENDIF ;IFE (NATIVE_MODE)R_1397:V) CALL InitBuf ;reset buffer and pointersP PUSH DS;  MOV DX, WORD PTR CS:[VEC09] MOV AX, WORD PTR CS:[VEC09+2] MOV DS, AXe# MOV AH, 25h ;reset INT 09h vector MOV AL, 09h INT 21h;f MOV DX, WORD PTR CS:[VEC16] MOV AX, WORD PTR CS:[VEC16+2] MOV DS, AXs# MOV AH, 25h ;reset INT 16h vectorN MOV AL, 16h INT 21h; POP DSE XOR AL, ALe RET;A; Saved interrupt vectors.;eVEC09 DD 0 ;was at 13C1VEC16 DD 0 ;was at 13C5;E; Address of our PSP.H;JPSP DW 0 ;was at 13C9;s); Address of byte containing our /X flag.r;SlashXFlag DB 0 ;was at 13CB;AK; Address of work area, in this program or elsewhere if another Digital TSRh#; is already running when we start.T;OWorkPtr DW 0 ;was at 13CC;_N; WorkArea is accessed via a segment address, so it must be paragraph-aligned.F; The way Digital did this wasted a paragraph as well for some reason.;0 IFE (SMALLER)  DB 10H DUP (0) ;was at 13CE,ENDIF ;IFE (SMALLER)+ DB 10H - ((($ - SegTop) AND 0Fh)) DUP (0)I IF (SMALLER)WorkArea DB WorkSize DUP (0) IF (DEBUG)&WorkDummy DB (400H - WorkSize) DUP (0)WorkDummySize EQU $ - WorkDummyeENDIF ;IF (DEBUG)uELSE%WorkArea DB 400H DUP (0) ;was at 13E0uENDIF ;IF (SMALLER)eIF (DEBUG AND GASP_SWITCH)(Gasping DB 0FFh ;display GASP messages!ENDIF ;IF (DEBUG AND GASP_SWITCH) ;F; Length of text segment.e;r"TextLen EQU $ - SegTop ;was 17E0h;fK; Don't know what the next byte does - it was lying around in the original.V1; Maybe I could save 1 byte and comment it out... ;M% DB 0FFh ;don't know what this doesM;E; End of TSR resident code.C;;[I; To save space, assemble code that doesn't need to live in the TSR here. 0; Good job TASM can handle pretty large macros !;tIFE (DEC_original) NonResCode ENDIF ;IFE (DEC_original)-IF (ATBIOS_SAVE) Extended_BIOSENDIF ;IF (ATBIOS_SAVE)B _TEXT ENDS;ZI; Description of the segment implemented by WorkArea, or by an equivalents+; sized buffer in another Digital program.oG; If we find that another program provides this area, we could probablyE); leave WorkArea out of the TSR as well.a;e8WorkSeg SEGMENT AT 0 ;addressed via ES most of the timeWorkStart EQU $2 ORG 0015hL,BDAseg DW ? ;BIOS data area segment addressIF (DEC_original)5 ORG 0019hJ;ID; Note that trying to track the mode of the LK250 (native or IBM) isH; fairly meaningless, since the user may type Alt-F17 to toggle modes, H; and programs such as SETHOST tend to change the mode without updating ; this flag. ;t7LK250status DB ? ;bit 0 set if LK250 is in native mode.aNativeBit EQU 01huENDIF ;IF (DEC_original);K; Translation table addresses.;C ORG 001FhTransTables EQU $E#O_UnshiftedKeys EQU $ - TransTablesA_UnshiftedKeys DD ?O_CtrlKeys EQU $ - TransTablesA_CtrlKeys DD ?_O_AltKeys EQU $ - TransTablesuA_AltKeys DD ?O_ShiftKeys EQU $ - TransTables A_ShiftKeys DD ? O_KeypadKeys EQU $ - TransTablesA_KeypadKeys DD ?w"O_CapsLockKeys EQU $ - TransTablesA_CapsLockKeys DD ?(!O_CtrlAltKeys EQU $ - TransTableseA_CtrlAltKeys DD ?"O_ShiftAltKeys EQU $ - TransTablesA_ShiftAltKeys DD ?#O_CtrlShiftKeys EQU $ - TransTableslA_CtrlShiftKeys DD ? O_ShiftIsCap EQU $ - TransTablesA_ShiftIsCap DD ?e;r-KBmagic DB ? ;LK250 hack flags; was at 0047hO8PostCall DD ? ;Called when char translated; was at 0048h9KBbufseg DW ? ;Segment address of KB buffer; was at 004ChP6PreCall DD ? ;Called when scancode read; was at 004Eh,KBbufsize DW ? ;KB buffer size; was at 0052h;oIF (ATBIOS_SAVE).ATbus_seen DB 0 ;1 if Check_ATbus was positive2AT_BIOS_seen DB 0 ;1 if Check_AT_BIOS was positive#AT_any_seen DB 0 ;OR of above flagsaENDIF ;IF (ATBIOS_SAVE)S;BIF (WIDE_SCREEN),SavedVideo DB VideoCO80X25 ;Saved video mode;EG; List of DECstation 316 video modes expressed as qualifiers to SETTEXT ; (VideoXXX means SETTEXT/XXX).;vVideoCO80X25 EQU 3VideoCO132X43 EQU 34VideoCO132X25 EQU 35VideoCO132X28 EQU 36VideoCO132X50 EQU 39VideoCO132X60 EQU 40VideoCO80X24 EQU 56NVideoCO132X27 EQU 57VideoCO80X32 EQU 582VideoCO80X48 EQU 59VideoV EQU 59VideoBW80X25 EQU 7VideoBW132X43 EQU 24VideoBW132X25 EQU 25VideoBW132X28 EQU 26VideoBW132X50 EQU 27VideoBW132X60 EQU 28VideoBW80X24 EQU 48VideoBW132X27 EQU 49VideoBW80X32 EQU 50hVideoBW80X48 EQU 51ENDIF ;IF (WIDE_SCREEN)E;AWorkSize EQU $ - WorkStart;L WorkSeg ENDS_;u; Flags in KBmagic byte.;T)PreCallOK EQU 01h ;PreCall can be calledf+PostCallOK EQU 02h ;PostCall can be calledIFE (NATIVE_MODE);PK; If we are not in native mode full-time, see if we allow native-mode keys. ;C2NativeKeysOK EQU 04h ;Native-mode keys recognisedENDIF ;IFE (NATIVE_MODE)KBmagic_08h EQU 08hMKBmagic_10H EQU 10hCKBmagic_20H EQU 20hCKBmagic_40H EQU 40h KBmagic_80H EQU 80hL;K; That's all folks !;u END Start)*[PCSA.APPLICATION.KEYBRD.KIT]KEYBRD.EXE;1+,%2. / 4 -:0123 KPWO 56j 7I 89GHJMZ ->0jr)t~ LK250.BCK%2:)[PCSA.APPLICATION.KEYBRD.KIT]KEYBRD.EXE;1 QRU.HuFu&6L&_W>&6L&;>r>>_SQWV% u+پ- & ^_Y[á;s&R+&;RtHHvu&>Vtatur3ø&>Vt3<u/ s*s%Q Y7u +tt4Vu !$!33XPSQRVWU3.H&>Vu ! !&>Uu .H3&>Vtd$`&>VuPa aaX'P XOrVu !$! t ]_^ZY[XPQ2$t'YXPSt-<u&&G2&6L&;>r ;t>&>Vt_XPQ@u/@$UPtD&8&YXt&<7u"PQ&>Vu !$!YXPXttVu !$!X>It PReZXu&&GSWtttyt tzt8&Gu@t<5w&C2O&u-kJ&'C&7<&;5&#.&?'&3VuQ(@tj@3d&>Tu! !\ yr,$`B`hs Tu!$!XQ3dt`tYP`3dX`3du`&>Vu4Q(@t @t 2&?YPW.H€t$ w7 t&&G! t&!&G_Xÿ EE EE? E E EE EE EE? EE? EE  E"E$sE&Ê3dd&>Vt@PSQ(3d&ϊ`(u uKuӀY[X6*8FE:R @  xyz{|}~ !"#$%&,-./012 9hijklmnopq    !"# $ % &+,-./01 2r 9^_`abcdefgwstuv 12345678 9 0 - =  qwertyuiop[] asd f!g"h#j$k%l&;''(`)\+z,x-c.v/b0n1m2,3.4/5*7 9;<=>?@ABCDEFGHI-JKLM+NOPQRS 7G8H9I-J4K5L6M+N1O2P3Q0R.S!@#$%^&* ( ) _ + QWERTYUIOP{} ASD F!G"H#J$K%L&:'"(~)|+Z,X-C.V/B0N1M2<3>4?5*7 9TUVWXYZ[\]EF7G8H9I-J4K5L6M+N1O2P3Q0R.S12345678 9 0 - =  QWERTYUIOP[] ASD F!G"H#J$K%L&;''(`)\+Z,X-C.V/B0N1M2,3.4/5*7 9;<=>?@ABCDEFGHI-JKLM+NOPQRS  P@XP&R &L XS&Gu*Vtdt Q(Ys3&Grq% !%!&>Tu&>Ut !$!>!K}.E&,I!1! ! KEYBRD.EXE V4.02 (C)Copyright 1987-1991 by Digital Equipment Corporation Modified version 0.43 by Nick Brown, November 1991 $KEYBRD successfully loaded. $KEYBRD already loaded. $KEYBRD cannot be installed on a VAXmate. $Incorrect DOS version. $Invalid parameter. $0201KBDV޾', 029H ; Scan_backquoteENDIF ;IF (FRENCH)IF (UNITED_STATES) DB ':', 027H ; Scan_semicolon DB '"', 028H ; Scan_quote DB '~', 029H ; Scan_backquoteENDIF ;IF (UNITED_STATES)0 DW 0FFFFH ; Scan_lshift IF (FRENCH)! DB 09CH, 02BH ; Scan_backslash  DB 'W', 02CH ; Scan_zENDIF ;IF (FRENCH)IF (UNITED_STATES) DB '|', 02BH ; Scan_backslash DB 'Z', 02CH ; Scan_zENDIF ;IF (UNITED_STATES)c DB 'X', 02DH ; Scan_x DB 'C', 02EH ; Scan_c DB 'V', 02FH ; Scan_v DB 'B', 030H ; Scan_b DB 'N', 031H ; Scan_n IF (FRENCH)  DB '?', 032H ; Scan_m DB '.', 033H ; Scan_comma DB '/', 034H ; Scan_periodR DB '+', 035H ; Scan_slashENDIF ;IF (FRENCH)IF (UNITED_STATES) DB 'M', 032H ; Scan_m DB '<', 033H ; Scan_comma DB '>', 034H ; Scan_period  DB '?', 035H ; Scan_slashENDIF ;IF (UNITED_STATES)  DW 0FFFFH ; Scan_rshift  DB '*', 037H ; Scan_kp_pf4 DW 0FFFFH ; Scan_alt DB ' ', 039H ; Scan_space DW 0FFFFH ; Scan_capslock DB 0, 054H ; Scan_f1 DB 0, 055H ; Scan_f2x DB 0, 056H ; Scan_f3 DB 0, 057H ; Scan_f4  DB 0, 058H ; Scan_f5B DB 0, 059H ; Scan_f6( DB 0, 05AH ; Scan_f7  DB 0, 05BH ; Scan_f8) DB 0, 05CH ; Scan_f9B DB 0, 05DH ; Scan_f10 DB 0, 045H ; Scan_kp_pf20 DB 0, 046H ; Scan_kp_pf3F DB '7', 047H ; Scan_kp_7F DB '8', 048H ; Scan_kp_8F DB '9', 049H ; Scan_kp_9F DB '-', 04AH ; Scan_kp_minus  DB '4', 04BH ; Scan_kp_4  DB '5', 04CH ; Scan_kp_5c DB '6', 04DH ; Scan_kp_6  DB '+', 04EH ; Scan_kp_commac DB '1', 04FH ; Scan_kp_1  DB '2', 050H ; Scan_kp_2n DB '3', 051H ; Scan_kp_3 DB '0', 052H ; Scan_kp_0 DB '.', 053H ; Scan_kp_period DB 0, 0A4H ; Scan_f20 DB 0, 085H ; Scan_homeH DB 0, 086H ; Scan_ins DB 0, 087H ; Scan_del DB 0, 088H ; Scan_selectF DB 0, 089H ; Scan_prevpageC DB 0, 08AH ; Scan_nextpagek DB 0, 08BH ; Scan_uparrow DB 0, 08CH ; Scan_leftarrow DB 0, 08DH ; Scan_rightarrow DB 0, 08EH ; Scan_downarrow DB 0, 09BH ; Scan_f11 DB 0, 09CH ; Scan_f12 DB 0, 09DH ; Scan_f13 DB 0, 09EH ; Scan_f14 DB 0, 09FH ; Scan_f15 DB 0, 0A0H ; Scan_f16 DB 0, 0A1H ; Scan_f17 DB 0, 0A2H ; Scan_f18 DB 0, 0A3H ; Scan_f19# DW Mapped_compose ; Scan_composen DB 0, 0A6H ; Scan_kp_enternERRIF (($ - TOP) NE 0D2h) ENDM;H(; Mapping for characters with Caps Lock.;_M_CapsLockKeys MACRO LOCAL TOPW TOP EQU $ DB 01BH, 001H ; Scan_kp_pf1 DB '1', 002H ; Scan_1 DB '2', 003H ; Scan_2 DB '3', 004H ; Scan_3 DB '4', 005H ; Scan_4 DB '5', 006H ; Scan_5 DB '6', 007H ; Scan_6 DB '7', 008H ; Scan_7 DB '8', 009H ; Scan_8 DB '9', 00AH ; Scan_9 DB '0', 00BH ; Scan_0 IF (FRENCH)  DB 0F8H, 00CH ; Scan_minusc DB '_', 00DH ; Scan_equalscENDIF ;IF (FRENCH)IF (UNITED_STATES) DB '-', 00CH ; Scan_minus DB '=', 00DH ; Scan_equalscENDIF ;IF (UNITED_STATES) ! DW Mapped_rubout ; Scan_ruboutc DB 009H, 00FH ; Scan_tabN IF (FRENCH)B DB 'A', 010H ; Scan_q DB 'Z', 011H ; Scan_wENDIF ;IF (FRENCH)IF (UNITED_STATES) DB 'Q', 010H ; Scan_q DB 'W', 011H ; Scan_wENDIF ;IF (UNITED_STATES)n DB 'E', 012H ; Scan_e DB 'R', 013H ; Scan_r DB 'T', 014H ; Scan_t DB 'Y', 015H ; Scan_y DB 'U', 016H ; Scan_u DB 'I', 017H ; Scan_i DB 'O', 018H ; Scan_o DB 'P', 019H ; Scan_p IF (FRENCH)k- DB '^', Flag_deadkey ; Scan_lbracket (dead)E DB '$', 01BH ; Scan_rbracketpENDIF ;IF (FRENCH)IF (UNITED_STATES) DB '[', 01AH ; Scan_lbracketB DB ']', 01BH ; Scan_rbracket,ENDIF ;IF (UNITED_STATES), DB 00DH, 01CH ; Scan_return DW 0FFFFH ; Scan_ctrlW IF (FRENCH)n DB 'Q', 01EH ; Scan_aENDIF ;IF (FRENCH)IF (UNITED_STATES) DB 'A', 01EH ; Scan_aENDIF ;IF (UNITED_STATES)N DB 'S', 01FH ; Scan_s DB 'D', 020H ; Scan_d DB 'F', 021H ; Scan_f DB 'G', 022H ; Scan_g DB 'H', 023H ; Scan_h DB 'J', 024H ; Scan_j DB 'K', 025H ; Scan_k DB 'L', 026H ; Scan_l IF (FRENCH)0 DB 'M', 027H ; Scan_semicolon DB 097H, 028H ; Scan_quote DB '<', 029H ; Scan_backquoteENDIF ;IF (FRENCH)IF (UNITED_STATES) DB ';', 027H ; Scan_semicolon DB "'", 028H ; Scan_quote DB '`', 029H ; Scan_backquoteENDIF ;IF (UNITED_STATES)0 DW 0FFFFH ; Scan_lshift; IF (FRENCH) ! DB 0E6H, 02BH ; Scan_backslash  DB 'W', 02CH ; Scan_zENDIF ;IF (FRENCH)IF (UNITED_STATES) DB '\', 02BH ; Scan_backslash DB 'Z', 02CH ; Scan_zENDIF ;IF (UNITED_STATES)4 DB 'X', 02DH ; Scan_x DB 'C', 02EH ; Scan_c DB 'V', 02FH ; Scan_v DB 'B', 030H ; Scan_b DB 'N', 031H ; Scan_n IF (FRENCH)a DB ',', 032H ; Scan_m DB ';', 033H ; Scan_comma DB ':', 034H ; Scan_periodN DB '=', 035H ; Scan_slashENDIF ;IF (FRENCH)IF (UNITED_STATES) DB 'M', 032H ; Scan_m DB ',', 033H ; Scan_comma DB '.', 034H ; Scan_perioda DB '/', 035H ; Scan_slashENDIF ;IF (UNITED_STATES)B DW 0FFFFH ; Scan_rshiftI DB '*', 037H ; Scan_kp_pf4 DW 0FFFFH ; Scan_alt DB ' ', 039H ; Scan_space DW 0FFFFH ; Scan_capslock  DB 0, 03BH ; Scan_f1 DB 0, 03CH ; Scan_f2 DB 0, 03DH ; Scan_f3_ DB 0, 03EH ; Scan_f4a DB 0, 03FH ; Scan_f5S DB 0, 040H ; Scan_f6; DB 0, 041H ; Scan_f7  DB 0, 042H ; Scan_f8 DB 0, 043H ; Scan_f9_ DB 0, 044H ; Scan_f10 DB 0, 045H ; Scan_kp_pf2W DB 0, 046H ; Scan_kp_pf3N DB 0, 047H ; Scan_kp_7 DB 0, 048H ; Scan_kp_8 DB 0, 049H ; Scan_kp_9o DB '-', 04AH ; Scan_kp_minus DB 0, 04BH ; Scan_kp_4  DB 0, 04CH ; Scan_kp_57 DB 0, 04DH ; Scan_kp_6I DB '+', 04EH ; Scan_kp_comma  DB 0, 04FH ; Scan_kp_1l DB 0, 050H ; Scan_kp_2a DB 0, 051H ; Scan_kp_3T DB 0, 052H ; Scan_kp_0S DB 0, 053H ; Scan_kp_period DB 0, 098H ; Scan_f20IF (NATIVE_MODE) DB 0, Hack_home ; Scan_home; DB 0, Hack_ins ; Scan_insW DB 0, Hack_del ; Scan_del ! DB 0, Hack_select ; Scan_select0% DB 0, Hack_prevpage ; Scan_prevpage_% DB 0, Hack_nextpage ; Scan_nextpage# DB 0, Hack_uparrow ; Scan_uparrowF' DB 0, Hack_leftarrow ; Scan_leftarrowr) DB 0, Hack_rightarrow ; Scan_rightarrow0' DB 0, Hack_downarrow ; Scan_downarrown DB 0, 085H ; Scan_f11 DB 0, 086H ; Scan_f12ELSE DB 0, 085H ; Scan_homen DB 0, 086H ; Scan_ins DB 0, 087H ; Scan_del DB 0, 088H ; Scan_select DB 0, 089H ; Scan_prevpageB DB 0, 08AH ; Scan_nextpage6 DB 0, 08BH ; Scan_uparrow DB 0, 08CH ; Scan_leftarrow DB 0, 08DH ; Scan_rightarrow DB 0, 08EH ; Scan_downarrow DB 0, 08FH ; Scan_f11 DB 0, 090H ; Scan_f12ENDIF ;IF (NATIVE_MODE)8 DB 0, 091H ; Scan_f13 DB 0, 092H ; Scan_f14 DB 0, 093H ; Scan_f15 DB 0, 094H ; Scan_f16 DB 0, 095H ; Scan_f17 DB 0, 096H ; Scan_f18 DB 0, 097H ; Scan_f19# DW Mapped_compose ; Scan_composeEIF (NATIVE_MODE)( DB 00DH, Hack_kp_enter ; Scan_kp_enterELSE DB 00DH, 09AH ; Scan_kp_enterENDIF ;IF (NATIVE_MODE)ERRIF (($ - TOP) NE 0D2h) ENDM; "; Mapping for Ctrl-Alt characters."; Same as AltKeys for US keyboard.;0M_CtrlAltKeys MACRO LOCAL TOPF TOP EQU $ IF (FRENCH)I DW 0FFFFH ; Scan_kp_pf17 DB '~', 029H ; Scan_1 DB '@', 003H ; Scan_2 DB '#', 004H ; Scan_3 DB '`', 029H ; Scan_4 DB 0, 07CH ; Scan_5 DB '^', 007H ; Scan_6 DB 0, 07EH ; Scan_7 DB '{', 01AH ; Scan_8 DB '}', 01BH ; Scan_9 DB 0, 081H ; Scan_0 DB 0, 082H ; Scan_minus DB 0, 083H ; Scan_equals0 DW 0FFFFH ; Scan_rubout0 DW 0FFFFH ; Scan_tab DB 0, 01EH ; Scan_q DB 0, 02CH ; Scan_w DB 0, 012H ; Scan_e DB 0, 013H ; Scan_r DB 0, 014H ; Scan_t DB 0, 015H ; Scan_y DB 0, 016H ; Scan_u DB 0, 017H ; Scan_i DB 0, 018H ; Scan_o DB 0, 019H ; Scan_p DB '[', 01AH ; Scan_lbracketB DB ']', 01BH ; Scan_rbracket  DW 0FFFFH ; Scan_returnH DW 0FFFFH ; Scan_ctrl  DB 0, 010H ; Scan_a DB 0, 01FH ; Scan_s DB 0, 020H ; Scan_d DB 0, 021H ; Scan_f DB 0, 022H ; Scan_g DB 0, 023H ; Scan_h DB 0, 024H ; Scan_j DB 0, 025H ; Scan_k DB 0, 026H ; Scan_l DB 0, 032H ; Scan_semicolon DW 0FFFFH ; Scan_quote DB '\', 02BH ; Scan_backquote DW 0FFFFH ; Scan_lshiftB DW 0FFFFH ; Scan_backslash DB 0, 011H ; Scan_z DB 0, 02DH ; Scan_x DB 0, 02EH ; Scan_c DB 0, 02FH ; Scan_v DB 0, 030H ; Scan m~ LK250.BCKh:*[PCSA.APPLICATION.KEYBRD.KIT]KEYMAPS.INC;1M<4_b DB 0, 031H ; Scan_n DW 0FFFFH ; Scan_m DW 0FFFFH ; Scan_comma DW 0FFFFH ; Scan_periodc DW 0FFFFH ; Scan_slash DW 0FFFFH ; Scan_rshift$ DW 0FFFFH ; Scan_kp_pf4p DW 0FFFFH ; Scan_alt DB ' ', 039H ; Scan_space DW 0FFFFH ; Scan_capslock0 DB 0, 068H ; Scan_f10 DW Mapped_alt_f2 ; Scan_f2  DW Mapped_alt_f3 ; Scan_f3_ DB 0, 06BH ; Scan_f4a DB 0, 06CH ; Scan_f5c DB 0, 06DH ; Scan_f6S DB 0, 06EH ; Scan_f7; DB 0, 06FH ; Scan_f8  DB 0, 070H ; Scan_f90 DB 0, 071H ; Scan_f10 DW 0FFFFH ; Scan_kp_pf2E DW 0FFFFH ; Scan_kp_pf3 DW 0FFFFH ; Scan_kp_7  DW 0FFFFH ; Scan_kp_8B DW 0FFFFH ; Scan_kp_9' DW 0FFFFH ; Scan_kp_minus0 DW 0FFFFH ; Scan_kp_4H DW 0FFFFH ; Scan_kp_5  DW 0FFFFH ; Scan_kp_6  DW 0FFFFH ; Scan_kp_comma0 DW 0FFFFH ; Scan_kp_1n DW 0FFFFH ; Scan_kp_2e DW 0FFFFH ; Scan_kp_3T DW 0FFFFH ; Scan_kp_0t DW 0FFFFH ; Scan_kp_period DW 0FFFFH ; Scan_f20 DW 0FFFFH ; Scan_homen DW 0FFFFH ; Scan_ins DW 0FFFFH ; Scan_del DW 0FFFFH ; Scan_select1 DW 0FFFFH ; Scan_prevpage  DW 0FFFFH ; Scan_nextpageS DW 0FFFFH ; Scan_uparrow DW 0FFFFH ; Scan_leftarrow DW 0FFFFH ; Scan_rightarrow1 DW 0FFFFH ; Scan_downarrow DB 0, 0B3H ; Scan_f11 DB 0, 0B4H ; Scan_f12 DB 0, 0B5H ; Scan_f13 DB 0, 0B6H ; Scan_f14 DB 0, 0B7H ; Scan_f15 DB 0, 0B8H ; Scan_f16 DB 0, 0B9H ; Scan_f17 DB 0, 0BAH ; Scan_f18 DB 0, 0BBH ; Scan_f19# DW Mapped_compose ; Scan_composeH DB 0, 0BEH ; Scan_kp_enterUENDIF ;IF (FRENCH)ERRIF (($ - TOP) NE 0D2h) ENDM; >; Table of keys where shift converts to capital letters (0FFH).; or converts to a totally different key (0).;cM_ShiftIsCap MACRO LOCAL TOP TOP EQU $ DB 0 ; Scan_kp_pf1 IF (FRENCH) DB 0FFH ; Scan_1 DB 0FFH ; Scan_2 DB 0FFH ; Scan_3 DB 0FFH ; Scan_4 DB 0FFH ; Scan_5 DB 0FFH ; Scan_6 DB 0FFH ; Scan_7 DB 0FFH ; Scan_8 DB 0FFH ; Scan_9 DB 0FFH ; Scan_0 DB 0FFH ; Scan_minus DB 0FFH ; Scan_equalsNENDIF ;IF (FRENCH)IF (UNITED_STATES) DB 0 ; Scan_1n DB 0 ; Scan_2" DB 0 ; Scan_3e DB 0 ; Scan_4  DB 0 ; Scan_5I DB 0 ; Scan_6S DB 0 ; Scan_7  DB 0 ; Scan_8F DB 0 ; Scan_96 DB 0 ; Scan_0k DB 0 ; Scan_minus  DB 0 ; Scan_equalsENDIF ;IF (UNITED_STATES)  DB 0 ; Scan_rubout DB 0 ; Scan_tab  DB 0FFH ; Scan_q DB 0FFH ; Scan_w DB 0FFH ; Scan_e DB 0FFH ; Scan_r DB 0FFH ; Scan_t DB 0FFH ; Scan_y DB 0FFH ; Scan_u DB 0FFH ; Scan_i DB 0FFH ; Scan_o DB 0FFH ; Scan_p DB 0 ; Scan_lbracket DB 0 ; Scan_rbracket DB 0 ; Scan_return DB 0 ; Scan_ctrl DB 0FFH ; Scan_a DB 0FFH ; Scan_s DB 0FFH ; Scan_d DB 0FFH ; Scan_f DB 0FFH ; Scan_g DB 0FFH ; Scan_h DB 0FFH ; Scan_j DB 0FFH ; Scan_k DB 0FFH ; Scan_l IF (FRENCH)  DB 0FFH ; Scan_semicolonENDIF ;IF (FRENCH)IF (UNITED_STATES) DB 0 ; Scan_semicolonnENDIF ;IF (UNITED_STATES)  DB 0 ; Scan_quote  DB 0 ; Scan_backquotec DB 0 ; Scan_lshift DB 0 ; Scan_backslashc DB 0FFH ; Scan_z DB 0FFH ; Scan_x DB 0FFH ; Scan_c DB 0FFH ; Scan_v DB 0FFH ; Scan_b DB 0FFH ; Scan_n IF (FRENCH)4 DB 0 ; Scan_mENDIF ;IF (FRENCH)IF (UNITED_STATES) DB 0FFH ; Scan_mENDIF ;IF (UNITED_STATES)B DB 0 ; Scan_comma  DB 0 ; Scan_period DB 0 ; Scan_slash ERRIF (($ - TOP) NE 035h)  ENDM+*[PCSA.APPLICATION.KEYBRD.KIT]LKNATIVE.ASM;1+,)./ 42-:0123KPWO56HR̔7v 89GHJ; LKNATIVE.ASM; Sets LK250 into native mode.;!_STACK SEGMENT PARA STACK 'STACK' DB 100H DUP (?) _STACK ENDS _TEXT SEGMENT WORD PUBLIC 'CODE'Start: ASSUME CS:_TEXT;+ MOV AH, 0DEH ;see if LK250 code is loaded INT 16H CMP AL, 0FFH JNE Finish;& MOV AH, 0D5H ;output a byte to LK2502 MOV AL, 0ACH ;this byte puts it into native mode INT 16H;Finish: MOV AH, 4CH INT 21H ;bye-bye _TEXT ENDS;; That's all folks !; END Start+*[PCSA.APPLICATION.KEYBRD.KIT]LKNATIVE.EXE;1+,4w./ 4-:0123 KPWO56p:͔789GHJMZ >0jr6~ LK250.BCKW:*[PCSA.APPLICATION.KEYBRD.KIT]SETHOST.ISR;1N޼01A] 38E5:04E5 JZ 04F2 38E5:04E7 MOV [SI],AX 38E5:04E9 MOV [001C],BX 38E5:04ED MOV AX,9102h ;interrupt complete 38E5:04F0 INT 15h 38E5:04F2 CLI 38E5:04F3 POP DI 38E5:04F4 POP SI 38E5:04F5 POP DX ; ; Fall through here, or jump here, after handling the key completely. ; 38E5:04F6 POP CX 38E5:04F7 POP BX 38E5:04F8 POP ES 38E5:04F9 POP DS 38E5:04FA CLI ; ; Reset interrupt controller. ; 38E5:04FB MOV AL,20 38E5:04FD OUT 20,AL ; 38E5:04FF CS: 38E5:0500 CMP BYTE PTR [01D3],0FFh 38E5:0505 JZ 0516 38E5:0507 PUSH CX 38E5:0508 CLI 38E5:0509 SUB CX,CX 38E5:050B IN AL,64 38E5:050D TEST AL,02 38E5:050F LOOPNZ 050B 38E5:0511 MOV AL,AE 38E5:0513 OUT 64,AL 38E5:0515 POP CX 38E5:0516 POP AX 38E5:0517 IRET ; SCANCODE.INC - LK250 scan code definitions (for US keyboard). ; ; Some codes are followed by the "hacked" code. For any dark grey key, ; this is the equivalent blue-printed white keypad key. If the LK250 is in ; IBM mode, the keyboard will usually generate a left-shift plus the "hacked" ; code when the key is pressed; in native mode, it is up to the program ; handling the keyboard (usually KEYBRD) to perform the appropriate mapping. ; Scan_kp_0 EQU 52h Scan_kp_1 EQU 4Fh Scan_kp_2 EQU 50h Scan_kp_3 EQU 51h Scan_kp_4 EQU 4Bh Scan_kp_5 EQU 4Ch Scan_kp_6 EQU 4Dh Scan_kp_7 EQU 47h Scan_kp_8 EQU 48h Scan_kp_9 EQU 49h Scan_kp_pf1 EQU 01h Scan_kp_pf2 EQU 45h Scan_kp_pf3 EQU 46h Scan_kp_pf4 EQU 37h Scan_kp_minus EQU 4Ah Scan_kp_comma EQU 4Eh ; Scan_kp_enter EQU 69h ;mapped to ... Hack_kp_enter EQU 1Ch ; Scan_kp_period EQU 53h ; Scan_a EQU 1Eh Scan_b EQU 30h Scan_c EQU 2Eh Scan_d EQU 20h Scan_e EQU 12h Scan_f EQU 21h Scan_g EQU 22h Scan_h EQU 23h Scan_i EQU 17h Scan_j EQU 24h Scan_k EQU 25h Scan_l EQU 26h Scan_m EQU 32h Scan_n EQU 31h Scan_o EQU 18h Scan_p EQU 19h Scan_q EQU 10h Scan_r EQU 13h Scan_s EQU 1Fh Scan_t EQU 14h Scan_u EQU 16h Scan_v EQU 2Fh Scan_w EQU 11h Scan_x EQU 2Dh Scan_y EQU 15h Scan_z EQU 2Ch ; Scan_0 EQU 0Bh Scan_1 EQU 02h Scan_2 EQU 03h Scan_3 EQU 04h Scan_4 EQU 05h Scan_5 EQU 06h Scan_6 EQU 07h Scan_7 EQU 08h Scan_8 EQU 09h Scan_9 EQU 0Ah ; Scan_minus EQU 0Ch Scan_equals EQU 0Dh Scan_rubout EQU 0Eh Scan_tab EQU 0Fh Scan_lbracket EQU 1Ah Scan_rbracket EQU 1Bh Scan_return EQU 1Ch ; Scan_semicolon EQU 27h Scan_quote EQU 28h Scan_backslash EQU 2Bh Scan_backquote EQU 29h Scan_comma EQU 33h Scan_period EQU 34h Scan_slash EQU 35h ;highest shiftable scan code Scan_space EQU 39h ; Scan_ctrl EQU 1Dh Scan_capslock EQU 3Ah Scan_lshift EQU 2Ah Scan_rshift EQU 36h Scan_alt EQU 38h ; Scan_f1 EQU 3Bh Scan_f2 EQU 3Ch Scan_f3 EQU 3Dh Scan_f4 EQU 3Eh Scan_f5 EQU 3Fh Scan_f6 EQU 40h Scan_f7 EQU 41h Scan_f8 EQU 42h Scan_f9 EQU 43h Scan_f10 EQU 44h ; Scan_f11 EQU 5Fh Scan_f12 EQU 60h Scan_f13 EQU 61h Scan_f14 EQU 62h Scan_f15 EQU 63h Scan_f16 EQU 64h Scan_f17 EQU 65h Scan_f18 EQU 66h Scan_f19 EQU 67h Scan_f20 EQU 54h ; Scan_home EQU 55h ;mapped to ... Hack_home EQU Scan_kp_7 Scan_ins EQU 56h ;mapped to ... Hack_ins EQU Scan_kp_0 Scan_del EQU 57h ;mapped to ... Hack_del EQU Scan_kp_period Scan_select EQU 58h ;mapped to ... Hack_select EQU Scan_kp_1 Scan_prevpage EQU 59h ;mapped to ... Hack_prevpage EQU Scan_kp_9 Scan_nextpage EQU 5Ah ;mapped to ... Hack_nextpage EQU Scan_kp_3 Scan_uparrow EQU 5Bh ;mapped to ... Hack_uparrow EQU Scan_kp_8 Scan_downarrow EQU 5Eh ;mapped to ... Hack_downarrow EQU Scan_kp_2 Scan_leftarrow EQU 5Ch ;mapped to ... Hack_leftarrow EQU Scan_kp_4 Scan_rightarrow EQU 5Dh ;mapped to ... Hack_rightarrow EQU Scan_kp_6 ; Scan_compose EQU 68h ;in LK250 mode Hack_compose EQU Scan_f10 ;in special mode (with left shift) ; ; Flag bytes used to indicate special mappings. ; Flag_deadkey EQU 0EFh ;dead key (non-US keyboards) ; ; 16-bit mappings looked for by DECKEYB. ; Mapped_compose EQU 0BD00h Mapped_rubout EQU 0E08h Mapped_alt_f2 EQU 6900h Mapped_alt_f3 EQU 6A00h (*[PCSA.APPLICATION.KEYBRD.KIT]STDXX.ASM;1+,<./ 4M-:0123KPWO56 :647 89GHJ$0; STDXX.ASM - a program to regenerate STDxx.KEY.J; Creates a STDxx.KEY file on standard output (user must redirect output).; ; Wishlist:; - check stdout is redirected.;; Revision history:;; 31-OCT-1991 V0.419; Ctrl = Ctrl-Shift and Alt = Shift-Alt for all mappings.;; 24-OCT-1991 V0.405; First public release. Version number is arbitrary.;H; STDxx.KEY files are all the same length (2226 bytes); they all containG; all the possible dead key mappings, which can be used (see below) asF; three-character mappings as well. However, none contain any of theD; more complex (VT-style) three-character compose mappings (such as; s+o=section sign).;G; Set the next variable to 1 to produce a program which will generate aK; .KEY file just like the Digital original version. All other conditionalL; assembly variables (except the language code) should go to zero when thisJ; is done. As a check, after assembling with this variable set to 1, runJ; the program and compare the output with the Digital-supplied version of; the .KEY file.;!IFDEF ORIGINAL ;TASM /dORIGINAL/DEC_original EQU 1 ;Assemble just like DigitalELSEDEC_original EQU 0ENDIF ;IFDEF ORIGINAL;H; Assemble code to map Compose (native mode) to ALT-C (for WordPerfect).?; We do this at our site; you may wish to choose another value.8; Shift-Compose will give the original code for Compose.;'WP_COMPOSE EQU 1 AND (NOT DEC_original);7; Assemble code to allow 3-character compose sequences.;&COMPOSE_3 EQU 1 AND (NOT DEC_original);*; Select enhancements to keyboard mapping.;)ENHANCED_MAP EQU 1 AND (NOT DEC_original);G; Assemble code to allow LK250 to run in native mode wherever possible.;(NATIVE_MODE EQU 1 AND (NOT DEC_original); ; Select national language code.;%UNITED_STATES EQU 0 ;US key mapping%FRENCH EQU 1 AND (NOT UNITED_STATES);COUNTRIES EQU 0 + \ UNITED_STATES + \ FRENCH + \ 0ERRIF (COUNTRIES NE 1);!; Keyboard scan code definitions.;%NOLIST include scancode.inc%LIST;); National key mapping macro definitions.;%NOLIST include keymaps.inc%LIST!_STACK SEGMENT PARA STACK 'STACK' DB 256 DUP (?) _STACK ENDS _DATA SEGMENT WORD PUBLIC 'DATA'+IdealFileSize EQU 2226d ;size of all filesFileStart EQU $;; Standard .KEY file header;) DB 'KE$Y ' ;for file type check code;M; The FileFlags byte is interesting. The only bit whose function I currentlyF; understand is bit 5 (20H). Setting this bit allows all the definedJ; dead-key sequences for all languages to work as three-character composeI; sequences in all DOS programs. If WP_COMPOSE is selected (see above),F; then some other method (eg Shift-Compose) will be needed to get the; Compose key to work.;FileFlags EQU $IF (COMPOSE_3) DB 20HELSE DB 0ENDIF ;IF (COMPOSE_3);5; Rest of first 128 bytes is extremely uninteresting.; DB 77H DUP (0);; Mapping for Alt-keys.;AltKeys EQU $ M_AltKeys;; Mapping for Control-keys.;CtrlKeys EQU $ M_CtrlKeys;I; Mapping for unshifted characters (or numeric keypad keys via Num Lock).;UnshiftedKeys EQU $ M_UnshiftedKeys;"; Mapping for numeric keypad keys.;KeypadKeys EQU $ M_KeypadKeys;; Mapping for shifted keys.;ShiftKeys EQU $ M_ShiftKeys;(; Mapping for characters with Caps Lock.;CapsLockKeys EQU $ M_CapsLockKeys;"; Mapping for Ctrl-Alt characters.;CtrlAltKeys EQU $ M_CtrlAltKeys;#; Mapping for Shift-Alt characters.;ShiftAltKeys EQU $;; M_ShiftAltKeys M_AltKeys;$; Mapping for Ctrl-Shift characters.;CtrlShiftKeys EQU $;; M_CtrlShiftKeys M_CtrlKeys;>; Table of keys where shift converts to capital letters (0FFh).; or converts to a totally different key (0).;ShiftIsCap EQU $ M_ShiftIsCap;2; End of tables which replace those in KEYBRD.EXE.; DB 0 ;Not part of tables...;; Compose character mappings.;ComposeMap DW Map_space ;' ' DW NoEntries ;'!'0 DW Map_umlaut ;'"' (dead key on French LK250) DW NoEntries ;'#' DW NoEntries ;'$' DW NoEntries ;'%' DW NoEntries ;'&' DW Map_quote ;"'" DW NoEntries ;'(' DW NoEntries ;')' DW NoEntries ;'*' DW NoEntries ;'+' DW NoEntries ;',' DW NoEntries ;'-' DW NoEntries ;'.' DW NoEntries ;'/' DW NoEntries ;'0' DW NoEntries ;'1' DW NoEntries ;'2' DW NoEntries ;'3' DW NoEntries ;'4' DW NoEntries ;'5' DW NoEntries ;'6' DW NoEntries ;'7' DW NoEntries ;'8' DW NoEntries ;'9' DW NoEntries ;':' DW NoEntries ;';' DW NoEntries ;'<' DW NoEntries ;'=' DW NoEntries ;'>' DW NoEntries ;'?' DW NoEntries ;'@' DW Map_A_upper ;'A' DW NoEntries ;'B' DW NoEntries ;'C' DW NoEntries ;'D' DW Map_E_upper ;'E' DW NoEntries ;'F' DW NoEntries ;'G' DW NoEntries ;'H' DW NoEntries ;'I' DW NoEntries ;'J' DW NoEntries ;'K' DW NoEntries ;'L' DW NoEntries ;'M' DW Map_N_upper ;'N' DW Map_O_upper ;'O' DW NoEntries ;'P' DW NoEntries ;'Q' DW NoEntries ;'R' DW NoEntries ;'S' DW NoEntries ;'T' DW Map_U_upper ;'U' DW NoEntries ;'V' DW NoEntries ;'W' DW NoEntries ;'X' DW NoEntries ;'Y' DW NoEntries ;'Z' DW NoEntries ;'[' DW NoEntries ;'\' DW NoEntries ;']'1 DW Map_circumf ;'^' (dead key on French LK250) DW NoEntries ;'_' DW Map_grave ;'`' DW Map_a_lower ;'a' DW NoEntries ;'b' DW NoEntries ;'c' DW NoEntries ;'d' DW Map_e_lower ;'e' DW NoEntries ;'f' DW NoEntries ;'g' DW NoEntries ;'h' DW Map_i_lower ;'i' DW NoEntries ;'j' DW NoEntries ;'k' DW NoEntries ;'l' DW NoEntries ;'m' DW Map_n_lower ;'n' DW Map_o_lower ;'o' DW NoEntries ;'p' DW NoEntries ;'q' DW NoEntries ;'r' DW NoEntries ;'s' DW NoEntries ;'t' DW Map_u_lower ;'u' DW NoEntries ;'v' DW NoEntries ;'w' DW NoEntries ;'x' DW Map_y_lower ;'y' DW NoEntries ;'z' DW NoEntries ;'{' DW NoEntries ;'|' DW NoEntries ;'}' DW Map_tilde ;'~' DW NoEntries ;DELNoEntries DB 0 ;0Map_space DB (Map_space_end - Map_space - 1) / 2 DB '~', '~' DB '`', '`' DB "'", "'" DB '^', '^' DB '"', '"'Map_space_end EQU $3Map_umlaut DB (Map_umlaut_end - Map_umlaut - 1) / 2 DB 'A', 08EH DB 'O', 099H DB 'U', 09AH DB 'a', 084H DB 'e', 089H DB 'i', 08BH DB 'o', 094H DB 'u', 081H DB 'y', 098HMap_umlaut_end EQU $0Map_quote DB (Map_quote_end - Map_quote - 1) / 2 DB 'E', 090H DB 'a', 0A0H DB 'e', 082H DB 'i', 0A1H DB 'o', 0A2H DB 'u', 0A3H DB ' ', 027HMap_quote_end EQU $6Map_A_upper DB (Map_A_upper_end - Map_A_upper - 1) / 2 DB '"', 08EHMap_A_upper_end EQU $6Map_E_upper DB (Map_E_upper_end - Map_E_upper - 1) / 2 DB "'", 090HMap_E_upper_end EQU $6Map_N_upper DB (Map_N_upper_end - Map_N_upper - 1) / 2 DB '~', 0A5HMap_N_upper_end EQU $6Map_O_upper DB (Map_O_upper_end - Map_O_upper - 1) / 2 DB '"', 099HMap_O_upper_end EQU $6Map_U_upper DB (Map_U_upper_end - Map_U_upper - 1) / 2 DB '"', 09AHMap_U_upper_end EQU $6Map_circumf DB (Map_circumf_end - Map_circumf - 1) / 2 DB 'a', 083H DB 'e', 088H DB 'i', 08CH DB 'o', 093H DB 'u', 096H DB ' ', 05EHMap_circumf_end EQU $0Map_grave DB (Map_grave_end - Map_grave - 1) / 2 DB 'a', 085H DB 'e', 08AH DB 'i', 08DH DB 'o', 095H DB 'u', 097H DB ' ', 060HMap_grave_end EQU $6Map_a_lower DB (Map_a_lower_end - Map_a_lower - 1) / 2 DB '`', 085H DB "'", 0A0H DB '^', 083H DB '"', 084HMap_a_lower_end EQU $6Map_e_lower DB (Map_e_lower_end - Map_e_lower - 1) / 2 DB '`', 08AH DB "'", 082H DB '^', 088H DB '"', 089HMap_e_lower_end EQU $6Map_i_lower DB (Map_i_lower_end - Map_i_lower - 1) / 2 DB '`', 08DH DB "'", 0A1H DB '^', 08CH DB '"', 08BHMap_i_lower_end EQU $6Map_n_lower DB (Map_n_lower_end - Map_n_lower - 1) / 2 DB '~', 0A4HMap_n_lower_end EQU $6Map_o_lower DB (Map_o_lower_end - Map_o_lower - 1) / 2 DB '`', 095H DB "'", 0A2H DB '^', 093H DB '"', 094HMap_o_lower_end EQU $6Map_u_lower DB (Map_u_lower_end - Map_u_lower - 1) / 2 DB '`', 097H DB "'", 0A3H DB '^', 096H DB '"', 081HMap_u_lower_end EQU $6Map_y_lower DB (Map_y_lower_end - Map_y_lower - 1) / 2 DB '"', 098HMap_y_lower_end EQU $0Map_tilde DB (Map_tilde_end - Map_tilde - 1) / 2 DB 'N', 0A5H DB 'n', 0A4H DB ' ', 07EHMap_tilde_end EQU $ DB 0FFH ;end of fileFileSize EQU $ - FileStart!ERRIF (FileSize NE IdealFileSize);; Announcement message.;3Announce DB 'STDXX version 0.41 - writes LK250 STD' IF (FRENCH) DB 'FR'ENDIF ;IF (FRENCH)IF (UNITED_STATES) DB 'US'ENDIF ;IF (UNITED_STATES)# DB '.KEY file to standard output' DB 0Dh, 0Ah% DB 'Copyright (C) 1991 Nick Brown.' DB ' All rights reserved.'- DB 0Dh, 0Ah ;no '$', we write it with 2140AnnounceSize EQU $ - Announce.ErrorMsg DB 'Error writing to standard output'- DB 0Dh, 0Ah ;no '$', we write it with 2140ErrorMsgSize EQU $ - ErrorMsg _DATA ENDS _TEXT SEGMENT WORD PUBLIC 'CODE'Start: ASSUME CS:_TEXT; mov ax, _DATA mov ds, ax ASSUME DS:_DATA;); Output announcement message to terminal;. mov dx, OFFSET Announce ;Announcement message mov cx, AnnounceSize mov bx, 2 ;stderr mov ah, 40h int 21h;>; Write data to standard output; assume user will redirect it.; Length_OK: mov bx, 1 ;standard output mov cx, (FileSize) mov dx, OFFSET FileStart mov ah, 40h int 21h jnc Success; mov dx, OFFSET ErrorMsg mov cx, ErrorMsgSize mov bx, 2 ;stderr mov ah, 40h int 21h; mov al, 1 jmp Finish;Success: xor al, al;Finish: mov ah, 4ch int 21h ;bye-bye _TEXT ENDS;; That's all folks !; END StartAEh~ LK250.BCK3 kk g 1Lz^_QmAT`;o ]ɤJ«/ߞfj mw5-_lZV;uJPH,!bb](rND>D?G2N_8={BP/O[|? ʾ㜍umf1=;5J>SUw ?PKo[/JҏWgyPqÇ\ 9<Iqux7wh.b|E_)y<;oW?s'B0*5 &UrCn;2QJY&%% T:[I߇ S]%5oR7x$7@0}a>=U)tWGAHq>Uo(\ xPTdxU}e|Ν҇RU1'D*^"rYgPnB&>mBI(MuzǔG8oRdžEMn1Ǩ'%<-{}3QMbf_ggAnRKDxDX}ju7EUJ1g}+h8FUmPg{ .M`A%37$ $J( >R~sa},^/5ct, sKt~%$^{ QJ̺f#w$7 nv9rX8IcSPRo^9Wv,ػ({fqKG4MNa`I2 3o5U,["w>AjvS~m-I*[pJjXmv2# dt.je ~  2= ljA  c*Xwz~D$q[ l}XM АYH ;>NeP`r/{y: -Sa¶YngOo* 'UrFf_h-U dI23At*9E y26Z?ɲUc8J6/R""xuTfY 1X@@WL2f{I)5Ztd6g'^M=jYcS D؂.LBs}O;+`N_S'!ytogZ[."V4bJ>\6N? L kk.#r<#)v]Y>7hg:M%+V_LQ&D!s4\1Hfjo:aT1 $Z}l>6\,~x/wsHXGBh$$ORhR^gB,8~R&u$wO-=_",Fih}/b=SkehS.QR״_&%et#P-A=AJ 7>Љiu_{DQ.4 EAN4rSY*vH\w>~xx ,oC152'|Zף10巉LL Ei3Zd|c6 «TWMKH,ڞEQpJ%s5X~ s37>S&}8g6AyYCZo-t7_|}:@ *$=E;T?\U%{1 M6|19BTA5??q;V1<jIdy b~ # .Ye5 cZNv$o*&ugoS5fʼ[ITnF^=\-seW !*6Gd(^.¼Y#e`kRUj8'LK|:MP8+wP E~o BnZf &A\%gGoYuX)"R%;aO3ɖQ>#d.f\A+OEAF7g*;vU@>Z wE0R!EF84^Gjt^jUE$1.?2C C!Bd&kXI$QOj> .F6[" ED/m^183"pT6Hb%~hѿ3(9t5Hc$L lKXٳS ҁةދO?Qmy:VM3+SXo^BS8Ԏ!S\D_r#[2a7dz'O6PfU5U|H2-Js4b}QT+X-ƞ &_f.r]tOMih7lj5 ־7'/ooj ! m䕠R MmCJ EU`*xR&;QGM7TFyz))K]Ó8G@i{Թo񡟓>+)UrcjeN cEQ:o .9zwwvYվS.GFm(J+48D,bzWQW,& {tB7XD+VvgVrA0s_+oOY֫O2 ʸ4@م{#uqXvw j/+}v2JXCFK|Dn-? j>_P& Fl` W+ PpxʪR_Lw \5YWdJJW̸T1<$4Hg(>zulQ7p#HVl hiA>àl@|H^#1IKy hsfyVx&JqΛ%>!Ա鍌]Fw/r mxEr^Dje:3Q4n@dp0bKXend[H[+L#X_h h x< 2[?r!S Bs7,ъ1]:I3fV"B}yrÃ͝r rҶJ@h[Z.dE$HOG;h",U bȅY2Q#v4"m-Y2!.v;Asep ^(OR|nvr1x5Cb{6u9^WGP9h%$]>R{Ez/aJ<#o6#*&IZ#/sH[f 'Q DA;aV)WOh[U_^fU E\d P= 24mc?"\\7{C1)f_B!hww[eDPE O+KTJ8j,dY}yxyU2G^?0iHJX^c` l NmJO( 1IXH-TXVAB9 VF*phfimUi^ N*!AQUJx37 I FQEKi[zUy|J.VufeZUe@>9, [ao2=Ly/IvP(U}2]>f* \{aaw\Gr ]rt[L 7UKNW$u~v~5#C~URKXI"N~%VEO5N?O!'m6IZq1\}q/5,4sD*-T1cu!bK A{$u"yf$XAh-ieVD\j4?Wb@,=liP:fM;o(;rJ %&M,2>!_P %D QcW@BIw%o0;}7GO\NTw=Sk[ąH<_|+ dxFF3Z9ZhOXn 8A[ MbTv.?Gm 3Ok4~Ao0'7/jJ?BYSP{=",D*vEB#36K$AWHQ?f=lhuY, @|# B8 TN$m]쌞g wy"_MXY87'xIh)|"i@e5W ~ը+7h kXJtg@{QD|h6g[V;b9_%%R[7\KMĪ~"VPe}K&ND.L<q-b # kN8(qa1Cyju¸ |;dSL 0l-zvz#bclRJg{E8sU@%Xk@yeu+oH"/DJWF f E../HdQ&[S~lkq8 9 С5]$ ZV@RwF[TO'ma+#]=~=`+J B} fL(N\A2aTL G?*hIE e 0=u@$y. ^=WV/@A pd+dkTқr\TVC3doB)ZCr.+RgzQ Ye3t|qPt{nx[AOl2e[+Nk w C`'V&_;4]#٭1 6;3~\xF6Bwf(H%;'bl2+QC>daV xP lurYww9&r w<lff4L7 cU5\n!NBAFn-[4{KZLg: eybS/jw+w*W+V69J!<^YcXeJJ&+TL"Gb!$hl~Y\ HU5f|;9n63|y ]nZM$XSdiQ8jLd_ Ys%x`Xf:*1i3L_f$1^ Ia}U  4$16lj_lD$&(qmT]xK[UWx,NT@E2h.+w(BFݲPcBYSW*"=ɘ .!GC5[k&q3FE=!IL~E5i?LS!xPLP#u:%4cxv12T@xki6Ro3 &f.OHY"?YZ|k>-.]uZwD'MKFkoWK5 U<[{7l--"\MbEHQn}# y %zpBg93x ',I3US:JA?]DW<s]A{?-:F!E`J-!2"kwJ"ygkP_GE;X>&*RP/w!)QuI j6`_L@BW_OD &?giDV&}n*Oi( JrO*%J]y6$[st,XEmxOgu2Co}4{Zf8 \E##OIGx/I"NZcf|WeE0*a]oڡyIWvThfiWT7]HFW3Y56X,|g.1,*}"v"7ie*93QAzTIOE}_iV,f{ZOo"4vMW2d mg&s F]OP.Bz =(lnV._]MD^&hcN)1bOPg@tm^oP#t#H~8 ZodF9y13f>9Lran} M%, unf?T9G"BrT.60 _X[p SFe 'oxlZ!'5o2YR8.61/u~M+IfV U0+L,UJg2&bQ$sh8a,e1 \)`I %a`rG_Ks CTB_*+N~B8?Z0} {'+;Xja ,Puz nf:6B )re/C>U;]wgs.QvU^Yig&#g8M!(fAn1fd!mN+Dj|{ wA[Qj*%uBq4a|YztR*0Yly0d[!f x949t +M]H@C>6g1K:8 a}HdNmHIm OfY:hC| JR~g;.+r.!|`\*<0'J==o++-h9J/GX]4LNhpbkR kCfD[X.-t@K iu/(g [+[%&Xxb7GJ]iot; C7#jTJRdhE ] 9o3_F*Ci ,qh ]7lg@Xi6oVTk{l-pCyotVhV25Kx|Jj VU'CnQ/#1Y2'/v1E=W SdQ)Ena=.I$Y@~'rAQe>?X ia#%-H6y8F&#V?mK`RB U2zy2Jt>`2 A7t92)|D.q`FfGN*k7o *ao x^Ka;7}*P[\9Bls(xxzcNa# F B;d5`?m30o1W)T,RvMuf : 8u9;bE"Uwwf9T |/p@: _U7?{! e&P1IV2O7,osFQYy2+F-'$y}Aj*l gB1nur :){{]]F6E/DN}}kl%qU_ie67W9@rCK]8ni]_ lI@ 5_wA F2fR"K@a0L^k#@+8ATa1EkJL'v]Ev=_KC yQ*j;"boY2:FN B$z +>no]eC-3Nc 1/;D=s =_!B)^}pnENG [346 Tx| wqH@?=_\"gPP#3]XtU>dlm8S(b6z6n6bLHjfSn\ImI[d**3jZC (DM`H-C1su,q%o< bY v0+]M7M;\*zL6{(K:7jw \XZQOBTVk9g$ G\"(RKSNnR2'h;B_VL1uI I2\$`N.{+gx,1t|r~ f9MM 'C&sRCwz]} 5n v4m 5N >nT0W)`,e\$,\d0noo68q4a4 puGub-XLVG$Aa99\!F%Q$h7>}h CX n\It\~dVQj;s+GUr_M;VA:t>cfKb (xV$)2"Tx4bs2#~v'5 @wjUQM6v 9G} ('s-3r>9=S{ZNGmyp/Nv'8(Bo`f]!IbpO]r+B5.6439Uur%uJM]?B,MAb;*?[/Z0Xz5L1=i&>NUVJNdU8.@jf`c/Ls2i@pGw1)+YsB0H-L@F:xg! %&K4X:07Bn|lKwoEp?o] H%KM%&Q#`o-> +sR x<V68x c(c4*,jM.0v_zZ_$,?%857*<2zrKg }`'@G{I2!HpSJ&bX3w'PLur.w;$(A`y7dbvaDHE<"98Si)M@ Pdx]>K)<;%Peh+'ir4M$FfdRbQu2ZZ>Vd*6FuS[wu{y~v/j9'w.gmn_rjFW5g|PWs|LoI&;92@[MwvNdf,Y7%'F{ o xmm8[/Oq@oCgCCW^Dw$*+peetMy'?Kf`- 5IH6wzbt3VOaL TB3[- '/FQ. *VoQo}bseQ& fC> +\w_F!YSuC=SM\ |Z|; -+_fQD[?F A5v- /7:j=x(rWwm6_oelZ-EAsae./AS )^]PR'j)}q+\ }xaC/-VZ[ E(j&j:C $tNDIL`@7" |B 7 ##j)k ,)=] txHb .'~?fAy/BWr9Hf Fo" {s(v 5(1dAw=N) oLZSmf(L5,/[l[UX%xP'2PY PX|[K\{%bnpok 6|5? T6Pgbn__ X|>+ AxH1HAzlz"Y*ZxL=,qDP3i."H,k8>3f7&o-(1\Tel(Bw^O5H:8R>%AQ9@ 4YPE~]=<-U&}]3c@dYq2Sdt&]A/#>zespS CT{ byj<ZXSWyo3s~"d\|&o!*eJ 2v5DQj_ iC @O9 kbuPX\W<9#H&$ ,TtN X 5E J*g9/-0#]"nvB=x5K4(_zZq/HB i8S[g#Q5#>&poRCP d`'9gy[v&d>y)[t)mW=9 BW1a(b-]j;o*. yn6i%%|X{'<2'nU=R^N>%(E:U~{VM_ 8^ m 0YND)&k)4@$srn2QlbwR%z`/g&I{kin0y)Amxzza:*~rQjn/>w]_@h~!Tf` -SX_:0,v)I(tDH^ !&b}{'nt%R ~n/IOS[UYPi*=I\Lb;0%R+n2Tt< HUohi1T5/nK-R!T?Fub8u45(a#N^lHxn9SS}O?yxfwDGJ>sWfQy*ja\ Z7Gny\<f{&_Xf4i 3fo(;~kh6n 8+-c@pUNATfr>~f7pA''g/K`= ,U) k.XDw)iDf>)f9oU@P92$IkwOUf$|Hq5B^~ Q&Lh;z8nJF@MQC5p*L/]UO yp} OA)q bz k.ROw2][K_%WK<r ~)vJ8i&Fpre'u+MG,Rxac!`@KX^|Cc!sp3]?5L3CrKntuN?c/>nbt.KS- f-%E!:$KOnv:kct5B[d_k<4LV > A|@1dXq:&2\nH}]x[d[9FwVjm`>iEyY|I$[Z7NiaK|0,dgdJI8Qaq6z+fL}e&NA>*| Ot|>)G%,W~V\&v@]Rs]OaEb,; ?Z jKY;7 +H>1+>&u4&A`8'_I@GeEGe3jciv_e9-%*+_V[+M :-Vq! =pr/[r~5IR~V6-+9}G6c Jq/ek2$(4_ @7H4W[9z)3Vwon_x# (&jL lB8R&'=A >,Krvu7/:^jc7'xKn9HBV,q)H$~2Q r,Dae.!nwEK~t7H~Gh ?WH+61 8e?c]&e <0Q ;w^:q4+d=G5B,Tkwf3)~ Vah>77vWrqc\F/jYo^H-)N\|Dq'ujW77u={_(ppN\z# y:,cAx?3FQC) @E?786Be~AR? u(M1sG)8>1bwGXI m4X tJ/O6>dP&n)A]VCJIh.N}dj)9}s"Yg*0~YNt;\d>kBc9X%6||.ptE3{m/&"EcCXr}T a`^\#Ra[:}UtZ.UpzoTpRtRsQ1 j?%yGBotb9^h j>[}M}X O[0 #:rB%kQignBlq$Tb1 "kGL'0MC:tl`5E$+#_Jc>!;e}rVpR[L.XQgf{b $M2e.d'L/KS^4 1h _y1![5MS'(wP`6~ 0@j,EFANUwnU65gm53b1Zl:y/$4!s=>~D7F\K0-;_ K=(}1#&~W~:;QwKZ Ft#s\Lh~f@LjH0 ?{2Dd` 9h P7gP~C_H b[-HB}c%r'\60"nLS`tzu:#: S( q>s|B!~uX2pJIC6VCUwxYs Hb.O0-}F?1/J,1'nwu`PMNqz5M\Y_7~ U x99>D=[}'%w3;=2,o.{-xB%']w@{Z*7( OsDJ]}d;Ie:2w)abcv]o78hGv '9w#rm#K{5m l@Aicb C4 [ sw$H1>~,NPLQX+9\TcRR."( ).VTJeXF7-7;1O5sF2Gr2BzJsYhm\SXHq5>3&-%0-=mORMp8aN2($%E n5(efD7t,[-Wic>t"|2/"yTmHb.bj5v&6glg/ C~Mgi7Gk.pY]Px.?#'3%2S ]ky@$A) Zy{Um=Ug"C'oN?PG|wQ{,nBq4/mxL7dis ]z".4zj2o:?(K p!&HK^tHbr%^(NW)FiYg@q}bcv7RJkpqUN SpwZ(HyhVhi03vOgZl|?O 94 kVea~a04[7@j !L2HW0f2 4O}y\?,d,h8/ RI4 GQW*Pc1.m}\M@n]9Q'}%k{xH p m)uu( I 2ZU=cZ? V=E?jeC8fesAd6s62l%h&;6M#)S ?35N~rA@Sf^{dBb!OFq]YipMr3hb"*wLaRbcL.TY1$6[D N4V0 O1y'o>Bo6YL~:;LG`VCb|d{ 8y-()&>VF}Z5r=`:@|?"O|MoNlTM?1Y&)FTza9[a#>[5AR-0 8'Qs@6s#d ^T|RS~ 45Cw`y+3M|o.lMnwZa%?*I &53 Oa2U.,FO Dt/OPy 6-](R4 KwY2I Lr:CQ";Kn/cSj,9Of*SL Y?~"Ed]a:x6A.7xr1]9,'$m9Nz-mX7@G~FDZHk$fr; 2:#C*Y"V,E 1>J6a HhJzYL=5# } ~ TjgF.H>?#VI|0IL5JOg4;|-s\C4ENjgK-ds?H%QqvdFDFWED,'Xv~7`! iwt9VZL_Ghn<k!]gi'Fi7t~EoHDIf/#_&&ql:c*>Sd`'Os[d\[{Dx X'UHR%NGj naC[kZNi L/IdC(CPYyWH(UV4b*$'Q;nNSNzFX }Cc6yEt E"vD/p8R_k7b~W:S",OPK5 `~<=A8eThS1b =C^39) ?l;4^/eVvL,U=s2Ya Y:&hTq9kJV#g! Sh'vCKb'T6yh(@yD Q@??+yGenaE( ?Ic;J^1kZ 3qb&9 VP3MmFm sK +el":(p:bD<Bi6,dM}s_ axV#82R@Yr?{Dz<5G1A&._~"rTP0*@[g!h),&Kvu)cϽ=;qTQx;dKHu2Y ]x:Y/44g^f|Lhm+]Ae9st={??Ulgx" =s15" $:JBn`lWE/~h#/F )cLw/fv<).*O+ 2\D.P+m$0rq02'7*X%&QHLi2/_HrC+4`,Cd@Rk~R*|2$F\Yqe9v#ub>u0=[{ x%w $mTk RbqA"guQ`iKDR"mP5FQ\zV#F x3Y8 O* OR\B] O35<sxL*(XdgV<A%axfc$%_ ?\#Cn3Ao'AO(DCed%b6B5HMxtQV%1:izHq %SOK~CLkacxkNXq6Qr>B}i^.Esl}0*'~?8]mP5v8`kd5&BDU uri UN*`bg,jquImyT ZP/ 8FCj~x,u4_m wHc;c-dcFk~GZ W~j&*gZxV2zuE2k+0E5KXh=n Ix b. gy0e84*qxC}OPjhP#LOx^fIL2o( 'sKlH[npt}W=V2G~TZ{!{%BDL#Z.$ YKqb.;P{3X{DU+. }32s}K+] Y#U*W]47]L\jABz z72MN%5.PGt[8[#zc,Y9M "?e)yxCCG#z,]%Yk. |b oZcE K4*}9Fo5X87IR] bW [XzK)e1oZU` *U;]t(1QEry&>W!B{zK,Z "icoRu8A< C?0WX+QSRT 4 >lWt1*r774k{$H m Q@d&dP^hYU #LJ(D^ݗ95$TPX(B_c$PC R1fJCN/WpE\H<uis`/cWF ?z Dg]"ܐJ4}`|{A*sL[+d):=m$kw'^x^Si,:^>'P/#>L s&SMJo1  tZCcO[]h=B!P+Ha0lN%m eN7VMs[mtup{m12^F>)|MU +~Vvq{]AM*0 9hA`w}fNw!T @CB`Z'P1=vf[wbc 9>,E>"l~D2F.uNzDM)dkeiqyo ]H En'+g|}H@0)g" {o2Bg7T_5 WrsL9b SAS?t/12J_%eqL73xPFxsUw!HFVNi;beb@0a::!;(%/:]}ur^j*vWJ z 8V{ f^D@!Rg9lyd%L/ {4o nWQ\EBx{F3:RC@W[eh67X/XyBvaW2TEiWMqmjoXNn[zNO@ z) jd4H o$P+@( Il5G;'c1'L?_2?%. M\=(^_a9Z*YvD~#p:c_A*/ro_ bno @h^{K$eKg%^QOBroAzD6 {3@SI_t}B{~!1\3fG #?`1Op!'>l1iB@[ I@ 7p<T?_H /[)[vg|>C.~%h3aB] ]b/`sFp))O)K8p7qHj  ]fc:.-{(rV/_j ES6`Zb_)dR )F#f<LgyC= X?P}F*C=!:-o;-z\8T|tI=:IS]A/{@>njGKnOU:c*j&E0wA:5_E&9L 1&zwazcMiXnZsa~c"6*iKyy3!@6%CN2\)u"hL Q"S -9B8F7?1P:Ibl0F>0H ?>vl[(@D :d.r\W(?KR<:Zg(47g]jBmz!8yTxiz]e35w]*'I&3D"zqWo mS.inRRS (O l[MIOQ*_\Y*}](z1>ZA2f26?6*A h WZI-SMqU RT:`&<;m2V}MQ.c|=hef$<2dFmxwso?RAZ zUBwodnt+j; ss\=v/xk) Ky'2r;}lmol6Y`_Qg#N?z a J654J0Ujt8N(v\ cj$cq&9%_+4- 3r2cQoKX(MM%'7}AQ^JSp|}i<8zD<`xlY`2s{y9@M;/T6<3H}[hwg A(1g!<|x *!50@#u"?G[kKk@gfSLU]^+^I}{y[#]M],p 1 B[a^I}9-w[*XnXW: )cfXlmc4J }gb/W3{luaOWuwz#,_is?V"8-rbn;9&ZwNNis{ &cW]y+*>{aeu?Ha$KAQ#V&V8no8B;xb`: i3|^P$gxsU:RlN 33"\W2(c1cZo_IGbQryFHgB_9>c^H8o`R^ Y hu59H6hLr(KUb"HUr=!']?vCR]\? "PEA}Wkn6+RN38Buu,CcizO:O+mTy{(m/I," $Ob_]O``y/4 ocwO1y!8n+Gb}$N6,,ajo|MHP@V/$R!g&t8/3;5+dESqU=T.3)L\X 5$k&Cdf8eS[G+WP cyh9Jks#,s;BPhgt!=]`,׷-#[ʳxd. ԲԩMyFQX! B;E|X U/Eȑ{8!~cqZ2 TY|z CJ#O~L_Ȭ;*!/a–:?\4yF KCHO >6~VHcI^(##E1bR:u 8-GW O; upۏYU98BW) /+ }{yƋW n~c`qj>4&ϭ z*QTZX/M 0Q6XiDa@RWUP[iyY I ]uG Z]gbjT[5VMn0UckQt.i@[,kiM*G#T nbAߕOn|)' =:0GwK&n2OZQ;I!HoQJr@F #pvj,g7@Y>f3YmY `ߨyܶeN[mTt[}{v+8tcHa.)5dqT CXVA4ʠ*RqV0~-S6e#iqHmwJٔr8>Vy{|TW} 0}88gl;W:kC+'2xҒ| z\+fܯ3 3Հm_ L:.P:X*pӪ9̈@*ܔ\J\Hw9Pw# bU_ MVA`E +_6pVD;s B. tu͢Qkbn]A75j()vۈ3fY]NDh?_vR1kXXiYEz[0xc?i buN٩mn^Kݠ!nw!}M'^[Cޓ|-gxM [/4*(=(]baXvmѿ0r CJ%\' Z/6$-L}k^䯰AK~] p .I`~)ypZކUkS[FУ.'! 9ҵ|'GR$_@ o%{ohZk{Y!*6؅%NFxHnH- D[RumN$raZo", lAyܗ.@l{P>KA Ҟ9VOPH}2B Č):622>P%Љ@n "ʴf+^M0t:a;BO؝^:%r|nu7~sk6'&%HOg&P(v0;܄4 /A;zzN9wmo[jSpZJP["{#pcdU%tى>+lڮ!t%m !g?4"k4K=̶FtIᶛ_l|;m9F5]=KL-_XQ3I{~&bPlS{A I+e1"N@UsAM!9R:Cf+0:2,\nL'5.b%;-hY_" IhPzR./(qJguk_jr / CLT& M_S 9:lpRp[_Re6W* ` nUf xzXl7M:<kv4tB#-Hwb^T0@.!^dmonyHbS(#B k5ko!kf2a?6#AJo}W@/Id&rP|b`iv!Kq}w 6#2I2Ye\)_bGP a'5-u4|rPF^p=?/ECXewA3#ju6mA W0.&3a0!q"S1Rl?3xyt%!Lsw}j'2Kd7^z_ ETX;rh b?7Vq1vy_<&KEo O@4n5?L9G1CBMDbQsl&_6SlE(Bxi `t;f^|sJK\S}f q&p~'i/UF~LE1nE\|wD# ,&"`p Vj6g*+FC:NMl-m[X QkaZq@vP![%6 6zM&g's&NjURz,!Dg  EvLv+i C?[DddzzNI6~\o^AklP8l1]\vXI Dp nYz?G`Jqn7=ZI dGnp2^ |A03q%qAt=EIPf=$nzY2I__lKfY`~|}U5L"M/vaFdY7LRSPZ.)sizU~DJC\ke1@o(+%,olbQBcb:HSb8'sM {jI @*vFmchU$ $Vwr (OQ9jfIei>s|r%AZ+j?m-=\v7"q\2#j@3(y )G F+amS: ,bx{v"x%@3AhmKsVZta T{U/*@Qt;sf)],ad,UN:WORQ;F|ld* ]_W?{w ]eBXqe$_wg4Te Dhko{`-^zgBW')rPF,WpE]g4E&5WYn>gCqz'- Jn#|U AH"e,+"y+W.h%_[,D -K6>9J{u#[@dM;wow%b7' #PC)/W@W;Qh{,N9>** ;{7;7z}^@p): J\sfQvg-Z])hx\2\HdeIzDVnn 'Wbpq3a)X\/0b_n6|ooom 614,[9iJxjxUc.s9)Q35#t0T-0"J4H#$rTZ'wj0 +"X.fEaqm J:HQSES*U_y@ReKJyO]X7Kgj UVhCL$xJn swB8w 1,``|rf4IuVy"0 ZZ-- d>>ze'NtI \NW=gsrT9mD6x7)fMC'/cGNFQ;t />\j' "1?@_X]5S'zs%B5}yOE=O(@;DrH9ae=7cLXD}T=QZq^CRwt`v'X^$F3bfn,Ms#$H .{Ju]KXXK'& r_0Og&+dB;%N|v8"c~|Yla(#8H$~</8f}}bjgC# A6W|%%K0dK 8e7G+E* UPQE("uLbvxUo%S`r'#IT~U\9$&h)\g^ CCw%#D<fjx}f,l QUCoeD\7l"- $ y?S4"L '<# d+8ChJ:{#, ZQlc *`rk8ed\{nl$]nk.fOq@F;)f0>1)zIV$TASYW*0-cY#Q >j1i\TR%D 6W$R_0qRl.=\;)@0g/KzA0~m{\ ]4u89`` Wj, bA9lV{U82tpbw%5-Fmm~$h])QvM;ou|%~{Yi;dqcTgC3>Z\S+G`Ky355_VQm=^B><O^/Ny-:_k@s4z.|'4zH?(0 0 f.3.DK0!ye)1Pg|tI<{eY5Sk1 19[ m!?@`A@xt9' 0Vc~]&jXJ9a!]F?d*{m8 L:ygJzX.XlTf6VoiqYZ mITsR.\st|5)Gt%i"L)nV'J[%FB\H|=HtiG&hb -A 8aRC#ZgTsW8U]CoR9zSC_RzWhqer{>2::8/2"]vHkRzPVtzuKP;yH^ax'#Ww4f 0;v-R_Q^R\/NyfPByl5_d"%+{_uxjmd"P%-'d;4t=:m4@m4[&r5],U]9s@(ho@zGJ!;fS cXf``'3oGi$H^*a   &LrxuIZK }wWMH<9wd d'Fl.r0}X('=f(>sH^g\>P296O- #LY]^Vi@_JWPbF\~q_&6%$&0D8^,X(uI=7NOJAh@eNw3P# ~FB:P *c G]T,D7Um6 AP} Wh Ս;sEdJrnX\ZHS/6'2\sHV@Xv]s|( Lglan{@{/X0f AULHw9 mfV!5dFGxAMW:V~K)X[nug^VId5fJ-d;X!g{7%nRxFF&o3Sy!O%-Z)mH*4K=~tK}'|5#y:z`IDzU*E.{ f=>QA c|-gJA`fv8X%B!=_8U$yvi[j L5m<3`4SZ,nA{2%[2H3 _,5y}(1y>MUxm6GN]PZXD,-JTRT"Q/=@o"qcTsG; jHAWr# DDBu!a>,nbs*aQ%i)% _.e]:LDXlA2zVbEM6b$v 1?$L5P3 L*GZT> 04MO>[,-TG|qU)][K %'Kazit Z@>B1aYx.dHl_^XqO;bU*]i|~`+{t2A2[5mXR"'?Z?H.Slrc0-!~P'F%hqT9?MtK FS >WX^_:Lf fCb(<1GF]^fU,VN{!\W@+;X[#F )g~`7YwF17\KdrVoNKpOY(5\QcVJynZuWccR0%KZ7_ xN5 JRY<8?k2o%YJYJu^/}+%4DV6 [qWJtfr[}X6`Ob^"0Kfx-b_Po .hFrgGnfH~~yq`Bg% DFH'o/*>"oPlze_9zwqG0'*SA#rd>d1$ -E"# L H3l-v[X: DXIan$9Je\@PihC"?Rwg, k66;?$qCTwFLf_k^!k!i 1CS o!65Q.WFo;EAvS$MU}R;OD'(~U3<7Y y29J0w\.MWWcm\i eh,gS ^UZ`C1QjTYUq9}RCI;dJ857a ,WEoc$<|@u!T=wR{P| ;tUl/5!Nj?w'K'Nr01[~+rTJ?a LeJ `AR.PM6|#))BKK~#, X<zJ.-)+mKh Z= l-"& RP|3`A5zi~">v5+pDx~0C'ai~r.iu9g'LdOIGXz+@a}pe}n 5n"!#k#b1;>E{<HECiSd7>$$py/ [_rv,{~/K} *IEG P JQ!s&$;BL Jm% B d\22fH'[ K'_MJrC!G3?k#gy+ 6F;f5MRx/<0l0VT '7Z]Dd/}BG5*]Q~e[ !*`Jt5F0_No B )\7 MW8^ [8f +rQx"|qX" @xm#M |2W8.`/ lA:SH3TZz/ST8*XWL0|]yl;=%0L4z &_4jfp `_`V =RfV@uAy@uBa5>> ~x;Jd=wTmA`.0)R 4\-6fl?m$-+\6Xw67(t'hg7mg`?<*uGdy/*b147%ZRD8)C)_A& 't /VF] :`bvY7PBFqwaF% 9l}x9eVybO+u-;(WheasiJs-k5(?mDQC+kVf$hQ] eKvzf:6_%u)]'e )A8Q&t'F8 ua?K ux $D~9k'>&f@8FU$od/Z-UCeFsX(v:KK]Np]X[rI{>vA{HPJ`V9$w"I#m^[!30^rT[3RgA/b$*1lVXliiZ= NRADN>OOkn,X- ^q }Q1DdX={ ^|A6=Nsk1A/;'fEu[ 1J$&OK[#H:~h//Fo O!5D58$ZN%TM/BQ q z[2aRY Orh [;QAe,fP;g<>-kh)168"`xe{[:f638vOew9+>E&*)ge1g'~vc8!Y|jB xQczz(N-+8Jd/?(sp0/>yx ;`qsD&s%K DG$Cb^H=cVSG6M:Y*)ggiz >69'W+'@{:hic9c$CYE] G v1XAR\T[E1BWjAZ:(:h!8ki+jD:ap821 $g*Q,,M[lQ0h&iIK*wWs8xH;'GRW65l WXWRUL*(}9Z'w4i~yr's"2x*>\LU^[)U2%C\T ~)J*-% 8xAB(c#,Z _/fPry s2eDTM;FDz3Ke4!ck T9^LQ3K9g4y w{-;Zz)j _sPvrDUg95=jx_ `;t((.N:jo%*s!3vL.!C67`1% #4$ikgMP :YRI"Ni88D<@MKi$|DRp66;t:N[Q>|,KKvo s2A w6[$,H kKJWMS}#E\9T"}M9K Z4j]I\~G# 2 h>6y"rhzn5[n?&[ghe:vC\l^wul|QC)h"xyTcJY_xtS)^zt?>GFIE!|7XP)-SaH>! b?.3HATF=23b3FJ4)ZAFJ k!$c VA7sny|*d:dlH;^bq\2Nw:v ^5z(s,Xoxs>oQQGG -l!p32L6CL1x!8m+4A!/v sC$C\*H_yT-Fdc+G]*_p pLu^EANE<_<#8e & x1=O7!qljmx  qp+'-Q8z&2@}8R 4-4~'_>~76y'J .pgW`>VNID~)Hk8M _ejq*vr. pzXT *Q!1dp&.U 6.%Z?{8U(-MJ=C?$g='<9XUJvJoaq.jSZu_r'bX 0oIlsjQ<~^A`gxBwll +q|T=-9"[r$ QF-C0=Q'6p )?dN( lbyegM\#=wi>j5OB(M_2,t(+jq)b$4*7%r~^ r|?k9~wfo^Y$8 Tc@lR""tM#LtRfQ~ & DyJDm{ lz2C< -Pa9 ,8;AW&//{GjyT!Ecup6$2A:I)m Yjqn/v[3*w;%;gZi>gD`?u,Oh o6EzRk!Lg+ X(,73_ chPJm14jXx)<-[~.]f{~>^pU\,r# AxhXw,ev3tU0~d|K]*JS1%pZqE]on6j?1.DUw emBH alCj^IW5n?46("va>nzCJgW>i993zj&xOF6 (*P| Ix?~3Sjb. rae;.kO)7;HJu>N9isXC` 1S5R`a:0g{^LT3KO.!F` jJ;];aIq=MH o*Ye3:L.DT>l RcIO/8">~H+0y "B6 `d'#o_Zgj/+W$qh3sv Uy8 #F gG\^RxsplzdDuY^6}DAR4g=, i*m:qJ~JGDvC6hz p&D oDT=*6wv'i>FPKGn=SCa'NQVewDiu~S"3K4UbLZ]B/:fB^jMV}\|Ap5p\EO39C ]H4LDb OL8TPq#x,2c)J.>)+ oC/,]d! &ml%jhc6P">=$Y::5}yY#&/=PE-M8h34IZb* NQ*&d!+bbLp" 1puWWWt)*`8-q0=^_hwMS.kgY+DJHXY3T+ &miQPf/U8NT+6'duNOO+[ 8V'O[m&-9{xZI\9;O`{ eR1W%cM}<"8+.ZvO~Vjk5 I960]h9FeH! ,+FZjG 2&,>~d=578s~ @> F&]qC MBS}C;>w:5M6+5F8v%A;5.gaJ\pQ+jn6R] > +.a,&/5/nLX}#_hdM9E160pWOH$3+e<2*t\zR*xNO ,D UT2pHBU]uu~+b'k,?}1 t ^,+dTxo0rJoO& os[`Ph Qc?nYsVH._yHZp=*l /Q(u%ZZd 1!A+ 1wyQ>QX$^{iy78/bfsfNg smc/ 0Qx0\]]zLUp[T 54j}H] O-hh =3>p{_;$%m9L+zveeY vEk6wx4\4\.=6$Y04zL hwoH* ) /kgV[#1MOO1, )3Hf&2;-}!v0 |JcQZo~PVuz&*%b~&j{JItj-o` SHTh*w9if/bf{r/ZSO%S-6z/SK&|tbh(Gy,)8k+vl?_*i>J`p~V",!(8M#aIIW'a6Vg'0g}^dk>sAu ?!JQ +b] ?o_@*VlC0 +/343"i5aqPv u~hX _QV$kcV~i)TK3%RJ;L- by\A V!kczZ\ $2 Y vB[vu/s^;e^$wclZnZPI4X}V._u#IA$j)l!IY na(w0G6\ogw#Dk|5jjBr 4#.b`h;lo# N S r' XW `nUvP \x0v tkgAzv_D_wCgjj5D\)@ssg ^spfU'c%Ps3,g2FR|6|UFu;iP. At'@s} z =JZd[ *3~fm^@9'TA{ Cw6]?i=DML,.)P9MO+8 xSy }78R #je ~]S!y  I WW%? m8'r=YwD '/Ut&#YTN :azW& +q^ J}D?d:Q6z6xPGO7G xj 71g+I!dY]LXjmyAcqH4[pZeVkuU{qWK[6NUM au.f'~* el c4|o0]\,a7zANEU}5Us\qs` <>ubZ`'S?S\4zp%O ytPlX?9Ui)3s.zAk#|NU{e\>\Kbw3*Tt\MT0]TyuHg r?O1 )@ ,d#CH+y@x4>7d8ApxHyrmfdo"b GgTxEB|-7 |s*bj[G,m"@YT9/Y nvXH$=y)X%Q6c@ )`: "3\NjP =6M^a[1jQpk1q5hn[Pobz|)hSF,kGE]H:Fm\aS?|r=jesU$4%U-c7+5?#(N8 ~-y-6r ;zXUj [:=hV#`mw1@@ d(|LuZ5m#!c!ME'=l?@l=Eq1h8F?9)8vGf tVNBL?,b^u]MphkgjE O})C+%OC@]$w6ii:DTW/a jo$FaE^ 'oh^m?Mz "6nr =_ew 91Zt@VlxnG#"9JV7Hr mwW8MIE-1$Ylh; xw%j.^}W@ o}f&eLS ' H4}3(,$.r+?E].9)_V[u-C3 F^h pH*QcN\Sd8:1O`~Tq+_"q^S5TRoLVk]6IPAG+ ].XQE'5]dj;N U-OTja5)T%7Q&j $E06$s|[`To 7W>#imZ P6t]a$trY}3Wz-g|Grd1!trV@^#;-\"VtU/~ {`PU*(c*nPESd6b0j:1;VD=/g!5^>&7+lbs1{)Pud-PWN+` ^%lX 8+aM\'xEUzk(|g1Wz Kj{4O-!Xo5NVXVA!_,(Y'yIx)R Gc}[B8,j<~HEl@y,f^||%W7a:L"eE0=MhNO3`q<`A)y `uU4[RLT'Fgtje, t,* 4 8veX2EP<2Jab|OR=!XAJEA$2JGL9u o>Vt