#include "insignia.h" #include "host_def.h" /* * SoftPC Revision 2.0 * * File: : keybd_io.c * * Title : Bios Keyboard Interface function * * Sccs ID : @(#)keybd_io.c 1.35 06/27/95 * * Description : This package contains a group of functions that provide * a logical keyboard interface: * * keyboard_init() Initialise the keyboard interface. * keyboard_int() Deal with a character from the keyboard * and place them in the BIOS buffer. * keyboard_io() User routine to read characters from * the BIOS buffer. * bios_buffer_size() How many chars in the buffer ? * * Author : Rod Macgregor / Henry Nash * * Modified : Jon Eyre / Jim Hatfield / Uncle Tom Cobbley and all * * Modfications : This module is now designed to be totally portable, it * represents both the hardware and user interrupt interfaces. * These two functions are provided by the routines * keyboard_int & keyboard_io. The system will initialise * itself by a call to keyboard_init. * * The user is expected to supply the following host dependent * routines for this module, tagged as follows:- * * [HOSTSPECIFIC] * * host_alarm(duration) * long int duration ; * - ring the host's bell. * * host_kb_init() - any local initialisations required when * keyboard_init is called. * * Removed calls to cpu_sw_interrupt and replaced with * host_simulate * */ #ifdef SCCSID static char SccsID[]="@(#)keybd_io.c 1.35 06/27/95 Copyright Insignia Solutions Ltd."; #endif #ifdef SEGMENTATION /* * The following #include specifies the code segment into which this * module will by placed by the MPW C compiler on the Mac II running * MultiFinder. */ #include "SOFTPC_BIOS.seg" #endif /* * O/S include files. */ #include #include TypesH #include TimeH /* * SoftPC include files */ #include "xt.h" #include CpuH #include "sas.h" #include "bios.h" #include "ios.h" #include "ppi.h" #include "keyboard.h" #include "timeval.h" #include "timer.h" #include "keyba.h" #include "ica.h" #ifndef PROD #include "trace.h" #endif #include "debug.h" #include "idetect.h" /* * ============================================================================ * External routines * ============================================================================ */ /* * ============================================================================ * Local static data and defines * ============================================================================ */ #define SHIFT_KEY_SIZE 8 #define ALT_TABLE_SIZE 36 /* * lookup table to check if the scan code received is a shift key */ static sys_addr shift_keys; /* * corresponding table to 'shift_keys' to set relevant bits in masks when * shift scan code received */ static sys_addr shift_masks; /* * next two tables give values of chars when control key depressed. First * table (ctl_n_table) is for main keyboard values and second (ctl_f_table) * is for the function keys and keypad. */ static sys_addr ctl_n_table; static sys_addr ctl_f_table; /* * values of ascii keys dependiing on shift or caps states */ static sys_addr lowercase; static sys_addr uppercase; /* * remapping of some keys when alt key depressed. note 1st ten are for * keypad entries. */ static sys_addr alt_table; /* Add variables for all these entry points instead of the previously used * defines. This allows modification of these entry points from a loaded * driver, when the Insignia bios may not be in the loaded in the default * or assumed location. */ #if defined(NTVDM) && defined(LOCAL) /* * Make static fns and globals visible to win32 debuggers */ #undef LOCAL #define LOCAL #endif #ifndef GISP_SVGA LOCAL word int15_seg = RCPU_INT15_SEGMENT, int15_off = RCPU_INT15_OFFSET; LOCAL word int1b_seg = KEYBOARD_BREAK_INT_SEGMENT, int1b_off = KEYBOARD_BREAK_INT_OFFSET; LOCAL word int05_seg = PRINT_SCREEN_INT_SEGMENT, int05_off = PRINT_SCREEN_INT_OFFSET; LOCAL word rcpu_nop_segment = RCPU_NOP_SEGMENT, rcpu_nop_offset = RCPU_NOP_OFFSET; LOCAL word rcpu_poll_segment = RCPU_POLL_SEGMENT, rcpu_poll_offset = RCPU_POLL_OFFSET; #else /* GISP_SVGA */ /* If we are GISP_SVGA the segments will be variables anyway */ #define int15_seg RCPU_INT15_SEGMENT LOCAL word int15_off = RCPU_INT15_OFFSET; #define int1b_seg KEYBOARD_BREAK_INT_SEGMENT LOCAL word int1b_off = KEYBOARD_BREAK_INT_OFFSET; #define int05_seg PRINT_SCREEN_INT_SEGMENT LOCAL word int05_off = PRINT_SCREEN_INT_OFFSET; #define rcpu_nop_segment RCPU_NOP_SEGMENT LOCAL word rcpu_nop_offset = RCPU_NOP_OFFSET; #define rcpu_poll_segment RCPU_POLL_SEGMENT LOCAL word rcpu_poll_offset = RCPU_POLL_OFFSET; #endif /* GISP_SVGA */ #if defined(IRET_HOOKS) && defined(GISP_CPU) IMPORT VOID HostAllowKbdInt(); /* Allow keybd Ints without an IRET */ #endif /* IRET_HOOKS && GISP_CPU */ #ifdef NTVDM #include "error.h" GLOBAL word wait_int_seg = RCPU_WAIT_INT_SEGMENT; GLOBAL word wait_int_off = RCPU_WAIT_INT_OFFSET; GLOBAL word dr_type_seg = DR_TYPE_SEGMENT; GLOBAL word dr_type_off = DR_TYPE_OFFSET; GLOBAL sys_addr dr_type_addr = DR_TYPE_ADDR; /* Global var to indicate whether keyboard bios or hardware owns the keyboard mutex. */ GLOBAL BOOL bBiosOwnsKbdHdw; IMPORT ULONG WaitKbdHdw(ULONG dwTimeOut); IMPORT VOID HostReleaseKbd(); IMPORT VOID HostResetKbdNotFullEvent(); IMPORT VOID HostSetKbdNotFullEvent(); GLOBAL VOID TryKbdInt(VOID); IMPORT VOID ResumeTimerThread(VOID); IMPORT VOID WaitIfIdle(VOID); #define FREEKBDHDW() bBiosOwnsKbdHdw = \ ( bBiosOwnsKbdHdw ? HostReleaseKbd(), FALSE : FALSE ) /* for optimizing timer hardware interrupt generation defined in timer.c */ extern word TimerInt08Seg; extern word TimerInt08Off; extern word TimerInt1CSeg; extern word TimerInt1COff; extern word KbdInt09Seg; extern word KbdInt09Off; extern BOOL VDMForWOW; void Keyb16Request(half_word BopFnCode); /* optimizes 16 bit handler */ extern word *pICounter; extern word *pCharPollsPerTick; extern word *pShortIdle; extern word *pIdleNoActivity; extern half_word * stream_io_buffer; extern word * stream_io_dirty_count_ptr; extern word stream_io_buffer_size; extern sys_addr stream_io_bios_busy_sysaddr; #else #define FREEKBDHDW() /* Nothing for conventional SoftPC */ #endif /* NTVDM */ /* * Mix in global defined data as well. */ #ifndef GISP_SVGA GLOBAL word rcpu_int1C_seg = USER_TIMER_INT_SEGMENT; GLOBAL word rcpu_int1C_off = USER_TIMER_INT_OFFSET; GLOBAL word rcpu_int4A_seg = RCPU_INT4A_SEGMENT; GLOBAL word rcpu_int4A_off = RCPU_INT4A_OFFSET; #else /* GISP_SVGA */ /* For GISPSVGA the segs will already be variables */ #define rcpu_int1C_seg = USER_TIMER_INT_SEGMENT; GLOBAL word rcpu_int1C_off = USER_TIMER_INT_OFFSET; #define rcpu_int4A_seg = RCPU_INT4A_SEGMENT; GLOBAL word rcpu_int4A_off = RCPU_INT4A_OFFSET; #endif /* GISP_SVGA */ GLOBAL word dummy_int_seg = 0; GLOBAL word dummy_int_off = 0; #ifdef NTVDM GLOBAL word int13h_vector_off; GLOBAL word int13h_vector_seg; GLOBAL word int13h_caller_off; GLOBAL word int13h_caller_seg; #endif /* NTVDM */ #if defined(NTVDM) && defined(MONITOR) /* ** Microsoft special. ** These variables are set below in kb_setup_vectors(), to addresses ** passed by NTIO.SYS via BOP 5F -> MS_bop_F() -> kb_setup_vectors() ** Tim June 92. */ /* ** New ntio.sys variables for video ROM matching. Tim August 92. */ GLOBAL word int10_seg=0; GLOBAL word int10_caller=0; GLOBAL word int10_vector=0; /* Address of native int 10*/ GLOBAL word useHostInt10=0; /* var that chooses between host video ROM or BOPs */ GLOBAL word babyModeTable=0; /* Address of small mode table lives in ntio.sys */ GLOBAL word changing_mode_flag=0; /* ntio.sys var to indicate vid mode change */ GLOBAL word vga1b_seg = 0; GLOBAL word vga1b_off = 0; /* VGA capability table normally in ROM */ GLOBAL word conf_15_off = 0; GLOBAL word conf_15_seg = 0; /* INT15 config table normally in ROM */ void printer_setup_table(sys_addr table_addr); #endif /* NTVDM & MONITOR */ extern int soft_reset ; /* set for ctl-alt-dels */ /* * ============================================================================ * Local macros * ============================================================================ */ LOCAL VOID exit_from_kbd_int IPT0(); /* * Function to increment BIOS buffer pointers, returns new one */ LOCAL word inc_buffer_ptr IFN1(word, buf_p) { buf_p += 2; if (buf_p == sas_w_at_no_check(BIOS_KB_BUFFER_END)) buf_p = sas_w_at_no_check(BIOS_KB_BUFFER_START); return buf_p; } /* * ============================================================================ * Internal functions * ============================================================================ */ /* * Routine to translate scan code pairs for standard calls * Returns CF set if this scancode/char pair should be thrown away. */ LOCAL VOID translate_std IFN0() { IU8 ah, al; enum {dontSetCF, setCF0, setCF1} cfSet = dontSetCF; ah = getAH(); al = getAL(); if ( ah == 0xE0 ) /* is it keypad enter or / */ { if ( (al == 0x0D) || (al == 0x0A) ) setAH( 0x1C ); /* code is enter */ else setAH( 0x35 ); /* must be keypad ' / ' */ cfSet = setCF0; } else { if ( ah > 0x84 ) /* is it an extended one */ cfSet = setCF1; else { if( al == 0xF0 ) /* is it one of the 'fill ins' */ { if ( ah == 0) /* AH = 0 special case */ cfSet = setCF0; else cfSet = setCF1; /* Delete me */ } else { if ( (al == 0xE0) && (ah != 0) ) setAL( 0 ); /* convert to compatible output */ cfSet = setCF0; } } } if (cfSet != dontSetCF) setCF(cfSet == setCF1); } static void translate_ext() /* * Routine to translate scan code pairs for extended calls */ { if ( (getAL() == 0xF0 ) && (getAH() != 0) ) setAL( 0 ); } /* * Send command or data byte to the keyboard and await for the acknowledgemnt */ /* * Arbitrary retry limits - experiments suggest that we always succeed * on the first try in a pure SoftWindows. A "real keyboard" version may * be different. */ #define WAIT_RETRY 5 #define RESEND_RETRY 3 LOCAL VOID send_data IFN1(half_word, data) { int resend_retry; word CS_save, IP_save; half_word var_kb_flag_2; note_trace1(BIOS_KB_VERBOSE,"Cmd to kb i/o buff:0x%x",data); /* * Save CS:IP before calling a recursive CPU to handle the interrupt * from the keyboard */ CS_save = getCS(); IP_save = getIP(); /* * Set the retry flag ( KB_FE ) to force outb() at least once. If * we have real keyboard hardware this may get set again if the * hardware didn't understand the command for some reason e.g. * garbled by the serial line. */ var_kb_flag_2 = sas_hw_at(kb_flag_2) | KB_FE; resend_retry = RESEND_RETRY; do { IBOOL resend_command; int wait_retry; resend_command = (var_kb_flag_2 & KB_FE) != 0; wait_retry = WAIT_RETRY; /* Clear resend, acknowledge and error flags */ var_kb_flag_2 &= ~(KB_FE + KB_FA + KB_ERR); /* * Update Intel memory with cleared down flags *BEFORE* * the outb(), which may set the acknowledge flag, if we * execute enough Intel due to virtualisation. */ sas_store(kb_flag_2, var_kb_flag_2); /* Do the outb if necessary */ if( resend_command ) { outb(KEYBA_IO_BUFFERS, data); } /* Look for one of the flag bits to be set or time out */ while( !(var_kb_flag_2 & (KB_FA + KB_FE + KB_ERR)) && ( --wait_retry > 0 )) { /* * Process interrupt from kb. * * Note for perplexed keyboard debuggers: * Keyboard interrupts are delayed for a few * Intel instructions using quick events. This * means that the IRR from the above outb() may * not be raised until we have done the following * sub-CPU a few times. */ setCS(rcpu_nop_segment); setIP(rcpu_nop_offset); host_simulate(); /* Re-read flag byte to see if anything has happened */ var_kb_flag_2 = sas_hw_at(kb_flag_2); } /* If we got an acknowledge we've succeeded */ if (var_kb_flag_2 & KB_FA) break; /* Set up error flag (in case this is the last retry) */ note_trace0(BIOS_KB_VERBOSE,"failed to get ack ... retry"); var_kb_flag_2 |= KB_ERR; } while( --resend_retry > 0 ); if (var_kb_flag_2 & KB_ERR) { note_trace0(BIOS_KB_VERBOSE,"no more retrys"); /* Write back flags with error bit set */ sas_store(kb_flag_2, var_kb_flag_2); } setCS(CS_save); setIP(IP_save); } LOCAL VOID check_indicators IFN1(IBOOL, eoi) /* end of interrupt flag - if set to non-zero */ /* 0x20 is written to port 0x20 */ { half_word indicators ; half_word var_kb_flag_2; /* move switch indicators to bits 0-2 */ indicators = (sas_hw_at_no_check(kb_flag) & (CAPS_STATE + NUM_STATE + SCROLL_STATE)) >> 4; var_kb_flag_2 = sas_hw_at_no_check(kb_flag_2); /* compare with previous setting */ if ((indicators ^ var_kb_flag_2) & KB_LEDS) { /* check if update in progress */ if( (var_kb_flag_2 & KB_PR_LED) == 0) { /* No update in progress */ var_kb_flag_2 |= KB_PR_LED; sas_store_no_check(kb_flag_2, var_kb_flag_2); if (eoi) outb(0x20, 0x20); #if defined(NTVDM) || defined(GISP_CPU) /* * On the NT port we do not update the real kbd lights * so we don't need to do communicate with the kbd hdw (keyba.c) * * If this ever changes for the NT port then do not use * send_data which forces us to switch context back to * 16 bit and waits for a reply. Do this with a direct * call to the kbd Hdw * */ /* set kb flag up with new status */ var_kb_flag_2 = (var_kb_flag_2 & 0xf8) | indicators; sas_store_no_check(kb_flag_2, var_kb_flag_2); #ifdef NTVDM host_kb_light_on (indicators); #endif #ifdef GISP_CPU /* ** We do update an emulation of the keyboard lights but we don't ** want to do it via send_data and switching back to 16-bit. ** We call the host routines directly. */ host_kb_light_on (indicators); host_kb_light_off ((~indicators)&0x7); #endif /* GISP_CPU */ #else /* not NTVDM nor GISP_CPU */ send_data(LED_CMD); /* set kb flag up with new status */ var_kb_flag_2 = (sas_hw_at_no_check(kb_flag_2) & 0xf8) | indicators; sas_store_no_check(kb_flag_2, var_kb_flag_2); /* check error from previous send_data() */ if( (var_kb_flag_2 & KB_ERR) == 0) { /* No error */ send_data(indicators); /* test for error */ if(sas_hw_at_no_check(kb_flag_2) & KB_ERR) { /* error! */ note_trace0(BIOS_KB_VERBOSE,"got error sending change LEDs command"); send_data(KB_ENABLE); } } else /* error! */ send_data(KB_ENABLE); #endif /* NTVDM or GISP_CPU */ /* turn off update indicator and error flag */ sas_store_no_check (kb_flag_2, sas_hw_at_no_check(kb_flag_2) & ~(KB_PR_LED + KB_ERR)); } } } /* * ============================================================================ * External functions * ============================================================================ */ /* ** called from hunter.c:do_hunter() ** tells hunter about the BIOS buffer size so it will not over fill ** the BIOS buffer ** Used in no Time Stamp mode only. ** ** Also useful in host paste code to make sure keys are not pasted in too ** fast. */ int bios_buffer_size IPT0() { word buffer_head, buffer_tail; buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); note_trace2( BIOS_KB_VERBOSE, "BIOS kbd buffer head=%d tail=%d", buffer_head, buffer_tail ); if( buffer_tail > buffer_head ) return( buffer_tail - buffer_head ); else return( buffer_head - buffer_tail ); } LOCAL VOID K26A IFN0() { /* Interrupt Return */ outb(0x20, 0x20); outb(KEYBA_STATUS_CMD, ENA_KBD); } LOCAL VOID K26 IFN0() { /* Reset last char H.C. flag */ sas_store_no_check(kb_flag_3, sas_hw_at_no_check(kb_flag_3) & ~(LC_E0 + LC_E1)); /* (same as K26A()) */ outb(0x20, 0x20); outb(KEYBA_STATUS_CMD, ENA_KBD); } #ifndef NTVDM LOCAL VOID INT15 IFN0() { word saveCS, saveIP; saveCS = getCS(); saveIP = getIP(); setCS(int15_seg); setIP(int15_off); host_simulate(); setCS(saveCS); setIP(saveIP); } #else /* NTVDM */ void INT15(void); word sp_int15_handler_seg = 0; word sp_int15_handler_off = 0; #endif /* NTVDM */ #ifndef NTVDM #define BEEP(message) always_trace0(message); \ host_alarm(250000L); \ K26A() #else /* NTVDM */ /* NTVDM code size is too large, change this often used macro * to a function, as the call overhead is not justified */ void BEEP(char *message) { note_trace0(BIOS_KB_VERBOSE,message); host_alarm(250000L); K26A(); } #endif /* NTVDM */ /* ** Tell ICA End of Interrupt has happened, the ICA will ** allow interupts to go off again. ** Call INT 15. ** Reenable the Keyboard serial line so Keyboard ** interrupts can go off. ** NOTE: ** this is different to the real BIOS. The real BIOS ** does ICA, Keyboard then INT 15, if we do that Keyboard ** interrupts occur too soon, during the INT 15 and blow the ** DOS stack. We effectively stop Keybd interrupts during the ** INT 15. ** ** Take a leaf outta NTVDM's book and make these ** functions rather than macros. (This reduced the size of keybd_io.c.o ** on the Mac from 38K to 12K!) After all, it isn't as if keyboards are ** highly speed sensitive! */ #ifndef NTVDM LOCAL VOID PutInBufferFunc IFN2(half_word, s, half_word, v) { word buffer_head, buffer_tail, buffer_ptr; buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_ptr = inc_buffer_ptr(/* from: */buffer_tail); if (buffer_ptr == buffer_head) { BEEP("BIOS keyboard buffer overflow"); } else { sas_store_no_check(BIOS_VAR_START + buffer_tail, v); sas_store_no_check(BIOS_VAR_START + buffer_tail+1, s); sas_storew_no_check(BIOS_KB_BUFFER_TAIL, buffer_ptr); outb(0x20, 0x20); setAX(0x9102); INT15(); outb(KEYBA_STATUS_CMD, ENA_KBD); sas_store (kb_flag_3, sas_hw_at(kb_flag_3) & ~(LC_E0 + LC_E1)); setIF(0); } exit_from_kbd_int(); } #else /* NTVDM */ /* NT's PutInBuffer seems to be slightly different to PutInBufferFunc above. */ /* So I'm Not Touching it! (Is this a good expansion of "NT"? :-) */ /* Our code size is too large, change this often used macro * to a function, as the call overhead is not justified */ void NtPutInBuffer(half_word s, half_word v) { word buffer_head, buffer_tail, buffer_ptr; buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_ptr = inc_buffer_ptr(/* from: */buffer_tail); if (buffer_ptr == buffer_head) { BEEP("BIOS keyboard buffer overflow"); } else { sas_store_no_check(BIOS_VAR_START + buffer_tail, v); sas_store_no_check(BIOS_VAR_START + buffer_tail+1, s); sas_storew_no_check(BIOS_KB_BUFFER_TAIL, buffer_ptr); setAX(0x9102); INT15(); K26(); setIF(0); } exit_from_kbd_int(); } #define PUT_IN_BUFFER(s, v) NtPutInBuffer(s,v); return #endif /* NTVDM */ /* Eurrgh; macros with embedded "return"s! */ #ifndef NTVDM #define PUT_IN_BUFFER(s, v) PutInBufferFunc(s,v); return #endif /* !NTVDM */ LOCAL VOID CheckAndPutInBufferFunc IFN2(half_word, s,half_word, v) { if ((s == 0xff) || (v == 0xff)) { K26(); exit_from_kbd_int(); } else { #ifndef NTVDM PutInBufferFunc(s, v); #else /* NTVDM */ NtPutInBuffer(s, v); #endif /* !NTVDM */ } } #define CHECK_AND_PUT_IN_BUFFER(s,v) CheckAndPutInBufferFunc(s, v); return LOCAL VOID PAUSE IFN0() { word CS_save; /* tmp. store for CS value */ word IP_save; /* tmp. store for IP value */ sas_store_no_check(kb_flag_1, sas_hw_at_no_check(kb_flag_1) | HOLD_STATE); outb(KEYBA_STATUS_CMD, ENA_KBD); outb(0x20, 0x20); CS_save = getCS(); IP_save = getIP(); FREEKBDHDW(); do { #if defined(IRET_HOOKS) && defined(GISP_CPU) HostAllowKbdInt(); /* Allow a keypress to generate an interrupt */ #endif /* IRET_HOOKS && GISP_CPU */ #if defined(NTVDM) IDLE_waitio(); #endif setCS(rcpu_nop_segment); setIP(rcpu_nop_offset); host_simulate(); } while (sas_hw_at_no_check(kb_flag_1) & HOLD_STATE); setCS(CS_save); setIP(IP_save); outb(KEYBA_STATUS_CMD, ENA_KBD); } #ifndef NTVDM static int re_entry_level=0; #endif /* ** All exits from keyboard_int() call this first. */ LOCAL void exit_from_kbd_int IFN0() { #ifndef NTVDM --re_entry_level; if( re_entry_level >= 4 ) always_trace1("ERROR: KBD INT bad exit level %d", re_entry_level); #endif note_trace0( BIOS_KB_VERBOSE, "KBD BIOS - END" ); setIF( 0 ); FREEKBDHDW(); /* JonLe NTVDM Mod */ } void keyboard_int IFN0() { int i; /* loop counter */ half_word code, /* scan code from keyboard */ code_save, /* tmp variable for above */ chr, /* ASCII char code */ last_kb_flag_3, /* kb_flag_3 saved */ mask; #ifdef NTVDM word IP_save, buffer_head, /* ptr. to head of kb buffer */ buffer_tail; /* ptr. to tail of kb buffer */ half_word BopFnCode; #endif /* NTVDM */ boolean upper; /* flag indicating upper case */ #ifdef NTVDM BopFnCode = getAH(); if (BopFnCode) { Keyb16Request(BopFnCode); return; } #endif #ifndef NTVDM ++re_entry_level; if( re_entry_level > 4 ){ always_trace1("ERROR: KBD BIOS re-entered at level %d\n", re_entry_level-1); } #endif setIF(0); note_trace0(BIOS_KB_VERBOSE,"KBD BIOS start"); #ifdef NTVDM /* JonLe keyboard mod */ bBiosOwnsKbdHdw = !WaitKbdHdw(5000); #endif /* NTVDM */ /* disable keyboard */ outb(KEYBA_STATUS_CMD, DIS_KBD); #ifdef NTVDM /* * CarbonCopy traces int 9 in order to gain control * over where the kbd data is coming from (the physical kbd * or the serial link) The kbd_inb instruction must be visible * in the 16 bit code via int 1 tracing, for CarbonCopy to work. * interrupts should be kept off. */ if (getTF()) { IP_save = getIP(); setIP(IP_save + 4); /* adavance by 4 bytes, pop ax, jmp iret_com */ host_simulate(); setIP(IP_save); code = getAL(); } else #endif inb(KEYBA_IO_BUFFERS, &code); /* get scan_code */ /* call recursive CPU to handle int 15 call */ setAH(0x4f); setAL(code); setCF(1); /* Default return says scan code NOT consumed - needed by Freelance Plus 3.01 */ INT15(); code = getAL(); /* suret int 15 function can change the scan code in AL */ if(!getCF()) /* check CF */ { K26(); exit_from_kbd_int();return; } if ( code == KB_RESEND ) /* check for resend */ { sas_store_no_check (kb_flag_2, sas_hw_at_no_check(kb_flag_2) | KB_FE); K26(); exit_from_kbd_int();return; } if( code == KB_ACK ) /* check for acknowledge */ { sas_store_no_check (kb_flag_2, sas_hw_at_no_check(kb_flag_2) | KB_FA); K26(); exit_from_kbd_int();return; } check_indicators(0); if ( code == KB_OVER_RUN ) /* test for overrun */ { BEEP("hardware keyboard buffer overflow"); exit_from_kbd_int();return; } last_kb_flag_3 = sas_hw_at_no_check(kb_flag_3); /* TEST TO SEE IF A READ_ID IS IN PROGRESS */ if ( last_kb_flag_3 & (RD_ID + LC_AB) ) { if ( sas_hw_at_no_check(kb_flag) & RD_ID ) /* is read_id flag on */ { if( code == ID_1 ) /* is this the 1st ID char. */ sas_store_no_check (kb_flag_3, sas_hw_at_no_check(kb_flag_3) | LC_AB); sas_store_no_check (kb_flag_3, sas_hw_at_no_check(kb_flag_3) & ~RD_ID); } else { sas_store_no_check (kb_flag_3, sas_hw_at_no_check(kb_flag_3) & ~LC_AB); if( code != ID_2A ) /* is this the 2nd ID char. */ { if( code == ID_2 ) { /* should we set NUM LOCK */ if( last_kb_flag_3 & SET_NUM_LK ) { sas_store_no_check (kb_flag, sas_hw_at_no_check(kb_flag) | NUM_STATE); check_indicators(1); } } else { K26(); exit_from_kbd_int();return; } } sas_store_no_check (kb_flag_3, sas_hw_at_no_check(kb_flag_3) | KBX); /* enhanced kbd found */ } K26(); exit_from_kbd_int();return; } if( code == MC_E0 ) /* general marker code? */ { sas_store_no_check(kb_flag_3, sas_hw_at_no_check(kb_flag_3) | ( LC_E0 + KBX )); K26A(); exit_from_kbd_int();return; } if( code == MC_E1 ) /* the pause key ? */ { sas_store_no_check (kb_flag_3, sas_hw_at_no_check (kb_flag_3) | ( LC_E1 + KBX )); K26A(); exit_from_kbd_int();return; } code_save = code; /* turn off break bit */ code &= 0x7f; if( last_kb_flag_3 & LC_E0) /* last code=E0 marker? */ { /* is it one of the shift keys */ if( code == sas_hw_at_no_check(shift_keys+6) || code == sas_hw_at_no_check(shift_keys+7) ) { K26(); exit_from_kbd_int();return; } } else if( last_kb_flag_3 & LC_E1 ) /* last code=E1 marker? */ { /* is it alt, ctl or one of the shift keys */ if( code == sas_hw_at_no_check(shift_keys+4) || code == sas_hw_at_no_check(shift_keys+5) || code == sas_hw_at_no_check(shift_keys+6) || code == sas_hw_at_no_check(shift_keys+7) ) { K26A(); exit_from_kbd_int();return; } if( code == NUM_KEY ) /* is it the pause key */ { /* is it the break or are we paused already */ if( (code_save & 0x80) || (sas_hw_at_no_check(kb_flag_1) & HOLD_STATE) ) { K26(); exit_from_kbd_int();return; } PAUSE(); exit_from_kbd_int();return; } } /* TEST FOR SYSTEM KEY */ else if( code == SYS_KEY ) { if( code_save & 0x80 ) /* check for break code */ { sas_store_no_check(kb_flag_1, sas_hw_at_no_check(kb_flag_1) & ~SYS_SHIFT); K26A(); /* call recursive CPU to call INT 15 */ setAX(0x8501); INT15(); exit_from_kbd_int();return; } if( sas_hw_at_no_check(kb_flag_1) & SYS_SHIFT) /* Sys key held down ? */ { K26(); exit_from_kbd_int();return; } sas_store_no_check (kb_flag_1, sas_hw_at_no_check(kb_flag_1) | SYS_SHIFT); K26A(); /* call recursive CPU to call INT 15 */ setAX(0x8500); INT15(); exit_from_kbd_int();return; } /* TEST FOR SHIFT KEYS */ for( i=0; i < SHIFT_KEY_SIZE; i++) if ( code == sas_hw_at_no_check(shift_keys+i) ) break; code = code_save; if( i < SHIFT_KEY_SIZE ) /* is there a match */ { mask = sas_hw_at_no_check (shift_masks+i); if( code & 0x80 ) /* test for break key */ { if (mask >= SCROLL_SHIFT) /* is this a toggle key */ { sas_store_no_check (kb_flag_1, sas_hw_at_no_check(kb_flag_1) & ~mask); K26(); exit_from_kbd_int();return; } sas_store_no_check (kb_flag, sas_hw_at_no_check(kb_flag) & ~mask); /* turn off shift bit */ if( mask >= CTL_SHIFT) /* alt or ctl ? */ { if( sas_hw_at_no_check (kb_flag_3) & LC_E0 ) /* 2nd alt or ctl ? */ sas_store_no_check (kb_flag_3, sas_hw_at_no_check(kb_flag_3) & ~mask); else sas_store_no_check (kb_flag_1, sas_hw_at_no_check(kb_flag_1) & ~(mask >> 2)); sas_store_no_check (kb_flag, sas_hw_at_no_check(kb_flag) | ((((sas_hw_at_no_check(kb_flag) >>2 ) | sas_hw_at(kb_flag_1)) << 2) & (ALT_SHIFT + CTL_SHIFT))); } if(code != (ALT_KEY + 0x80)) /* alt shift release ? */ { K26(); exit_from_kbd_int();return; } code = sas_hw_at_no_check(alt_input); if ( code == 0 ) /* input == 0 ? */ { K26(); exit_from_kbd_int();return; } sas_store_no_check(alt_input, 0); /* Zero the ALT_INPUT char */ /* At this point, the ALT input char (now in "code") should be put in the buffer. */ PUT_IN_BUFFER(0, code); #ifdef NTVDM return; #endif } /* SHIFT MAKE FOUND, DETERMINE SET OR TOGGLE */ if( mask < SCROLL_SHIFT ) { sas_store_no_check (kb_flag, sas_hw_at_no_check(kb_flag) | mask); if ( mask & (CTL_SHIFT + ALT_SHIFT) ) { if( sas_hw_at_no_check(kb_flag_3) & LC_E0 ) /* one of the new keys ?*/ sas_store_no_check(kb_flag_3, sas_hw_at_no_check(kb_flag_3) | mask); /* set right, ctl alt */ else sas_store_no_check (kb_flag_1,sas_hw_at_no_check(kb_flag_1) | (mask >> 2)); /* set left, ctl alt */ } K26(); exit_from_kbd_int();return; } /* TOGGLED SHIFT KEY, TEST FOR 1ST MAKE OR NOT */ if( (sas_hw_at_no_check(kb_flag) & CTL_SHIFT) == 0 ) { if( code == INS_KEY ) { if( sas_hw_at_no_check(kb_flag) & ALT_SHIFT ) goto label1; if( (sas_hw_at_no_check(kb_flag_3) & LC_E0) == 0 ) /* the new insert key ? */ { /* only continue if NUM_STATE flag set OR one or both of the shift flags */ if( ((sas_hw_at_no_check(kb_flag) & (NUM_STATE + LEFT_SHIFT + RIGHT_SHIFT)) == NUM_STATE) || (((sas_hw_at_no_check(kb_flag) & NUM_STATE) == 0) && (sas_hw_at_no_check(kb_flag) & (LEFT_SHIFT + RIGHT_SHIFT))) ) goto label1; } } /* shift toggle key hit */ if( mask & sas_hw_at_no_check(kb_flag_1) ) /* already depressed ? */ { K26(); exit_from_kbd_int();return; } sas_store_no_check (kb_flag_1, sas_hw_at_no_check(kb_flag_1) | mask); /* set and toggle flags */ sas_store_no_check ( kb_flag, sas_hw_at_no_check(kb_flag) ^ mask); if( mask & (CAPS_SHIFT + NUM_SHIFT + SCROLL_SHIFT) ) check_indicators(1); if( code == INS_KEY ) /* 1st make of ins key */ goto label2; K26(); exit_from_kbd_int();return; } } label1: /* TEST FOR HOLD STATE */ if( code & 0x80 ) /* test for break */ { K26(); exit_from_kbd_int();return; } if( sas_hw_at_no_check(kb_flag_1) & HOLD_STATE ) /* in hold state ? */ { if( code != NUM_KEY ) sas_store_no_check (kb_flag_1, sas_hw_at_no_check(kb_flag_1) & ~HOLD_STATE); K26(); exit_from_kbd_int();return; } label2: /* NOT IN HOLD STATE */ if( code > 88) /* out of range ? */ { K26(); exit_from_kbd_int();return; } /* are we in alternate shift */ if( (sas_hw_at_no_check(kb_flag) & ALT_SHIFT) && ( ((sas_hw_at_no_check(kb_flag_3) & KBX) == 0) || ((sas_hw_at_no_check(kb_flag_1) & SYS_SHIFT) == 0) ) ) { /* TEST FOR RESET KEY SEQUENCE (CTL ALT DEL) */ if( (sas_hw_at_no_check(kb_flag) & CTL_SHIFT ) && (code == DEL_KEY) ) { #ifndef NTVDM reboot(); #else K26(); #endif exit_from_kbd_int();return; } /* IN ALTERNATE SHIFT, RESET NOT FOUND */ if( code == SPACEBAR ) { PUT_IN_BUFFER(code, ' '); } if( code == TAB_KEY ) { PUT_IN_BUFFER(0xa5, 0 ); /* special code for alt-tab */ } if( (code == KEY_PAD_MINUS) || (code == KEY_PAD_PLUS) ) { PUT_IN_BUFFER(code, 0xf0); /* special ascii code */ } /* LOOK FOR KEYPAD ENTRY */ for (i = 0; i < 10; i++ ) if ( code == sas_hw_at_no_check (alt_table+i) ) break; if( i < 10 ) { if( sas_hw_at_no_check(kb_flag_3) & LC_E0 ) /* one of the new keys ? */ { PUT_IN_BUFFER((code + 80), 0 ); } sas_store_no_check (alt_input, sas_hw_at_no_check(alt_input) * 10 + i); K26(); exit_from_kbd_int();return; } /* LOOK FOR SUPERSHIFT ENTRY */ for( i = 10; i < ALT_TABLE_SIZE; i++) if( code == sas_hw_at_no_check (alt_table+i)) break; if( i < ALT_TABLE_SIZE ) { PUT_IN_BUFFER(code, 0 ); } /* LOOK FOR TOP ROW OF ALTERNATE SHIFT */ if( code < TOP_1_KEY ) { CHECK_AND_PUT_IN_BUFFER(code, 0xf0); /* must be escape */ } if( code < BS_KEY ) { PUT_IN_BUFFER((code + 118), 0); } /* TRANSLATE ALTERNATE SHIFT PSEUDO SCAN CODES */ if((code == F11_M) || (code == F12_M) ) /* F11 or F12 */ { PUT_IN_BUFFER((code + 52), 0 ); } if( sas_hw_at_no_check(kb_flag_3) & LC_E0 ) /* one of the new keys ?*/ { if( code == KEY_PAD_ENTER ) { PUT_IN_BUFFER(0xa6, 0); } if( code == DEL_KEY ) { PUT_IN_BUFFER(( code + 80), 0 ); } if( code == KEY_PAD_SLASH ) { PUT_IN_BUFFER(0xa4, 0); } K26(); exit_from_kbd_int();return; } if( code < F1_KEY ) { CHECK_AND_PUT_IN_BUFFER(code, 0xf0); } if( code <= F10_KEY ) { PUT_IN_BUFFER( (code + 45), 0 ); } K26(); exit_from_kbd_int();return; } /* NOT IN ALTERNATE SHIFT */ if(sas_hw_at_no_check(kb_flag) & CTL_SHIFT) /* control shift ? */ { if( (code == SCROLL_KEY) && ( ((sas_hw_at_no_check(kb_flag_3) & KBX) == 0) || (sas_hw_at_no_check(kb_flag_3) & LC_E0) ) ) { /* reset buffer to empty */ sas_storew_no_check(BIOS_KB_BUFFER_TAIL, sas_w_at_no_check(BIOS_KB_BUFFER_HEAD)); sas_store (bios_break, 0x80); /* turn on bios brk bit */ outb(KEYBA_STATUS_CMD, ENA_KBD); /* enable keyboard */ FREEKBDHDW(); /* JonLe NTVDM mod */ exec_sw_interrupt(int1b_seg, int1b_off); PUT_IN_BUFFER(0, 0); } /* TEST FOR PAUSE */ if( ((sas_hw_at_no_check(kb_flag_3) & KBX) == 0) && (code == NUM_KEY)) { PAUSE(); exit_from_kbd_int();return; } /* TEST SPECIAL CASE KEY 55 */ if( code == PRINT_SCR_KEY ) { if ( ((sas_hw_at_no_check(kb_flag_3) & KBX) == 0) || (sas_hw_at_no_check(kb_flag_3) &LC_E0) ) { PUT_IN_BUFFER(0x72, 0); } } else { if( code != TAB_KEY ) { if( (code == KEY_PAD_SLASH) && (sas_hw_at_no_check(kb_flag_3) & LC_E0) ) { PUT_IN_BUFFER(0x95, 0 ); } if( code < F1_KEY ) /* is it in char table? */ { if( sas_hw_at_no_check(kb_flag_3) & LC_E0) { CHECK_AND_PUT_IN_BUFFER(MC_E0, sas_hw_at_no_check(ctl_n_table+code - 1) ); } else { CHECK_AND_PUT_IN_BUFFER(code, sas_hw_at_no_check(ctl_n_table+code - 1) ); } } } } chr = ( sas_hw_at_no_check(kb_flag_3) & LC_E0 ) ? MC_E0 : 0; CHECK_AND_PUT_IN_BUFFER(sas_hw_at_no_check(ctl_n_table+code - 1), chr); } /* NOT IN CONTROL SHIFT */ if( code <= CAPS_KEY ) { if( code == PRINT_SCR_KEY ) { if( ((sas_hw_at_no_check(kb_flag_3) & (KBX + LC_E0)) == (KBX + LC_E0)) || ( ((sas_hw_at_no_check(kb_flag_3) & KBX) == 0) && (sas_hw_at_no_check(kb_flag) & (LEFT_SHIFT + RIGHT_SHIFT))) ) { /* print screen */ outb(KEYBA_STATUS_CMD, ENA_KBD); outb(0x20, 0x20); FREEKBDHDW(); /* JonLe NTVDM Mod */ exec_sw_interrupt(int05_seg, int05_off); sas_store_no_check (kb_flag_3, sas_hw_at_no_check(kb_flag_3)& ~(LC_E0 + LC_E1)); exit_from_kbd_int();return; } } else { if( ((sas_hw_at_no_check(kb_flag_3) & LC_E0) == 0) || (code != KEY_PAD_SLASH)) { for( i = 10; i < ALT_TABLE_SIZE; i++ ) if(code == sas_hw_at_no_check(alt_table+i)) break; /* did we find one */ upper = FALSE; if( (i < ALT_TABLE_SIZE) && (sas_hw_at_no_check(kb_flag) & CAPS_STATE) ) { if( (sas_hw_at_no_check(kb_flag) & (LEFT_SHIFT + RIGHT_SHIFT)) == 0 ) upper = TRUE; } else { if( sas_hw_at_no_check(kb_flag) & (LEFT_SHIFT + RIGHT_SHIFT) ) upper = TRUE; } if (upper) { /* translate to upper case */ if( sas_hw_at_no_check(kb_flag_3) & LC_E0) { CHECK_AND_PUT_IN_BUFFER(MC_E0, sas_hw_at_no_check(uppercase+code - 1) ); } else { CHECK_AND_PUT_IN_BUFFER(code, sas_hw_at_no_check (uppercase+code - 1) ); } } } } /* translate to lower case */ if( sas_hw_at_no_check(kb_flag_3) & LC_E0) { CHECK_AND_PUT_IN_BUFFER(MC_E0, sas_hw_at_no_check (lowercase+code - 1) ); } else { CHECK_AND_PUT_IN_BUFFER(code, sas_hw_at_no_check(lowercase+code - 1) ); } } /* TEST FOR KEYS F1 - F10 */ /* 7.10.92 MG AND TEST FOR F11 AND F12 !!!! We were shoving the code for shift-F11 or shift-F12 in if you pressed unshifted keys. This has been changed so that all the function keys are handled the same way, which is the correct procedure. */ if( code > F10_KEY && (code != F11_KEY && code != F12_KEY) ) { if( code > DEL_KEY ) { if (code == WT_KEY ) { if ( sas_hw_at_no_check(kb_flag) & (LEFT_SHIFT + RIGHT_SHIFT) ) { /* translate to upper case */ if( sas_hw_at_no_check(kb_flag_3) & LC_E0) { CHECK_AND_PUT_IN_BUFFER(MC_E0, sas_hw_at_no_check(uppercase+code - 1) ); } else { CHECK_AND_PUT_IN_BUFFER(code, sas_hw_at_no_check(uppercase+code - 1) ); } } else { /* translate to lower case */ if( sas_hw_at_no_check(kb_flag_3) & LC_E0) { CHECK_AND_PUT_IN_BUFFER(MC_E0, sas_hw_at_no_check(lowercase+code - 1) ); } else { CHECK_AND_PUT_IN_BUFFER(code, sas_hw_at_no_check(lowercase+code - 1) ); } } } else { if( (code == 76) && ((sas_hw_at_no_check(kb_flag) & (LEFT_SHIFT + RIGHT_SHIFT)) == 0)) { PUT_IN_BUFFER( code, 0xf0); } /* translate for pseudo scan codes */ chr = ( sas_hw_at_no_check(kb_flag_3) & LC_E0 ) ? MC_E0 : 0; /* Should this always be upper case ???? */ CHECK_AND_PUT_IN_BUFFER(sas_hw_at_no_check (uppercase+code - 1), chr); } } if ( (code == KEY_PAD_MINUS) || (code == KEY_PAD_PLUS) || ( !(sas_hw_at_no_check(kb_flag_3) & LC_E0) && ( ((sas_hw_at_no_check(kb_flag) & (NUM_STATE + LEFT_SHIFT + RIGHT_SHIFT)) == NUM_STATE) || (((sas_hw_at_no_check(kb_flag) & NUM_STATE) == 0) && (sas_hw_at_no_check(kb_flag) & (LEFT_SHIFT + RIGHT_SHIFT))) ) ) ) { /* translate to upper case */ if( sas_hw_at_no_check(kb_flag_3) & LC_E0) { CHECK_AND_PUT_IN_BUFFER(MC_E0, sas_hw_at_no_check(uppercase+code - 1) ); } else { CHECK_AND_PUT_IN_BUFFER(code, sas_hw_at_no_check(uppercase+code - 1) ); } } } else { if( sas_hw_at_no_check(kb_flag) & (LEFT_SHIFT + RIGHT_SHIFT) ) { /* translate for pseudo scan codes */ chr = ( sas_hw_at_no_check(kb_flag_3) & LC_E0 ) ? MC_E0 : 0; CHECK_AND_PUT_IN_BUFFER(sas_hw_at_no_check(uppercase+code - 1), chr); } } if ( code == 76 ) { PUT_IN_BUFFER(code, 0xf0 ); } /* translate for pseudo scan codes */ chr = ( sas_hw_at_no_check(kb_flag_3) & LC_E0 ) ? MC_E0 : 0; CHECK_AND_PUT_IN_BUFFER(sas_hw_at_no_check(lowercase+code - 1), chr); } /* end of keyboard_int() AT version */ void kb_idle_poll() { /* * this routine is called from bios assembler routines to * cause an idle poll to occur. */ IDLE_poll(); } #ifdef NTVDM /* * Ntvdm has a 16-bit int 16 handler * it requires a few services for idle * detection from softpc... * */ void keyboard_io() { switch (getAH()) { /* * The 16 bit thread has not reached idle status yet * but it is polling the kbd, so do some brief waits. */ case 0: WaitIfIdle(); #ifndef NTVDM if (!WaitKbdHdw(0)) { TryKbdInt(); HostReleaseKbd(); } #endif /* NTVDM */ break; /* * App wants to idle, so consult idle algorithm */ case 1: IDLE_poll(); break; /* * App is starting a waitio */ case 2: IDLE_waitio(); break; /* * update the keyboard lights, */ case 3: host_kb_light_on (getAL()); break; } } #else void keyboard_io() { /* * Request to keyboard. The AH register holds the type of request: * * AH == 0 Read an character from the queue - wait if no * character available. Return character in AL * and the scan code in AH. * * AH == 1 Determine if there is a character in the queue. * Set ZF = 0 if there is a character and return * it in AX (but leave it in the queue). * * AH == 2 Return shift state in AL. * * For AH = 0 to 2, the value returned in AH is zero. This correction * made in r2.69. * * NB : The order of reference/increment of buffer_head is critical to * ensure we do not upset put_in_buffer(). * * * XT-SFD BIOS Extended functions: * * AH == 5 Place ASCII char/scan code pair (CL / CH) * into tail of keyboard buffer. Return 0 in * AL if successful, 1 if buffer already full. * * AH == 0x10 Extended read for enhanced keyboard. * * AH == 0x11 Extended function 1 for enhanced keyboard. * * AH == 0x12 Extended shift status. AL contains kb_flag, * AH has bits for left/right Alt and Ctrl keys * from kb_flag_1 and kb_flag_3. */ word buffer_head, /* local copy of BIOS data area variable*/ buffer_tail, /* local copy of BIOS data area variable*/ buffer_ptr; /* pointer into BIOS keyboard buffer */ #define INT16H_DEC 0x12 /* AH decremented by this if invalid command */ word CS_save, /* CS before recursive CPU call */ IP_save; /* IP before recursive CPU call */ half_word data, /* byte conyaining typamatic data */ status1, /* temp variables used for storing */ status2; /* status in funtion 0x12 */ INT func_index; /* func_index == AH */ setZF(0); func_index = (INT)getAH(); note_trace1( BIOS_KB_VERBOSE, "Keyboard BIOS func 0x%x", func_index); switch (func_index) { case 0x00: /* Read next char in Kbd buffer */ /* * The AT emulation of the BIOS uses a recursive CPU to handle * the HW interrrupts, so there is no need to set the Zero Flag * and return to our CPU (see original xt version ) */ buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); if (buffer_tail == buffer_head) { IDLE_waitio(); setAX(0x9002); INT15(); /* call int 15h - wait function */ } do { check_indicators(0); /* see if LED's need updating */ buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); if (buffer_tail == buffer_head) { CS_save = getCS(); IP_save = getIP(); /* wait for character in buffer */ do { IDLE_poll(); setCS(rcpu_poll_segment); setIP(rcpu_poll_offset); host_simulate(); buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); } while (buffer_tail == buffer_head); setCS(CS_save); setIP(IP_save); } setAX(sas_w_at_no_check(BIOS_VAR_START + buffer_head)); buffer_head = inc_buffer_ptr(/* from: */buffer_head); sas_storew_no_check(BIOS_KB_BUFFER_HEAD, buffer_head); translate_std(); /*translate scan_code pairs */ } while (getCF()); /* if CF set throw code away and start again */ setIF(1); IDLE_init(); break; case 0x01: /* Set ZF to reflect char availability in Kbd buffer */ do { check_indicators(1); /* see if LED's need updating */ /* and issue an out 20h,20h */ buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); setAX(sas_w_at_no_check(BIOS_VAR_START + buffer_head)); if (buffer_tail == buffer_head) { /* buffer empty - set flag and return */ IDLE_poll(); setZF(1); break; } else IDLE_init(); translate_std(); /* translate scan_code pairs, returns CF if throwaway */ if(getCF()) { /* throw code away by incrementing pointer */ buffer_head = inc_buffer_ptr(/* from: */buffer_head); sas_storew_no_check(BIOS_KB_BUFFER_HEAD, buffer_head); } } while (getCF()); /* if CF set - start again */ setIF(1); break; case 0x02: /* AL := Current Shift Status (really "kb_flag") */ setAH(0); setAL(sas_hw_at_no_check(kb_flag)); break; case 0x03: /* Alter typematic rate */ /* check for correct values in registers */ if( (getAL() == 5) && !(getBX() & 0xfce0) ) { note_trace1(BIOS_KB_VERBOSE, "\talter typematic rate (BX %#x)\n", getBX()); send_data(KB_TYPA_RD); data = (getBH() << 5) | getBL(); send_data(data); } break; case 0x05: /* Place ASCII + ScanCode in Kbd Buffer */ buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); /* * check for sufficient space - if no set AL */ buffer_ptr = inc_buffer_ptr(/*from:*/buffer_tail); if( buffer_head == buffer_ptr ) setAL( 1 ); else { /* * load CX into buffer and update buffer_tail */ sas_storew_no_check(BIOS_VAR_START + buffer_tail, getCX() ); sas_storew_no_check(BIOS_KB_BUFFER_TAIL, buffer_ptr); setAL( 0 ); } setAH( 0 ); setIF(1); break; case 0x10: /* Extended ASCII Read */ buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); if (buffer_tail == buffer_head) { IDLE_waitio(); setAX(0x9002); INT15(); /* call int 15h - wait function */ } check_indicators(0); /* see if LED's need updating */ buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); if (buffer_tail == buffer_head) { CS_save = getCS(); IP_save = getIP(); /* wait for character in buffer */ while (buffer_tail == buffer_head) { IDLE_poll(); setCS(rcpu_poll_segment); setIP(rcpu_poll_offset); host_simulate(); buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); } IDLE_init(); setCS(CS_save); setIP(IP_save); } setAX(sas_w_at_no_check(BIOS_VAR_START + buffer_head)); /* Pickup the "current" scancode/char pair */ buffer_head = inc_buffer_ptr(/* from: */buffer_head); sas_storew_no_check(BIOS_KB_BUFFER_HEAD, buffer_head); translate_ext(); /* translate scan_code pairs */ setIF(1); break; case 0x11: /* Extended ASCII Status */ check_indicators(1); /* see if LED's need updating */ /* and issue an out 20h,20h */ buffer_head = sas_w_at_no_check(BIOS_KB_BUFFER_HEAD); buffer_tail = sas_w_at_no_check(BIOS_KB_BUFFER_TAIL); setAX(sas_w_at_no_check(BIOS_VAR_START + buffer_head)); if (buffer_tail == buffer_head) /* No keys pressed */ { setZF(1); IDLE_poll(); } else /* Keystrokes available! */ { translate_ext(); /* translate scan_code pairs */ IDLE_init(); } setIF(1); break; case 0x12: /* Extended Shift Status */ status1 = sas_hw_at_no_check(kb_flag_1) & SYS_SHIFT; /* only leave SYS KEY */ status1 <<= 5; /* move to bit 7 */ status2 = sas_hw_at_no_check(kb_flag_1) & 0x73; /* remove SYS_SHIFT, HOLD, STATE and INS_SHIFT */ status1 |= status2; /* merge */ status2 = sas_hw_at_no_check(kb_flag_3) & 0x0C; /* remove LC_E0 & LC_E1 */ status1 |= status2; /* merge */ setAH( status1 ); setAL( sas_hw_at_no_check(kb_flag) ); break; default: setAH((func_index - INT16H_DEC)); break; } } #endif #ifdef SEGMENTATION /* * The following #include specifies the code segment into which this * module will by placed by the MPW C compiler on the Mac II running * MultiFinder. */ #include "SOFTPC_INIT.seg" #endif void keyboard_post() { /* Set up BIOS keyboard variables */ /* Initialize the keyboard table pointers */ shift_keys = K6; shift_masks = K7; ctl_n_table = K8; ctl_f_table = K9; lowercase = K10; uppercase = K11; alt_table = K30; sas_storew_no_check(BIOS_KB_BUFFER_HEAD, BIOS_KB_BUFFER); sas_storew_no_check(BIOS_KB_BUFFER_TAIL, BIOS_KB_BUFFER); sas_storew_no_check(BIOS_KB_BUFFER_START, BIOS_KB_BUFFER); sas_storew_no_check(BIOS_KB_BUFFER_END, BIOS_KB_BUFFER + 2*BIOS_KB_BUFFER_SIZE); /* The following are #defines, referring to locations in BIOS */ /* data area. */ sas_store_no_check (kb_flag,NUM_STATE); sas_store_no_check (kb_flag_1,0); sas_store_no_check (kb_flag_2,2); sas_store_no_check (kb_flag_3,KBX); sas_store_no_check (alt_input,0); } void keyboard_init() { /* ** host specific keyboard initialisation ** is now before AT base keyboard initialisation */ host_kb_init(); #if defined(CPU_40_STYLE) && !defined (NTVDM) ica_iret_hook_control(ICA_MASTER, CPU_KB_INT, TRUE); #endif } #ifdef NTVDM /*:::::::::::::::::::::::::::::::::::::::::::::::: Map in new keyboard tables */ /*::::::::::::::::::::::::::::::::::::::::::::::::::::: Set interrupt vectors */ /* ** The Microsoft NTIO.SYS calls this func via BOP 5F to pass ** interesting addresses to our C BIOS. */ #if defined(MONITOR) IMPORT UTINY getNtScreenState IPT0(); #endif void kb_setup_vectors(void) { word KbdSeg, w; word *pkio_table; double_word phy_base; KbdSeg = getDS(); pkio_table = (word *) effective_addr(getCS(), getSI()); /* IDLE variables */ sas_loadw((sys_addr)(pkio_table + 12), &w); pICounter = (word *) (Start_of_M_area + ((KbdSeg<<4)+w)); pCharPollsPerTick = (word *) (Start_of_M_area + ((KbdSeg<<4)+w+4)); pMinConsecutiveTicks = (word *) (Start_of_M_area + ((KbdSeg<<4)+w+8)); #if defined(MONITOR) phy_base = (double_word)KbdSeg << 4; /* key tables */ shift_keys = phy_base + *pkio_table++; shift_masks = phy_base + *pkio_table++; ctl_n_table = phy_base + *pkio_table++; ctl_f_table = phy_base + *pkio_table++; lowercase = phy_base + *pkio_table++; uppercase = phy_base + *pkio_table++; alt_table = phy_base + *pkio_table++; dummy_int_seg = KbdSeg; /* dummy int, iret routine */ dummy_int_off = *pkio_table++; int05_seg = KbdSeg; /* print screen caller */ int05_off = *pkio_table++; int15_seg = KbdSeg; /* int 15 caller */ int15_off = *pkio_table++; rcpu_nop_segment = KbdSeg; /* cpu nop */ rcpu_nop_offset = *pkio_table++; sp_int15_handler_seg = KbdSeg; /* int 15 handler */ sp_int15_handler_off = *pkio_table++; pkio_table++; /* iDle variables, done above */ rcpu_int4A_seg = KbdSeg; rcpu_int4A_off = *pkio_table++; /* real time clock */ int1b_seg = KbdSeg; /* kbd break handler */ int1b_off = *pkio_table++; int10_seg = KbdSeg; int10_caller = *pkio_table++; int10_vector = *pkio_table++; /* ** Address of data in keyboard.sys, Tim August 92. ** ** useHostInt10 is a one byte variable. 1 means use host video BIOS, ** (ie. full-screen), 0 means use SoftPC video BIOS (ie. windowed). ** babyModeTable is a mini version of the table in ROM that contains ** all the VGA register values for all the modes. The keyboard.sys ** version only has two entries; for 40 column text mode and 80 ** column text mode. */ useHostInt10 = *pkio_table++; sas_store_no_check((sys_addr)(phy_base + useHostInt10), getNtScreenState()); babyModeTable = (int10_seg << 4) + *pkio_table++; changing_mode_flag = *pkio_table++; /* indicates trying to change vid mode*/ /* Initialise printer status table. */ printer_setup_table(effective_addr(KbdSeg, *pkio_table++)); wait_int_off = *pkio_table++; wait_int_seg = KbdSeg; dr_type_seg = KbdSeg; dr_type_off = *pkio_table++; dr_type_addr = (sys_addr)dr_type_seg * 16L + (sys_addr)dr_type_off; vga1b_seg = KbdSeg; vga1b_off = *pkio_table++; /* VGA capability table (normally lives in ROM) */ conf_15_seg = KbdSeg; conf_15_off = *pkio_table++; /* INT15 config table (normally in ROM) */ TimerInt08Seg = KbdSeg; TimerInt08Off = *pkio_table++; int13h_vector_seg = KbdSeg; int13h_caller_seg = KbdSeg; int13h_vector_off = *pkio_table++; int13h_caller_off = *pkio_table++; stream_io_buffer_size = *pkio_table++; stream_io_buffer = (half_word *)effective_addr(*pkio_table++, 0); stream_io_dirty_count_ptr = (word *)effective_addr(KbdSeg, *pkio_table++); stream_io_bios_busy_sysaddr = effective_addr(KbdSeg, *pkio_table++); #ifndef PROD if (*pkio_table != getAX()) { always_trace0("ERROR: KbdVectorTable!"); } #endif TimerInt1CSeg = KbdSeg; TimerInt1COff = dummy_int_off; #else /* ndef MONITOR */ /* kbd bios callout optimization */ sas_loadw(0x15*4, &sp_int15_handler_off); sas_loadw(0x15*4 + 2, &sp_int15_handler_seg); /* timer hardware interrupt optimizations */ sas_loadw(0x08*4, &TimerInt08Off); sas_loadw(0x08*4 + 2, &TimerInt08Seg); sas_loadw(0x1C*4, &TimerInt1COff); sas_loadw(0x1C*4 + 2, &TimerInt1CSeg); sas_loadw(0x13 * 4, &int13h_vector_off); sas_loadw(0x13 * 4 + 2, &int13h_vector_seg); int13h_caller_seg = KbdSeg; dr_type_seg = KbdSeg; sas_loadw((sys_addr)(pkio_table + 27), &int13h_caller_off); sas_loadw((sys_addr)(pkio_table + 22), &dr_type_off); dr_type_addr = effective_addr(dr_type_seg, dr_type_off); #endif /* MONITOR */ sas_loadw(0x09*4, &KbdInt09Off); sas_loadw(0x09*4 + 2, &KbdInt09Seg); ResumeTimerThread(); } /*::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Int15 caller */ /* * Gives chance for other parts of NTVDM to * update the kbd i15 kbd callout optimization */ void UpdateKbdInt15(word Seg,word Off) { word int15Off, int15Seg; /* make sure nobody has hooked since the last time */ /* we stored the i15 vector */ sas_loadw(0x15*4 , &int15Off); sas_loadw(0x15*4 + 2, &int15Seg); if(int15Off != sp_int15_handler_off || int15Seg != sp_int15_handler_seg) { #ifndef PROD printf("NTVDM: UpdateKbdInt15 Nuking I15 offsets\n"); #endif sp_int15_handler_off = sp_int15_handler_seg = 0; return; } sp_int15_handler_off = Off; sp_int15_handler_seg = Seg; } IMPORT void (*BIOS[])(); void INT15(void) { ULONG ul; word CS_save, IP_save; word int15Off, int15Seg; /*:::::::::::::::::::::::::::::::::: Get location of current 15h handler */ sas_loadw(0x15*4 , &int15Off); sas_loadw(0x15*4 + 2, &int15Seg); /*:::::::::::::::::::::: Does the 15h vector point to the softpc handler */ ul = (ULONG)getAH(); if((ul == 0x4f || ul == 0x91) && int15Off == sp_int15_handler_off && int15Seg == sp_int15_handler_seg) { (BIOS[0x15])(); /* Call int15 handler defined in base */ } else { /*::::::::::::::::::::::::::::::::::::::::::::::: Call int15 handler */ ul = (ULONG)bBiosOwnsKbdHdw; if (bBiosOwnsKbdHdw) { bBiosOwnsKbdHdw = FALSE; HostReleaseKbd(); } CS_save = getCS(); /* Save current CS,IP settings */ IP_save = getIP(); setCS(int15_seg); setIP(int15_off); host_simulate(); /* Call int15 handler */ setCS(CS_save); /* Restore CS,IP */ setIP(IP_save); if (ul) bBiosOwnsKbdHdw = !WaitKbdHdw(5000); } } /* * 32 bit services for kb16.com, the international 16 bit * interrupt 9 service handler. * */ void Keyb16Request(half_word BopFnCode) { /* * upon entry to kb16, take ownership of kbd * disable the kbd * disable interrupts */ if (BopFnCode == 1) { bBiosOwnsKbdHdw = !WaitKbdHdw(5000); outb(KEYBA_STATUS_CMD, DIS_KBD); setIF(1); } /* K26A type exit from i9 handler */ else if (BopFnCode == 2) { if (getBH()) { /* bl == do beep */ host_alarm(250000L); } outb(0x20, 0x20); /* eoi */ if (getBL()) { /* got character ? do device post */ setAX(0x9102); INT15(); } outb(KEYBA_STATUS_CMD, ENA_KBD); if (bBiosOwnsKbdHdw) { bBiosOwnsKbdHdw = FALSE; HostReleaseKbd(); } } /* K27A exit notify */ else if (BopFnCode == 3) { outb(0x20, 0x20); outb(KEYBA_STATUS_CMD, ENA_KBD); if (bBiosOwnsKbdHdw) { bBiosOwnsKbdHdw = FALSE; HostReleaseKbd(); } } /* K27A exit notify */ else if (BopFnCode == 4) { outb(KEYBA_STATUS_CMD, ENA_KBD); if (bBiosOwnsKbdHdw) { bBiosOwnsKbdHdw = FALSE; HostReleaseKbd(); } } } #endif /* NTVDM */