/* * Copyright (C) 2002-2004 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $Id: bios.cpp,v 1.35 2004/08/04 09:12:56 qbix79 Exp $ */ #include #include "dosbox.h" #include "bios.h" #include "regs.h" #include "cpu.h" #include "callback.h" #include "inout.h" #include "mem.h" #include "pic.h" #include "joystick.h" #include "dos_inc.h" #include "mouse.h" static Bitu call_int1a,call_int11,call_int8,call_int17,call_int12,call_int15,call_int1c; static Bitu call_int1,call_int70,call_int14; static Bit16u size_extended; static Bitu INT70_Handler(void) { /* Acknowledge irq with cmos */ IO_Write(0x70,0xc); IO_Read(0x71); if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) { Bit32u count=mem_readd(BIOS_WAIT_FLAG_COUNT); if (count>997) { mem_writed(BIOS_WAIT_FLAG_COUNT,count-997); } else { mem_writed(BIOS_WAIT_FLAG_COUNT,0); PhysPt where=Real2Phys(mem_readd(BIOS_WAIT_FLAG_POINTER)); mem_writeb(where,mem_readb(where)|0x80); mem_writeb(BIOS_WAIT_FLAG_ACTIVE,0); mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP)); IO_Write(0x70,0xb); IO_Write(0x71,IO_Read(0x71)&~0x40); } } /* Signal EOI to both pics */ IO_Write(0xa0,0x20); IO_Write(0x20,0x20); return 0; } static Bitu INT1A_Handler(void) { switch (reg_ah) { case 0x00: /* Get System time */ { Bit32u ticks=mem_readd(BIOS_TIMER); reg_al=0; /* Midnight never passes :) */ reg_cx=(Bit16u)(ticks >> 16); reg_dx=(Bit16u)(ticks & 0xffff); break; } case 0x01: /* Set System time */ mem_writed(BIOS_TIMER,(reg_cx<<16)|reg_dx); break; case 0x02: /* GET REAL-TIME CLOCK TIME (AT,XT286,PS) */ IO_Write(0x70,0x04); //Hours reg_ch=IO_Read(0x71); IO_Write(0x70,0x02); //Minutes reg_cl=IO_Read(0x71); IO_Write(0x70,0x00); //Seconds reg_dh=IO_Read(0x71); reg_dl=0; //Daylight saving disabled CALLBACK_SCF(false); break; case 0x04: /* GET REAL-TIME ClOCK DATA (AT,XT286,PS) */ reg_dx=0; reg_cx=0x2003; CALLBACK_SCF(false); LOG(LOG_BIOS,LOG_ERROR)("INT1A:04:Faked RTC get date call"); break; // reg_dx=reg_cx=0; // CALLBACK_SCF(false); // LOG(LOG_BIOS,LOG_ERROR)("INT1A:04:Faked RTC get date call"); // break; case 0x80: /* Pcjr Setup Sound Multiplexer */ LOG(LOG_BIOS,LOG_ERROR)("INT1A:80:Setup tandy sound multiplexer to %d",reg_al); break; case 0x81: /* Tandy sound system checks */ if (machine!=MCH_TANDY) break; reg_ax=0xc4; CALLBACK_SCF(false); break; /* INT 1A - Tandy 2500, Tandy 1000L series - DIGITAL SOUND - INSTALLATION CHECK AX = 8100h Return: AL > 80h if supported AX = 00C4h if supported (1000SL/TL) CF set if sound chip is busy CF clear if sound chip is free Note: the value of CF is not definitive; call this function until CF is clear on return, then call AH=84h"Tandy" */ case 0xb1: /* PCI Bios Calls */ LOG(LOG_BIOS,LOG_ERROR)("INT1A:PCI bios call %2X",reg_al); CALLBACK_SCF(true); break; default: LOG(LOG_BIOS,LOG_ERROR)("INT1A:Undefined call %2X",reg_ah); } return CBRET_NONE; } static Bitu INT11_Handler(void) { reg_ax=mem_readw(BIOS_CONFIGURATION); return CBRET_NONE; } static Bitu INT8_Handler(void) { /* Increase the bios tick counter */ mem_writed(BIOS_TIMER,mem_readd(BIOS_TIMER)+1); /* decrease floppy motor timer */ Bit8u val = mem_readb(BIOS_DISK_MOTOR_TIMEOUT); if (val>0) mem_writeb(BIOS_DISK_MOTOR_TIMEOUT,val-1); /* and running drive */ mem_writeb(BIOS_DRIVE_RUNNING,mem_readb(BIOS_DRIVE_RUNNING) & 0xF0); // Save ds,dx,ax Bit16u oldds = SegValue(ds); Bit16u olddx = reg_dx; Bit16u oldax = reg_ax; // run int 1c CALLBACK_RunRealInt(0x1c); // restore old values SegSet16(ds,oldds); reg_dx = olddx; reg_ax = oldax; return CBRET_NONE; }; static Bitu INT1C_Handler(void) { return CBRET_NONE; }; static Bitu INT12_Handler(void) { reg_ax=mem_readw(BIOS_MEMORY_SIZE); return CBRET_NONE; }; static Bitu INT17_Handler(void) { LOG(LOG_BIOS,LOG_NORMAL)("INT17:Function %X",reg_ah); switch(reg_ah) { case 0x00: /* PRINTER: Write Character */ reg_ah=1; /* Report a timeout */ break; case 0x01: /* PRINTER: Initialize port */ break; case 0x02: /* PRINTER: Get Status */ reg_ah=0; break; case 0x20: /* Some sort of printerdriver install check*/ break; default: E_Exit("Unhandled INT 17 call %2X",reg_ah); }; return CBRET_NONE; } static Bitu INT14_Handler(void) { switch (reg_ah) { case 0x00: /* Init port */ { Bitu port=real_readw(0x40,reg_dx*2); reg_ah=IO_ReadB(port+5); reg_al=IO_ReadB(port+6); LOG_MSG("AX %X DX %X",reg_ax,reg_dx); } break; default: LOG_MSG("Unhandled INT 14 call %2X",reg_ah); } return CBRET_NONE; } static Bitu INT15_Handler(void) { static Bitu biosConfigSeg=0; switch (reg_ah) { case 0x06: LOG(LOG_BIOS,LOG_NORMAL)("INT15 Unkown Function 6"); break; case 0xC0: /* Get Configuration*/ { if (biosConfigSeg==0) biosConfigSeg = DOS_GetMemory(1); //We have 16 bytes PhysPt data = PhysMake(biosConfigSeg,0); mem_writew(data,8); // 3 Bytes following mem_writeb(data+2,0xFC); // Model ID mem_writeb(data+3,0x00); // Submodel ID mem_writeb(data+4,0x01); // Bios Revision mem_writeb(data+5,(1<<6)|(1<<5)|(1<<4));// Feature Byte 1 mem_writeb(data+6,(1<<6)); // Feature Byte 2 mem_writeb(data+7,0); // Feature Byte 3 mem_writeb(data+8,0); // Feature Byte 4 mem_writeb(data+9,0); // Feature Byte 4 CPU_SetSegGeneral(es,biosConfigSeg); reg_bx = 0; reg_ah = 0; CALLBACK_SCF(false); }; break; case 0x4f: /* BIOS - Keyboard intercept */ /* Carry should be set but let's just set it just in case */ CALLBACK_SCF(true); break; case 0x83: /* BIOS - SET EVENT WAIT INTERVAL */ { if(reg_al == 0x01) LOG(LOG_BIOS,LOG_WARN)("Bios set event interval cancelled: not handled"); if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) { reg_ah=0x80; CALLBACK_SCF(true); break; } Bit32u count=(reg_cx<<16)|reg_dx; mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(SegValue(es),reg_bx)); mem_writed(BIOS_WAIT_FLAG_COUNT,count); mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1); /* Reprogram RTC to start */ IO_Write(0x70,0xb); IO_Write(0x71,IO_Read(0x71)|0x40); CALLBACK_SCF(false); } break; case 0x84: /* BIOS - JOYSTICK SUPPORT (XT after 11/8/82,AT,XT286,PS) */ if (reg_dx==0x0000) { // Get Joystick button status if (JOYSTICK_IsEnabled(0) || JOYSTICK_IsEnabled(1)) { reg_al = (JOYSTICK_GetButton(0,0)<<7)|(JOYSTICK_GetButton(0,1)<<6); reg_al |= (JOYSTICK_GetButton(1,0)<<5)|(JOYSTICK_GetButton(1,1)<<4); CALLBACK_SCF(false); } else { // dos values reg_ax = 0x00f0; reg_dx = 0x0201; CALLBACK_SCF(true); } } else if (reg_dx==0x0001) { if (JOYSTICK_IsEnabled(0) || JOYSTICK_IsEnabled(1)) { reg_ax = (Bit16u)JOYSTICK_GetMove_X(0); reg_bx = (Bit16u)JOYSTICK_GetMove_Y(0); reg_cx = (Bit16u)JOYSTICK_GetMove_X(1); reg_dx = (Bit16u)JOYSTICK_GetMove_Y(1); CALLBACK_SCF(false); } else { reg_ax=reg_bx=reg_cx=reg_dx=0; CALLBACK_SCF(true); } } else { LOG(LOG_BIOS,LOG_ERROR)("INT15:84:Unknown Bios Joystick functionality."); } break; case 0x86: /* BIOS - WAIT (AT,PS) */ { //TODO Perhaps really wait :) Bit32u micro=(reg_cx<<16)|reg_dx; if (mem_readb(BIOS_WAIT_FLAG_ACTIVE)) { reg_ah=0x83; CALLBACK_SCF(true); break; } Bit32u count=(reg_cx<<16)|reg_dx; mem_writed(BIOS_WAIT_FLAG_POINTER,RealMake(0,BIOS_WAIT_FLAG_TEMP)); mem_writed(BIOS_WAIT_FLAG_COUNT,count); mem_writeb(BIOS_WAIT_FLAG_ACTIVE,1); /* Reprogram RTC to start */ IO_Write(0x70,0xb); IO_Write(0x71,IO_Read(0x71)|0x40); while (mem_readd(BIOS_WAIT_FLAG_COUNT)) { CALLBACK_Idle(); } CALLBACK_SCF(false); } case 0x87: /* Copy extended memory */ { bool enabled = MEM_A20_Enabled(); MEM_A20_Enable(true); Bitu bytes = reg_cx * 2; PhysPt data = SegPhys(es)+reg_si; PhysPt source = mem_readd(data+0x12) & 0x00FFFFFF + (mem_readb(data+0x16)<<24); PhysPt dest = mem_readd(data+0x1A) & 0x00FFFFFF + (mem_readb(data+0x1E)<<24); MEM_BlockCopy(dest,source,bytes); reg_ax = 0x00; MEM_A20_Enable(enabled); CALLBACK_SCF(false); break; } case 0x88: /* SYSTEM - GET EXTENDED MEMORY SIZE (286+) */ reg_ax=size_extended; LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function 0x88 Remaining %04X kb",reg_ax); CALLBACK_SCF(false); break; case 0x89: /* SYSTEM - SWITCH TO PROTECTED MODE */ { IO_Write(0x20,0x10);IO_Write(0x21,reg_bh);IO_Write(0x21,0); IO_Write(0xA0,0x10);IO_Write(0xA1,reg_bl);IO_Write(0xA1,0); MEM_A20_Enable(true); PhysPt table=SegPhys(es)+reg_si; CPU_LGDT(mem_readw(table+0x8),mem_readd(table+0x8+0x2) & 0xFFFFFF); CPU_LIDT(mem_readw(table+0x10),mem_readd(table+0x10+0x2) & 0xFFFFFF); CPU_SET_CRX(0,CPU_GET_CRX(0)|1); CPU_SetSegGeneral(ds,0x18); CPU_SetSegGeneral(es,0x20); CPU_SetSegGeneral(ss,0x28); reg_sp+=6; //Clear stack of interrupt frame CPU_SetFlags(0,FMASK_ALL); reg_ax=0; CPU_JMP(false,0x30,reg_cx,0); } break; case 0x90: /* OS HOOK - DEVICE BUSY */ CALLBACK_SCF(false); reg_ah=0; break; case 0x91: /* OS HOOK - DEVICE POST */ CALLBACK_SCF(false); reg_ah=0; break; case 0xc3: /* set carry flag so BorlandRTM doesn't assume a VECTRA/PS2 */ reg_ah=0x86; CALLBACK_SCF(true); break; case 0xc2: /* BIOS PS2 Pointing Device Support */ switch (reg_al) { case 0x00: // enable/disable if (reg_bh==0) { // disable Mouse_SetPS2State(false); reg_ah=0; CALLBACK_SCF(false); } else if (reg_bh==0x01) { //enable Mouse_SetPS2State(true); reg_ah=0; CALLBACK_SCF(false); } else CALLBACK_SCF(true); break; case 0x01: // reset reg_bx=0x00aa; // mouse CALLBACK_SCF(false); break; case 0x02: // set sampling rate CALLBACK_SCF(false); reg_ah=0; break; case 0x03: // set resolution CALLBACK_SCF(false); reg_ah=0; break; case 0x04: // get type reg_bh=0; // ID CALLBACK_SCF(false); reg_ah=0; break; case 0x05: // initialize CALLBACK_SCF(false); reg_ah=0; break; case 0x06: // extended commands if ((reg_bh==0x01) || (reg_bh==0x02)) { CALLBACK_SCF(false); reg_ah=0;} else CALLBACK_SCF(true); break; case 0x07: // set callback Mouse_ChangePS2Callback(SegValue(es),reg_bx); CALLBACK_SCF(false); break; default: CALLBACK_SCF(true); break; } break; case 0xc4: /* BIOS POS Programm option Select */ LOG(LOG_BIOS,LOG_NORMAL)("INT15:Function %X called, bios mouse not supported",reg_ah); CALLBACK_SCF(true); break; default: LOG(LOG_BIOS,LOG_ERROR)("INT15:Unknown call %4X",reg_ax); reg_ah=0x86; CALLBACK_SCF(false); } return CBRET_NONE; } static Bitu INT1_Single_Step(void) { static bool warned=false; if (!warned) { warned=true; LOG(LOG_CPU,LOG_NORMAL)("INT 1:Single Step called"); } return CBRET_NONE; } void BIOS_ZeroExtendedSize(void) { size_extended=0; } void BIOS_SetupKeyboard(void); void BIOS_SetupDisks(void); void BIOS_Init(Section* sec) { MSG_Add("BIOS_CONFIGFILE_HELP","Nothing to setup yet!\n"); /* Clear the Bios Data Area */ for (Bit16u i=0;i<1024;i++) real_writeb(0x40,i,0); /* Setup all the interrupt handlers the bios controls */ /* INT 8 Clock IRQ Handler */ //TODO Maybe give this a special callback that will also call int 8 instead of starting //a new system call_int8=CALLBACK_Allocate(); CALLBACK_Setup(call_int8,&INT8_Handler,CB_IRET); phys_writeb(CB_BASE+(call_int8<<4)+0,(Bit8u)0xFE); //GRP 4 phys_writeb(CB_BASE+(call_int8<<4)+1,(Bit8u)0x38); //Extra Callback instruction phys_writew(CB_BASE+(call_int8<<4)+2,call_int8); //The immediate word phys_writeb(CB_BASE+(call_int8<<4)+4,(Bit8u)0x50); // push ax phys_writeb(CB_BASE+(call_int8<<4)+5,(Bit8u)0xb0); // mov al, 0x20 phys_writeb(CB_BASE+(call_int8<<4)+6,(Bit8u)0x20); phys_writeb(CB_BASE+(call_int8<<4)+7,(Bit8u)0xe6); // out 0x20, al phys_writeb(CB_BASE+(call_int8<<4)+8,(Bit8u)0x20); phys_writeb(CB_BASE+(call_int8<<4)+9,(Bit8u)0x58); // pop ax phys_writeb(CB_BASE+(call_int8<<4)+10,(Bit8u)0xcf); // iret mem_writed(BIOS_TIMER,0); //Calculate the correct time RealSetVec(0x8,CALLBACK_RealPointer(call_int8)); /* INT 11 Get equipment list */ call_int11=CALLBACK_Allocate(); CALLBACK_Setup(call_int11,&INT11_Handler,CB_IRET); RealSetVec(0x11,CALLBACK_RealPointer(call_int11)); /* INT 12 Memory Size default at 640 kb */ call_int12=CALLBACK_Allocate(); CALLBACK_Setup(call_int12,&INT12_Handler,CB_IRET); RealSetVec(0x12,CALLBACK_RealPointer(call_int12)); mem_writew(BIOS_MEMORY_SIZE,640); /* INT 13 Bios Disk Support */ BIOS_SetupDisks(); call_int14=CALLBACK_Allocate(); CALLBACK_Setup(call_int14,&INT14_Handler,CB_IRET); RealSetVec(0x14,CALLBACK_RealPointer(call_int14)); /* INT 15 Misc Calls */ call_int15=CALLBACK_Allocate(); CALLBACK_Setup(call_int15,&INT15_Handler,CB_IRET); RealSetVec(0x15,CALLBACK_RealPointer(call_int15)); /* INT 16 Keyboard handled in another file */ BIOS_SetupKeyboard(); /* INT 16 Printer Routines */ call_int17=CALLBACK_Allocate(); CALLBACK_Setup(call_int17,&INT17_Handler,CB_IRET); RealSetVec(0x17,CALLBACK_RealPointer(call_int17)); /* INT 1A TIME and some other functions */ call_int1a=CALLBACK_Allocate(); CALLBACK_Setup(call_int1a,&INT1A_Handler,CB_IRET_STI); RealSetVec(0x1A,CALLBACK_RealPointer(call_int1a)); /* INT 1C System Timer tick called from INT 8 */ call_int1c=CALLBACK_Allocate(); CALLBACK_Setup(call_int1c,&INT1C_Handler,CB_IRET); RealSetVec(0x1C,CALLBACK_RealPointer(call_int1c)); /* IRQ 8 RTC Handler */ call_int70=CALLBACK_Allocate(); CALLBACK_Setup(call_int70,&INT70_Handler,CB_IRET); RealSetVec(0x70,CALLBACK_RealPointer(call_int70)); /* Some defeault CPU error interrupt handlers */ call_int1=CALLBACK_Allocate(); CALLBACK_Setup(call_int1,&INT1_Single_Step,CB_IRET); RealSetVec(0x1,CALLBACK_RealPointer(call_int1)); /* Setup some stuff in 0x40 bios segment */ /* Test for parallel port */ if (IO_Read(0x378)!=0xff) real_writew(0x40,0x08,0x378); /* Test for serial port */ Bitu index=0; if (IO_Read(0x3fa)!=0xff) real_writew(0x40,(index++)*2,0x3f8); if (IO_Read(0x2fa)!=0xff) real_writew(0x40,(index++)*2,0x2f8); /* Setup equipment list */ Bitu config=0x4400; //1 Floppy, 2 serial and 1 parrallel #if (C_FPU) config|=0x2; //FPU #endif switch (machine) { case MCH_HERC: config|=0x30; //Startup monochrome break; case MCH_CGA: case MCH_TANDY: config|=0x20; //Startup 80x25 color break; default: config|=0; //EGA VGA break; } config |= 0x04; // PS2 mouse mem_writew(BIOS_CONFIGURATION,config); /* Setup extended memory size */ IO_Write(0x70,0x30); size_extended=IO_Read(0x71); IO_Write(0x70,0x31); size_extended|=(IO_Read(0x71) << 8); }