mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2162 lines
57 KiB
2162 lines
57 KiB
#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 <stdio.h>
|
|
#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.
|
|
**
|
|
** <tur 17-Jun-93> 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 */
|
|
|
|
|
|
/* <tur> 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 */
|
|
|
|
|
|
/* <tur 17-Jun-93> 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 */
|