コード例 #1
0
ファイル: rs232_io.c プロジェクト: chunhualiu/OpenNT
void rs232_io()
{
#ifdef BIT_ORDER2
   union {
      half_word all;
      struct {
	 HALF_WORD_BIT_FIELD word_length:2;
	 HALF_WORD_BIT_FIELD stop_bit:1;
	 HALF_WORD_BIT_FIELD parity:2;
	 HALF_WORD_BIT_FIELD baud_rate:3;
      } bit;
   } parameters;
#endif
#ifdef BIT_ORDER1
   union {
      half_word all;
      struct {
	 HALF_WORD_BIT_FIELD baud_rate:3;
	 HALF_WORD_BIT_FIELD parity:2;
	 HALF_WORD_BIT_FIELD stop_bit:1;
	 HALF_WORD_BIT_FIELD word_length:2;
      } bit;
   } parameters;
#endif

   DIVISOR_LATCH divisor_latch;
   int j;
   half_word timeout;
   sys_addr timeout_location;

   /* clear com/lpt idle flag */
   IDLE_comlpt ();

   setIF(1);

   /*
    * Which adapter?
    */
   switch (getDX ())
   {
	case 0:
   		port = RS232_COM1_PORT_START;
		timeout_location = RS232_COM1_TIMEOUT;
		break;
	case 1:
   		port = RS232_COM2_PORT_START;
		timeout_location = RS232_COM2_TIMEOUT;
		break;
	case 2:
   		port = RS232_COM3_PORT_START;
		timeout_location = RS232_COM3_TIMEOUT;
		break;
	case 3:
   		port = RS232_COM4_PORT_START;
		timeout_location = RS232_COM4_TIMEOUT;
		break;
	default:
		break;
   }
   
   /*
    * Determine function
    */
   switch (getAH ())
   {
   case 0:
      /*
       * Initialise the communication port
       */
      value = 0x80;   /* set DLAB */
      outb(port + (io_addr) RS232_LCR, value);
      /*
       * Set baud rate
       */
      parameters.all = getAL();
      divisor_latch.all = divisors[parameters.bit.baud_rate];
      outb(port + (io_addr) RS232_IER, divisor_latch.byte.MSByte);
      outb(port + (io_addr) RS232_TX_RX, divisor_latch.byte.LSByte);
      /*
       * Set word length, stop bits and parity
       */
      parameters.bit.baud_rate = 0;
      outb(port + (io_addr) RS232_LCR, parameters.all);
      /*
       * Disable interrupts
       */
      value = 0;
      outb(port + (io_addr) RS232_IER, value);
      return_status();
      break;

   case 1:
      /*
       * Send char over the comms line
       */

      /*
       * Set DTR and RTS
       */
      outb(port + (io_addr) RS232_MCR, 3);
      /*
       * Real BIOS checks CTS and DSR - we know DSR ok.
       * Real BIOS check THRE - we know it's ok.
	   * We only check CTS - this is supported on a few ports, eg. Macintosh.
       */
      /*
       * Wait for CTS to go high, or timeout
       */
      sas_load(timeout_location, &timeout);
      for ( j = 0; j < timeout; j++)
      {
	  	inb(port + (io_addr) RS232_MSR, &value);
		if(value & 0x10)break;	/* CTS High, all is well */
      }
	  if(j < timeout)
	  {
      	outb(port + (io_addr) RS232_TX_RX, getAL());	/* Send byte */
		inb(port + (io_addr) RS232_LSR, &value);
		setAH(value);									/* Return Line Status Reg in AH */
	  }
      else
	  {
	    setAH(value | 0x80);	/* Indicate time out */
	  }
      break;

   case 2:
      /*
       * Receive char over the comms line
       */
      /*
       * Set DTR
       */
      value = 1;
      outb(port + (io_addr) RS232_MCR, value);
      /*
       * Real BIOS checks DSR - we know it's ok.
       */
      /*
       * Wait for data to appear, or timeout(just an empirical guess)
       */
      
      sas_load(timeout_location, &timeout);
      for ( j = 0; j < timeout; j++)
	 {
	 inb(port + (io_addr) RS232_LSR, &value);
	 if ( (value & 1) == 1 )
	    {
	    /*
	     * Data ready go read it
	     */
	    value &= 0x1e;   /* keep error bits only */
	    setAH(value);
               
	    inb(port + (io_addr) RS232_TX_RX, &value);
	    setAL(value);
	    return;
	    }
	 }
      
      /*
       * Set timeout
       */
      value |= 0x80;
      setAH(value);
      break;

   case 3:
      /*
       * Return the communication port status
       */
      return_status();
      break;
   case 4:
      /*
       * EXTENDED (PS/2) Initialise the communication port
       */
	value = 0x80;   /* set DLAB */
	outb(port + (io_addr) RS232_LCR, value);
	parameters.bit.word_length = getCH();
	parameters.bit.stop_bit = getBL();
	parameters.bit.parity = getBH();
	parameters.bit.baud_rate = getCL();

	/*
        	Set baud rate
	*/
      divisor_latch.all = divisors[parameters.bit.baud_rate];
      outb(port + (io_addr) RS232_IER, divisor_latch.byte.MSByte);
      outb(port + (io_addr) RS232_TX_RX, divisor_latch.byte.LSByte);
      /*
       * Set word length, stop bits and parity
       */
      parameters.bit.baud_rate = 0;
      outb(port + (io_addr) RS232_LCR, parameters.all);
      /*
       * Disable interrupts
       */
      value = 0;
      outb(port + (io_addr) RS232_IER, value);
      return_status();
      break;
   
   case 5:	/* EXTENDED Comms Port Control */
	switch( getAL() )
	{
		case 0:	/* Read modem control register */
			inb( port + (io_addr) RS232_MCR, &value);
			setBL(value);
			break;
		case 1: /* Write modem control register */
			outb( port + (io_addr) RS232_MCR, getBL());
			break;
	}
	/*
		 Return the communication port status
	*/
	return_status();
	break;
   default:
	/*
	** Yes both XT and AT BIOS's really do this.
	*/
	setAH( getAH()-3 );
      	break;
   }
}
コード例 #2
0
ファイル: sbbsexec.c プロジェクト: kindy/synchronet-bbs-1
__declspec(dllexport) void __cdecl VDDDispatch(void) 
{
	char			str[512];
	DWORD			count;
	DWORD			msgs;
	int				retval;
	int				node_num;
	BYTE*			p;
	vdd_status_t*	status;
	static  DWORD	writes;
	static  DWORD	bytes_written;
	static	DWORD	reads;
	static  DWORD	bytes_read;
	static  DWORD	inbuf_poll;
	static	DWORD	online_poll;
	static	DWORD	status_poll;
	static	DWORD	vdd_yields;
	static	DWORD	vdd_calls;
	VDD_IO_HANDLERS  IOHandlers = { NULL };
	static VDD_IO_PORTRANGE PortRange;

	retval=0;
	node_num=getBH();

	lprintf(LOG_DEBUG,"VDD_OP: (handle=%d) %d (arg=%X)", getAX(),getBL(),getCX());
	vdd_calls++;

	switch(getBL()) {

		case VDD_OPEN:

			sscanf("$Revision: 1.40 $", "%*s %s", revision);

			lprintf(LOG_INFO,"Synchronet Virtual Device Driver, rev %s %s %s"
				,revision, __DATE__, __TIME__);
#if 0
			sprintf(str,"sbbsexec%d.log",node_num);
			fp=fopen(str,"wb");
#endif

			sprintf(str,"\\\\.\\mailslot\\sbbsexec\\wr%d",node_num);
			rdslot=CreateMailslot(str
				,0 //LINEAR_RX_BUFLEN		/* Max message size (0=any) */
				,MAILSLOT_WAIT_FOREVER 	/* Read timeout */
				,NULL);
			if(rdslot==INVALID_HANDLE_VALUE) {
				lprintf(LOG_ERR,"!VDD_OPEN: Error %d opening %s"
					,GetLastError(),str);
				retval=1;
				break;
			}

			sprintf(str,"\\\\.\\mailslot\\sbbsexec\\rd%d",node_num);
			wrslot=CreateFile(str
				,GENERIC_WRITE
				,FILE_SHARE_READ
				,NULL
				,OPEN_EXISTING
				,FILE_ATTRIBUTE_NORMAL
				,(HANDLE) NULL);
			if(wrslot==INVALID_HANDLE_VALUE) {
				lprintf(LOG_ERR,"!VDD_OPEN: Error %d opening %s"
					,GetLastError(),str);
				retval=2;
				break;
			}

			if(RingBufInit(&rdbuf, RINGBUF_SIZE_IN)!=0) {
				retval=3;
				break;
			}

			sprintf(str,"sbbsexec_hungup%d",node_num);
			hungup_event=OpenEvent(
				EVENT_ALL_ACCESS,	/* access flag  */
				FALSE,				/* inherit flag  */
				str);				/* pointer to event-object name  */
			if(hungup_event==NULL) {
				lprintf(LOG_ERR,"!VDD_OPEN: Error %d opening %s"
					,GetLastError(),str);
				retval=4;
				break;
			}

			sprintf(str,"sbbsexec_hangup%d",node_num);
			hangup_event=OpenEvent(
				EVENT_ALL_ACCESS,	/* access flag  */
				FALSE,				/* inherit flag  */
				str);				/* pointer to event-object name  */
			if(hangup_event==NULL) {
				lprintf(LOG_WARNING,"!VDD_OPEN: Error %d opening %s"
					,GetLastError(),str);
			}

			status_poll=0;
			inbuf_poll=0;
			online_poll=0;
			yields=0;

			lprintf(LOG_INFO,"Yield interval: %f milliseconds", yield_interval);

			if(virtualize_uart) {
				lprintf(LOG_INFO,"Virtualizing UART (0x%x, IRQ %u)"
					,uart_io_base, uart_irq);

				IOHandlers.inb_handler = uart_rdport;
				IOHandlers.outb_handler = uart_wrport;
				PortRange.First=uart_io_base;
				PortRange.Last=uart_io_base + UART_IO_RANGE;

				VDDInstallIOHook((HANDLE)getAX(), 1, &PortRange, &IOHandlers);

				interrupt_event=CreateEvent(NULL,FALSE,FALSE,NULL);
				InitializeCriticalSection(&interrupt_mutex);

				_beginthread(interrupt_thread, 0, NULL);
			}

			lprintf(LOG_DEBUG,"VDD_OPEN: Opened successfully (wrslot=%p)", wrslot);

			_beginthread(input_thread, 0, NULL);

			retval=0;
			break;

		case VDD_CLOSE:
			lprintf(LOG_INFO,"VDD_CLOSE: rdbuf=%u "
				"status_poll=%u inbuf_poll=%u online_poll=%u yields=%u vdd_yields=%u vdd_calls=%u"
				,RingBufFull(&rdbuf),status_poll,inbuf_poll,online_poll
				,yields,vdd_yields,vdd_calls);
			lprintf(LOG_INFO,"           read=%u bytes (in %u calls)",bytes_read,reads);
			lprintf(LOG_INFO,"           wrote=%u bytes (in %u calls)",bytes_written,writes);

			if(virtualize_uart) {
				lprintf(LOG_INFO,"Uninstalling Virtualizaed UART IO Hook");
				VDDDeInstallIOHook((HANDLE)getAX(), 1, &PortRange);
			}

			CloseHandle(rdslot);
			CloseHandle(wrslot);
			if(hungup_event!=NULL)
				CloseHandle(hungup_event);
			if(hangup_event!=NULL)
				CloseHandle(hangup_event);

#if 0	/* This isn't strictly necessary... 
		   and possibly the cause of a NULL dereference in the input_thread */
			RingBufDispose(&rdbuf);
#endif
			status_poll=0;
			retval=0;

			break;

		case VDD_READ:
			count = getCX();
			if(count != 1)
				lprintf(LOG_DEBUG,"VDD_READ of %d",count);
			p = (BYTE*) GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 
			retval=vdd_read(p, count);
			reads++;
			bytes_read+=retval;
			reset_yield();
			break;

		case VDD_PEEK:
			count = getCX();
			if(count != 1)
				lprintf(LOG_DEBUG,"VDD_PEEK of %d",count);

			p = (BYTE*) GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 
			retval=RingBufPeek(&rdbuf,p,count);
			reset_yield();
			break;

		case VDD_WRITE:
			count = getCX();
			if(count != 1)
				lprintf(LOG_DEBUG,"VDD_WRITE of %d",count);
			p = (BYTE*) GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 
			if(!WriteFile(wrslot,p,count,&retval,NULL)) {
				lprintf(LOG_ERR,"!VDD_WRITE: WriteFile Error %d (size=%d)"
					,GetLastError(),retval);
				retval=0;
			} else {
				writes++;
				bytes_written+=retval;
				reset_yield();
			}
			break;

		case VDD_STATUS:

			status_poll++;
			count = getCX();
			if(count != sizeof(vdd_status_t)) {
				lprintf(LOG_DEBUG,"!VDD_STATUS: wrong size (%d!=%d)",count,sizeof(vdd_status_t));
				retval=sizeof(vdd_status_t);
				break;
			}
			status = (vdd_status_t*) GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 

			status->inbuf_size=RINGBUF_SIZE_IN;
			status->inbuf_full=RingBufFull(&rdbuf);
			msgs=0;

			/* OUTBUF FULL/SIZE */
			if(!GetMailslotInfo(
				wrslot,					/* mailslot handle  */
 				&status->outbuf_size,	/* address of maximum message size  */
				&status->outbuf_full,	/* address of size of next message  */
				&msgs,					/* address of number of messages  */
 				NULL					/* address of read time-out  */
				)) {
				lprintf(LOG_ERR,"!VDD_STATUS: GetMailSlotInfo(%p) failed, error %u (msgs=%u, inbuf_full=%u, inbuf_size=%u)"
					,wrslot
					,GetLastError(), msgs, status->inbuf_full, status->inbuf_size);
				status->outbuf_full=0;
				status->outbuf_size=DEFAULT_MAX_MSG_SIZE;
			} else
				lprintf(LOG_DEBUG,"VDD_STATUS: MailSlot maxmsgsize=%u, nextmsgsize=%u, msgs=%u"
					,status->outbuf_size
					,status->outbuf_full
					,msgs);
			if(status->outbuf_full==MAILSLOT_NO_MESSAGE)
				status->outbuf_full=0;
			status->outbuf_full*=msgs;
			
			/* ONLINE */
			if(WaitForSingleObject(hungup_event,0)==WAIT_OBJECT_0)
				status->online=0;
			else
				status->online=1;

			retval=0;	/* success */
			break;

		case VDD_INBUF_PURGE:
			RingBufReInit(&rdbuf);
			retval=0;
			break;

		case VDD_OUTBUF_PURGE:
			lprintf(LOG_WARNING,"!VDD_OUTBUF_PURGE: NOT IMPLEMENTED");
			retval=0;
			break;

		case VDD_INBUF_FULL:
			retval=RingBufFull(&rdbuf);
			inbuf_poll++;
			break;

		case VDD_INBUF_SIZE:
			retval=RINGBUF_SIZE_IN;
			break;

		case VDD_OUTBUF_FULL:
			if(!GetMailslotInfo(
				wrslot,		/* mailslot handle  */
 				NULL,		/* address of maximum message size  */
				&retval,	/* address of size of next message  */
				&msgs,		/* address of number of messages  */
 				NULL		/* address of read time-out  */
				))
				retval=0;
			if(retval==MAILSLOT_NO_MESSAGE)
				retval=0;
			retval*=msgs;
			break;

		case VDD_OUTBUF_SIZE:
			if(!GetMailslotInfo(
				wrslot,		/* mailslot handle  */
 				&retval,	/* address of maximum message size  */
				NULL,		/* address of size of next message  */
				NULL,		/* address of number of messages  */
 				NULL		/* address of read time-out  */
				)) 
				retval=DEFAULT_MAX_MSG_SIZE;
			break;

		case VDD_ONLINE:
			if(WaitForSingleObject(hungup_event,0)==WAIT_OBJECT_0)
				retval=0;
			else
				retval=1;
			online_poll++;
			break;

		case VDD_YIELD:			/* forced yield */
			vdd_yields++;
			yield();
			break;

		case VDD_MAYBE_YIELD:	/* yield if YieldInterval is enabled and expired */
			maybe_yield();
			break;

		case VDD_LOAD_INI_FILE:	/* Load and parse settings file */
			{
				FILE*	fp;
				char	cwd[MAX_PATH+1];

				/* Load exec/sbbsexec.ini first (setting default values) */
				count = getCX();
				p = (BYTE*)GetVDMPointer((ULONG)((getES() << 16)|getDI())
					,count,FALSE); 
				iniFileName(ini_fname, sizeof(ini_fname), p, INI_FILENAME);
				if((fp=fopen(ini_fname,"r"))!=NULL) {
					ini=iniReadFile(fp);
					fclose(fp);
					parse_ini(ROOT_SECTION);
				}

				/* Load cwd/sbbsexec.ini second (over-riding default values) */
				GetCurrentDirectory(sizeof(cwd),cwd);
				iniFileName(ini_fname, sizeof(ini_fname), cwd, INI_FILENAME);
				if((fp=fopen(ini_fname,"r"))!=NULL) {
					ini=iniReadFile(fp);
					fclose(fp);
					parse_ini(ROOT_SECTION);
				}
			}
			break;

		case VDD_LOAD_INI_SECTION:	/* Parse (program-specific) sub-section of settings file */
			count = getCX();
			p = (BYTE*)GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 
			parse_ini(p);
			break;

		case VDD_DEBUG_OUTPUT:	/* Send string to debug output */
			count = getCX();
			p = (BYTE*)GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 
			lputs(LOG_INFO, p);
			break;

		case VDD_HANGUP:
			hangup();
			break;

		default:
			lprintf(LOG_ERR,"!UNKNOWN VDD_OP: %d",getBL());
			break;
	}
	setAX((WORD)retval);
}
コード例 #3
0
ファイル: dskbios32.c プロジェクト: Moteesh/reactos
/*static*/
VOID WINAPI BiosDiskService(LPWORD Stack)
{
    BYTE Drive;
    PDISK_IMAGE DiskImage;

    switch (getAH())
    {
        /* Disk -- Reset Disk System */
        case 0x00:
        {
            Drive = getDL();

            if (Drive & 0x80)
            {
                AllDisksReset();

                /* Return success */
                setAH(0x00);
                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                break;
            }

            Drive &= ~0x80;

            if (Drive >= ARRAYSIZE(FloppyDrive))
            {
                DPRINT1("BiosDiskService(0x00): Drive number 0x%02X invalid\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            // TODO: Reset drive

            /* Return success */
            setAH(0x00);
            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
            break;
        }

        /* Disk -- Get Status of Last Operation */
        case 0x01:
        {
            BYTE LastOperationStatus = 0x00;

            Drive = getDL();
            DiskImage = GetDisk(Drive);
            if (!DiskImage || !IsDiskPresent(DiskImage))
            {
                DPRINT1("BiosDiskService(0x01): Disk number 0x%02X invalid\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            LastOperationStatus = DiskImage->LastOperationStatus;

            if (Drive & 0x80)
                Bda->LastDiskOperation = LastOperationStatus;
            else
                Bda->LastDisketteOperation = LastOperationStatus;

            /* Return last error */
            setAH(LastOperationStatus);
            if (LastOperationStatus == 0x00)
                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
            else
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;

            break;
        }

        /* Disk -- Read Sectors into Memory */
        case 0x02:
        {
            BYTE Status;
            BYTE Head = getDH();
            BYTE NumSectors = getAL();

            // CH: Low eight bits of cylinder number
            // CL: High two bits of cylinder (bits 6-7, hard disk only)
            WORD Cylinder = MAKEWORD(getCH(), (getCL() >> 6) & 0x02);

            // CL: Sector number 1-63 (bits 0-5)
            BYTE Sector = (getCL() & 0x3F); // 1-based

            Drive = getDL();
            DiskImage = GetDisk(Drive);
            if (!DiskImage || !IsDiskPresent(DiskImage))
            {
                DPRINT1("BiosDiskService(0x02): Disk number 0x%02X invalid\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            /* Read the sectors */
            Status = ReadDisk(DiskImage, Cylinder, Head, Sector, NumSectors);
            if (Status == 0x00)
            {
                /* Return success */
                setAH(0x00);
                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
            }
            else
            {
                DPRINT1("BiosDiskService(0x02): Error when reading from disk number 0x%02X (0x%02X)\n", Drive, Status);

                /* Return error */
                setAH(Status);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
            }

            break;
        }

        /* Disk -- Write Disk Sectors */
        case 0x03:
        {
            BYTE Status;
            BYTE Head = getDH();
            BYTE NumSectors = getAL();

            // CH: Low eight bits of cylinder number
            // CL: High two bits of cylinder (bits 6-7, hard disk only)
            WORD Cylinder = MAKEWORD(getCH(), (getCL() >> 6) & 0x02);

            // CL: Sector number 1-63 (bits 0-5)
            BYTE Sector = (getCL() & 0x3F); // 1-based

            Drive = getDL();
            DiskImage = GetDisk(Drive);
            if (!DiskImage || !IsDiskPresent(DiskImage))
            {
                DPRINT1("BiosDiskService(0x03): Disk number 0x%02X invalid\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            /* Write the sectors */
            Status = WriteDisk(DiskImage, Cylinder, Head, Sector, NumSectors);
            if (Status == 0x00)
            {
                /* Return success */
                setAH(0x00);
                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
            }
            else
            {
                DPRINT1("BiosDiskService(0x03): Error when writing to disk number 0x%02X (0x%02X)\n", Drive, Status);

                /* Return error */
                setAH(Status);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
            }

            break;
        }

        /* Disk -- Verify Disk Sectors */
        case 0x04:

        /* Floppy/Fixed Disk -- Format Track */
        case 0x05:

        /* Fixed Disk -- Format Track and Set Bad Sector Flags */
        case 0x06:

        /* Fixed Disk -- Format Drive starting at Given Track */
        case 0x07:
            goto Default;

        /* Disk -- Get Drive Parameters */
        case 0x08:
        {
            WORD MaxCylinders;
            BYTE MaxHeads;
            BYTE PresentDrives = 0;
            BYTE i;

            Drive = getDL();
            DiskImage = GetDisk(Drive);
            if (!DiskImage || !IsDiskPresent(DiskImage))
            {
                DPRINT1("BiosDiskService(0x08): Disk number 0x%02X invalid\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            // Minus 2 because it's the maximum cylinder number (not count),
            // and the last cylinder is reserved (for compatibility with BIOSes
            // which reserve it for testing purposes).
            MaxCylinders = DiskImage->DiskInfo.Cylinders - 2;
            // Minus 1 because it's the maximum head number (not count).
            MaxHeads     = DiskImage->DiskInfo.Heads - 1;

            // CL: Sector number 1-63 (bits 0-5)
            //     High two bits of cylinder (bits 6-7, hard disk only)
            setCL((DiskImage->DiskInfo.Sectors & 0x3F) |
                  ((HIBYTE(MaxCylinders) & 0x02) << 6));
            // CH: Low eight bits of cylinder number
            setCH(LOBYTE(MaxCylinders));

            setDH(MaxHeads);

            if (Drive & 0x80)
            {
                /* Count the number of active HDDs */
                for (i = 0; i < ARRAYSIZE(HardDrive); ++i)
                {
                    if (IsDiskPresent(HardDrive[i]))
                        ++PresentDrives;
                }

                /* Reset ES:DI to NULL */
                // FIXME: NONONO!! Apps expect (for example, MS-DOS kernel)
                // that this function does not modify ES:DI if it was called
                // for a HDD.
                // setES(0x0000);
                // setDI(0x0000);
            }
            else
            {
                /* Count the number of active floppies */
                for (i = 0; i < ARRAYSIZE(FloppyDrive); ++i)
                {
                    if (IsDiskPresent(FloppyDrive[i]))
                        ++PresentDrives;
                }

                /* ES:DI points to the floppy parameter table */
                setES(HIWORD(((PULONG)BaseAddress)[0x1E]));
                setDI(LOWORD(((PULONG)BaseAddress)[0x1E]));
            }
            setDL(PresentDrives);

            setBL(DiskImage->DiskType); // DiskGeometryList[DiskImage->DiskType].biosval
            setAL(0x00);

            /* Return success */
            setAH(0x00);
            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
            break;
        }

        /* Hard Disk -- Initialize Controller with Drive Parameters */
        case 0x09:

        /* Hard Disk -- Read Long Sectors */
        case 0x0A:

        /* Hard Disk -- Write Long Sectors */
        case 0x0B:
            goto Default;

        /* Hard Disk -- Seek to Cylinder */
        case 0x0C:
        {
            BYTE Status;
            BYTE Head = getDH();

            // CH: Low eight bits of cylinder number
            // CL: High two bits of cylinder (bits 6-7, hard disk only)
            WORD Cylinder = MAKEWORD(getCH(), (getCL() >> 6) & 0x02);

            // CL: Sector number 1-63 (bits 0-5)
            BYTE Sector = (getCL() & 0x3F); // 1-based

            Drive = getDL();
            if (!(Drive & 0x80))
            {
                DPRINT1("BiosDiskService(0x0C): Disk number 0x%02X is not a HDD\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            DiskImage = GetDisk(Drive);
            if (!DiskImage || !IsDiskPresent(DiskImage))
            {
                DPRINT1("BiosDiskService(0x0C): Disk number 0x%02X invalid\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            /* Set position */
            Status = SeekDisk(DiskImage, Cylinder, Head, Sector);
            if (Status == 0x00)
            {
                /* Return success */
                setAH(0x00);
                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
            }
            else
            {
                DPRINT1("BiosDiskService(0x0C): Error when seeking in disk number 0x%02X (0x%02X)\n", Drive, Status);

                /* Return error */
                setAH(Status);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
            }

            break;
        }

        /* Hard Disk -- Reset Hard Disks */
        case 0x0D:
        {
            // FIXME: Should do what 0x11 does.
            UNIMPLEMENTED;
        }

        /* Hard Disk -- Read Sector Buffer (XT only) */
        case 0x0E:

        /* Hard Disk -- Write Sector Buffer (XT only) */
        case 0x0F:
            goto Default;

        /* Hard Disk -- Check if Drive is ready */
        case 0x10:
        {
            Drive = getDL();
            if (!(Drive & 0x80))
            {
                DPRINT1("BiosDiskService(0x10): Disk number 0x%02X is not a HDD\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            DiskImage = GetDisk(Drive);
            if (!DiskImage || !IsDiskPresent(DiskImage))
            {
                DPRINT1("BiosDiskService(0x10): Disk number 0x%02X invalid\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            /* Return success */
            setAH(0x00);
            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
            break;
        }

        /* Hard Disk -- Recalibrate Drive */
        case 0x11:
        {
            BYTE Status;

            Drive = getDL();
            if (!(Drive & 0x80))
            {
                DPRINT1("BiosDiskService(0x11): Disk number 0x%02X is not a HDD\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            DiskImage = GetDisk(Drive);
            if (!DiskImage || !IsDiskPresent(DiskImage))
            {
                DPRINT1("BiosDiskService(0x11): Disk number 0x%02X invalid\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            /* Set position to zero */
            Status = SeekDisk(DiskImage, /*Cylinder*/ 0, /*Head*/ 0, /*Sector*/ 1);
            if (Status == 0x00)
            {
                /* Return success */
                setAH(0x00);
                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
            }
            else
            {
                DPRINT1("BiosDiskService(0x11): Error when recalibrating disk number 0x%02X (0x%02X)\n", Drive, Status);

                /* Return error */
                setAH(Status);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
            }

            break;
        }

        /* Hard Disk -- Controller RAM Diagnostic */
        case 0x12:

        /* Hard Disk -- Drive Diagnostic */
        case 0x13:

        /* Hard Disk -- Controller Internal Diagnostic */
        case 0x14:
            goto Default;

        /* Disk -- Get Disk Type */
        case 0x15:
        {
            Drive = getDL();
            DiskImage = GetDisk(Drive);
            if (!DiskImage || !IsDiskPresent(DiskImage))
            {
                DPRINT1("BiosDiskService(0x15): Disk number 0x%02X invalid\n", Drive);

                /* Return error */
                setAH(0x01);
                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                break;
            }

            if (Drive & 0x80)
            {
                ULONG NumSectors;

                /* Hard disk */
                setAH(0x03);

                /* Number of 512-byte sectors in CX:DX */
                NumSectors = (ULONG)((ULONG)DiskImage->DiskInfo.Cylinders * DiskImage->DiskInfo.Heads)
                                                                          * DiskImage->DiskInfo.Sectors;
                setCX(HIWORD(NumSectors));
                setDX(LOWORD(NumSectors));
            }
            else
            {
                /* Floppy */
                setAH(0x01);
            }

            /* Return success */
            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
            break;
        }

        /* Floppy Disk -- Detect Disk Change */
        case 0x16:

        /* Floppy Disk -- Set Disk Type for Format */
        case 0x17:

        /* Disk -- Set Media Type for Format */
        case 0x18:
            goto Default;

        default: Default:
        {
            DPRINT1("BIOS Function INT 13h, AH = 0x%02X, AL = 0x%02X, BH = 0x%02X NOT IMPLEMENTED\n",
                    getAH(), getAL(), getBH());
        }
    }
}