# 1 "rombios.c"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

# 1 "biosconfig.h" 1
 

# 25 "rombios.c" 2


 


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


 
 
 
 
 
 
 
 
 
 
 

 
 
 
 







    




   
   





 
 
 
 
 
 
 

 
 
 
 



#asm
.rom
.org 0x0000


use16 386




MACRO HALT
  ;; the HALT macro is called with the line number of the HALT call.
  ;; The line number is then sent to the 0x400 , causing Bochs to
  ;; print a BX_PANIC message.  This will normally halt the simulation
  ;; with a message such as "BIOS panic at rombios.c, line 4091".
  ;; However, users can choose to make panics non-fatal and continue.
  mov dx,#0x400 
  mov ax,#?1
  out dx,ax
MEND

MACRO JMP_AP
  db 0xea
  dw ?2
  dw ?1
MEND

MACRO SET_INT_VECTOR
  mov ax, ?3
  mov ?1*4, ax
  mov ax, ?2
  mov ?1*4+2, ax
MEND

#endasm


typedef unsigned char  Bit8u;
typedef unsigned short Bit16u;
typedef unsigned short Boolean;

 
 

typedef struct {
  unsigned char filler1[0x400];
  unsigned char filler2[0x6c];
  Bit16u ticks_low;
  Bit16u ticks_high;
  Bit8u  midnight_flag;
  } bios_data_t;



typedef struct {
  union {
    struct {
      Bit16u di, si, bp, sp;
      Bit16u bx, dx, cx, ax;
      } r16;
    struct {
      Bit16u filler[4];
      Bit8u  bl, bh, dl, dh, cl, ch, al, ah;
      } r8;
    } u;
  } pusha_regs_t;

typedef struct {
  union {
    struct {
      Bit16u flags;
      } r16;
    struct {
      Bit8u  flagsl;
      Bit8u  flagsh;
      } r8;
    } u;
  } flags_t;







typedef struct {
  Bit16u ip;
  Bit16u cs;
  flags_t flags;
  } iret_addr_t;



static Bit8u          inb();
static Bit8u          inb_cmos();
static void           outb();
static void           outb_cmos();
static Bit16u         inw();
static void           outw();
static void           init_rtc();
static Boolean        rtc_updating();

static Bit8u          read_byte();
static Bit16u         read_word();
static void           write_byte();
static void           write_word();
static void           bios_printf();

static Bit16u         UDIV();

static Bit8u          inhibit_mouse_int_and_events();
static void           enable_mouse_int_and_events();
static Bit8u          send_to_mouse_ctrl();
static Bit8u          get_mouse_data();
static void           set_kbd_command_byte();

static void           int09_function();
static void           int13_function();
static void           int13_diskette_function();
static void           int15_function();
static void           int16_function();
static void           int1a_function();
static void           int70_function();
static void           int74_function();
 
 
static Bit16u         get_SS();
static void           enqueue_key();
static unsigned int   dequeue_key();
static void           set_disk_ret_status();
static void           get_hd_geometry();
static void           set_diskette_ret_status();
static void           set_diskette_current_cyl();
static void           determine_floppy_media();
static Boolean        floppy_drive_exists();
static Boolean        floppy_drive_recal();
static Boolean        floppy_media_known();
static Boolean        floppy_media_sense();
static void           cli();
static Boolean        set_enable_a20();
static void           debugger_on();
static void           debugger_off();
static void           keyboard_panic();
static void           boot_failure_msg();
static void           nmi_handler_msg();
static void           print_bios_banner();
static char bios_version_string[] = "BIOS Version is $Id: rombios.c,v 1.14 2001/06/13 07:06:10 bdenney Exp $";












































static struct {
  Bit16u normal;
  Bit16u shift;
  Bit16u control;
  Bit16u alt;
  } scan_to_scanascii[0x53  + 1] = {
      {   0 ,   0 ,   0 ,   0  },
      { 0x011b, 0x011b, 0x011b, 0x0100 },  
      { 0x0231, 0x0221,   0 , 0x7800 },  
      { 0x0332, 0x0340, 0x0300, 0x7900 },  
      { 0x0433, 0x0423,   0 , 0x7a00 },  
      { 0x0534, 0x0524,   0 , 0x7b00 },  
      { 0x0635, 0x0625,   0 , 0x7c00 },  
      { 0x0736, 0x075e, 0x071e, 0x7d00 },  
      { 0x0837, 0x0826,   0 , 0x7e00 },  
      { 0x0938, 0x092a,   0 , 0x7f00 },  
      { 0x0a39, 0x0a28,   0 , 0x8000 },  
      { 0x0b30, 0x0b29,   0 , 0x8100 },  
      { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200 },  
      { 0x0d3d, 0x0d2b,   0 , 0x8300 },  
      { 0x0e08, 0x0e08, 0x0e7f,   0  },  
      { 0x0f09, 0x0f00,   0 ,   0  },  
      { 0x1071, 0x1051, 0x1011, 0x1000 },  
      { 0x1177, 0x1157, 0x1117, 0x1100 },  
      { 0x1265, 0x1245, 0x1205, 0x1200 },  
      { 0x1372, 0x1352, 0x1312, 0x1300 },  
      { 0x1474, 0x1454, 0x1414, 0x1400 },  
      { 0x1579, 0x1559, 0x1519, 0x1500 },  
      { 0x1675, 0x1655, 0x1615, 0x1600 },  
      { 0x1769, 0x1749, 0x1709, 0x1700 },  
      { 0x186f, 0x184f, 0x180f, 0x1800 },  
      { 0x1970, 0x1950, 0x1910, 0x1900 },  
      { 0x1a5b, 0x1a7b, 0x1a1b,   0  },  
      { 0x1b5d, 0x1b7d, 0x1b1d,   0  },  
      { 0x1c0d, 0x1c0d, 0x1c0a,   0  },  
      {   0 ,   0 ,   0 ,   0  },  
      { 0x1e61, 0x1e41, 0x1e01, 0x1e00 },  
      { 0x1f73, 0x1f53, 0x1f13, 0x1f00 },  
      { 0x2064, 0x2044, 0x2004, 0x2000 },  
      { 0x2166, 0x2146, 0x2106, 0x2100 },  
      { 0x2267, 0x2247, 0x2207, 0x2200 },  
      { 0x2368, 0x2348, 0x2308, 0x2300 },  
      { 0x246a, 0x244a, 0x240a, 0x2400 },  
      { 0x256b, 0x254b, 0x250b, 0x2500 },  
      { 0x266c, 0x264c, 0x260c, 0x2600 },  
      { 0x273b, 0x273a,   0 ,   0  },  
      { 0x2827, 0x2822,   0 ,   0  },  
      { 0x2960, 0x297e,   0 ,   0  },  
      {   0 ,   0 ,   0 ,   0  },  
      { 0x2b5c, 0x2b7c, 0x2b1c,   0  },  
      { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00 },  
      { 0x2d78, 0x2d58, 0x2d18, 0x2d00 },  
      { 0x2e63, 0x2e43, 0x2e03, 0x2e00 },  
      { 0x2f76, 0x2f56, 0x2f16, 0x2f00 },  
      { 0x3062, 0x3042, 0x3002, 0x3000 },  
      { 0x316e, 0x314e, 0x310e, 0x3100 },  
      { 0x326d, 0x324d, 0x320d, 0x3200 },  
      { 0x332c, 0x333c,   0 ,   0  },  
      { 0x342e, 0x343e,   0 ,   0  },  
      { 0x352f, 0x353f,   0 ,   0  },  
      {   0 ,   0 ,   0 ,   0  },  
      { 0x372a, 0x372a,   0 ,   0  },  
      {   0 ,   0 ,   0 ,   0  },  
      { 0x3920, 0x3920, 0x3920, 0x3920 },  
      {   0 ,   0 ,   0 ,   0  },  
      { 0x3b00, 0x5400, 0x5e00, 0x6800 },  
      { 0x3c00, 0x5500, 0x5f00, 0x6900 },  
      { 0x3d00, 0x5600, 0x6000, 0x6a00 },  
      { 0x3e00, 0x5700, 0x6100, 0x6b00 },  
      { 0x3f00, 0x5800, 0x6200, 0x6c00 },  
      { 0x4000, 0x5900, 0x6300, 0x6d00 },  
      { 0x4100, 0x5a00, 0x6400, 0x6e00 },  
      { 0x4200, 0x5b00, 0x6500, 0x6f00 },  
      { 0x4300, 0x5c00, 0x6600, 0x7000 },  
      { 0x4400, 0x5d00, 0x6700, 0x7100 },  
      {   0 ,   0 ,   0 ,   0  },  
      {   0 ,   0 ,   0 ,   0  },  
      { 0x4700, 0x4737, 0x7700,   0  },  
      { 0x4800, 0x4838,   0 ,   0  },  
      { 0x4900, 0x4939, 0x8400,   0  },  
      { 0x4a2d, 0x4a2d,   0 ,   0  },  
      { 0x4b00, 0x4b34, 0x7300,   0  },  
      { 0x4c00, 0x4c35,   0 ,   0  },  
      { 0x4d00, 0x4d36, 0x7400,   0  },  
      { 0x4e2b, 0x4e2b,   0 ,   0  },  
      { 0x4f00, 0x4f31, 0x7500,   0  },  
      { 0x5000, 0x5032,   0 ,   0  },  
      { 0x5100, 0x5133, 0x7600,   0  },  
      { 0x5200, 0x5230,   0 ,   0  },  
      { 0x5300, 0x532e,   0 ,   0  }   
      };

  Bit8u
inb(port)
  Bit16u port;
{
#asm
  push bp
  mov  bp, sp

    push dx
    mov  dx, 4[bp]
    in   al, dx
    pop  dx

  pop  bp
#endasm
}

# 430 "rombios.c"



  void
outb(port, val)
  Bit16u port;
  Bit8u  val;
{
#asm
  push bp
  mov  bp, sp

    push ax
    push dx
    mov  dx, 4[bp]
    mov  al, 6[bp]
    out  dx, al
    pop  dx
    pop  ax

  pop  bp
#endasm
}

# 475 "rombios.c"


  void
outb_cmos(cmos_reg, val)
  Bit8u cmos_reg;
  Bit8u val;
{
#asm
  push bp
  mov  bp, sp

    mov  al, 4[bp] ;; cmos_reg
    out  0x70, al
    mov  al, 6[bp] ;; val
    out  0x71, al

  pop  bp
#endasm
}

  Bit8u
inb_cmos(cmos_reg)
  Bit8u cmos_reg;
{
#asm
  push bp
  mov  bp, sp

    mov  al, 4[bp] ;; cmos_reg
    out 0x70, al
    in  al, 0x71

  pop  bp
#endasm
}

  void
init_rtc()
{
  outb_cmos(0x0a, 0x26);
  outb_cmos(0x0b, 0x02);
  inb_cmos(0x0c);
  inb_cmos(0x0d);
}

  Boolean
rtc_updating()
{
   
   
   
   
   
   
   
   

  Bit16u count;

  count = 25000;
  while (--count != 0) {
    if ( (inb_cmos(0x0a) & 0x80) == 0 )
      return(0);
    }
  return(1);  
}


  Bit8u
read_byte(seg, offset)
  Bit16u seg;
  Bit16u offset;
{
#asm
  push bp
  mov  bp, sp

    push bx
    push ds
    mov  ax, 4[bp] ; segment
    mov  ds, ax
    mov  bx, 6[bp] ; offset
    mov  al, [bx]
    ;; al = return value (byte)
    pop  ds
    pop  bx

  pop  bp
#endasm
}

  Bit16u
read_word(seg, offset)
  Bit16u seg;
  Bit16u offset;
{
#asm
  push bp
  mov  bp, sp

    push bx
    push ds
    mov  ax, 4[bp] ; segment
    mov  ds, ax
    mov  bx, 6[bp] ; offset
    mov  ax, [bx]
    ;; ax = return value (word)
    pop  ds
    pop  bx

  pop  bp
#endasm
}

  void
write_byte(seg, offset, data)
  Bit16u seg;
  Bit16u offset;
{
#asm
  push bp
  mov  bp, sp

    push ax
    push bx
    push ds
    mov  ax, 4[bp] ; segment
    mov  ds, ax
    mov  bx, 6[bp] ; offset
    mov  al, 8[bp] ; data byte
    mov  [bx], al  ; write data byte
    pop  ds
    pop  bx
    pop  ax

  pop  bp
#endasm
}

  void
write_word(seg, offset, data)
  Bit16u seg;
  Bit16u offset;
{
#asm
  push bp
  mov  bp, sp

    push ax
    push bx
    push ds
    mov  ax, 4[bp] ; segment
    mov  ds, ax
    mov  bx, 6[bp] ; offset
    mov  ax, 8[bp] ; data word
    mov  [bx], ax  ; write data word
    pop  ds
    pop  bx
    pop  ax

  pop  bp
#endasm
}


  Bit16u
UDIV(a, b)
  Bit16u a, b;
{
   
   
#asm
  push bp
  mov  bp, sp

    push bx
    mov  ax, 4[bp] ;; a
    mov  bx, 6[bp] ;; b: only low eight bits used
    div  bl  ;; AX / BL -->  quotient=AL, remainder=AH
    pop  bx

  pop  bp
#endasm
}

Bit16u
UDIV16(a, b)
  Bit16u a, b;
{
   
#asm
  push bp
  mov bp, sp

    push dx
    push bx
    xor dx,dx
    mov ax, 4[bp] ;; a
    mov bx, 6[bp] ;; b
    div bx ;; DX:AX / BX -> AX, DX = remainder
    pop bx
    pop dx
  pop bp
#endasm
}

 
 
 
 
 
 
 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

  Bit16u
get_SS()
{
#asm
  mov  ax, ss
#endasm
}

  void
put_int(val, width, neg)
  short val, width;
  Boolean neg;
{
  short nval = UDIV16(val, 10);
  if (nval)
    put_int(nval, width - 1, neg);
  else {
    while (--width > 0) outb(0xfff0, ' ');
    if (neg) outb(0xfff0, '-');
  }
  outb(0xfff0, val - (nval * 10) + '0');
}

 
 
 
 
 
 
  void
bios_printf(bomb, s)
  Boolean bomb;
  Bit8u *s;
{
  Bit8u c, format_char;
  Boolean  in_format;
  short i;
  Bit16u  *arg_ptr;
  Bit16u   arg_seg, arg, nibble, shift_count, format_width;

  arg_ptr = &s;
  arg_seg = get_SS();

  in_format = 0;
  format_width = 0;

  while (c = read_byte(0xf000, s)) {
    if ( c == '%' ) {
      in_format = 1;
      format_width = 0;
      }
    else if (in_format) {
      if ( (c>='0') && (c<='9') ) {
        format_width = (format_width * 10) + (c - '0');
        }
      else if (c == 'x') {
        arg_ptr++;  
        arg = read_word(arg_seg, arg_ptr);
        if (format_width == 0)
          format_width = 4;
        for (i=format_width-1; i>=0; i--) {
          nibble = (arg >> (4 * i)) & 0x000f;
          if (nibble <= 9)
            outb(0xfff0, nibble + '0');
          else
            outb(0xfff0, (nibble - 10) + 'A');
          }
        in_format = 0;
        }
      else if (c == 'd') {
        arg_ptr++;  
        arg = read_word(arg_seg, arg_ptr);
        if (arg & 0x8000)
          put_int(-arg, format_width - 1, 1);
        else
          put_int(arg, format_width, 0);
        in_format = 0;
        }
      else
        bios_printf(1,  "bios_printf: unknown format\n"  ) ;
      }
    else {
      outb(0xfff0, c);
      }
    s ++;
    }

  if (bomb) {
#asm
    HALT(795)
#endasm
    }
}

  void
cli()
{
#asm
  cli
#endasm
}

  void
keyboard_panic()
{
  bios_printf(1,  "Keyboard RESET error\n"  ) ;
}

  void
boot_failure_msg(drive)
  Bit16u drive;
{
  if (drive < 0x80) {
    bios_printf(0, "Boot Failure!  I could not read floppy drive %d.\n", drive);
  } else {
    drive &= 0x7f;
    bios_printf(0, "Boot Failure!  I could not read hard disk %d.\n", drive);
  }
}

void
nmi_handler_msg()
{
  bios_printf(0, "NMI Handler called\n");
}

void
print_bios_banner()
{
  bios_printf(0, bios_version_string);
  bios_printf(0, "\n");
}


  Boolean
set_enable_a20(val)
  Boolean val;
{
  Bit8u  oldval;

   

   
  oldval = inb(0x92);

   
  if (val)
    outb(0x92, oldval | 0x02);
  else
    outb(0x92, oldval & 0xfd);

  return((oldval & 0x02) != 0);
}

  void
debugger_on()
{
  outb(0xfedc, 0x01);
}

  void
debugger_off()
{
  outb(0xfedc, 0x00);
}



  void
int15_function(DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS)
  Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
{
  Bit16u ebda_seg;
  Bit8u  mouse_flags_1, mouse_flags_2;
  Bit16u mouse_driver_seg;
  Bit16u mouse_driver_offset;
  Bit8u  in_byte;
  Bit8u  response, prev_command_byte;
  Boolean prev_a20_enable;
  Bit16u  base15_00;
  Bit8u   base23_16;
  Bit16u  ss;
  Bit8u   ret, mouse_data1, mouse_data2, mouse_data3;
  Bit8u   comm_byte;

  switch (( AX >> 8 ) ) {
    case 0x24:  
       ;
      FLAGS |= 0x0001 ;
      AX = ((AX & 0x00ff) | (( 0x86  ) << 8)) ;
      break;

    case 0x41:
      FLAGS |= 0x0001 ;
      AX = ((AX & 0x00ff) | (( 0x86  ) << 8)) ;
      break;

    case 0x4f:
       
      FLAGS |= 0x0001 ;



      break;

    case 0x87:



       
       

      cli();

      prev_a20_enable = set_enable_a20(1);  

       
       

       
       
       
       
       
       
       
       
       

       
       
       
       

 

       
      base15_00 = (ES << 4) + SI;
      base23_16 = ES >> 12;
      if (base15_00 < (ES<<4))
        base23_16++;
      write_word(ES, SI+0x08+0, 47);        
      write_word(ES, SI+0x08+2, base15_00); 
      write_byte(ES, SI+0x08+4, base23_16); 
      write_byte(ES, SI+0x08+5, 0x93);      
      write_word(ES, SI+0x08+6, 0x0000);    

       
      write_word(ES, SI+0x20+0, 0xffff); 
      write_word(ES, SI+0x20+2, 0x0000); 
      write_byte(ES, SI+0x20+4, 0x000f); 
      write_byte(ES, SI+0x20+5, 0x9b);   
      write_word(ES, SI+0x20+6, 0x0000); 

       
      ss = get_SS();
      base15_00 = ss << 4;
      base23_16 = ss >> 12;
      write_word(ES, SI+0x28+0, 0xffff);    
      write_word(ES, SI+0x28+2, base15_00); 
      write_byte(ES, SI+0x28+4, base23_16); 
      write_byte(ES, SI+0x28+5, 0x93);      
      write_word(ES, SI+0x28+6, 0x0000);    

#asm
       
       
      mov  bx, sp
      SEG SS
        mov  cx, _int15_function.CX [bx]

       
       
      mov ax, #0x00
      mov ds, ax
      mov 0x0469, ss
      mov 0x0467, sp

      SEG ES
        lgdt [si + 0x08]
      SEG CS
        lidt [pmode_IDT_info]
      ;;  perhaps do something with IDT here

      ;; set PE bit in CR0
      xor  eax, eax
      mov  al, #0x01
      mov  cr0, eax
      ;; far jump to flush CPU queue after transition to protected mode
      JMP_AP(0x0020, protected_mode)

protected_mode:
      ;; GDT points to valid descriptor table, now load SS, DS, ES
      mov  ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
      mov  ss, ax
      mov  ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
      mov  ds, ax
      mov  ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
      mov  es, ax
      xor  si, si
      xor  di, di
      cld
      rep
        movsw  ;; move CX words from DS:SI to ES:DI

      ;; clear CR3 and reset PG bit in CR0 ???
      xor  eax, eax
      mov cr0, eax

      ;; far jump to flush CPU queue after transition to real mode
      JMP_AP(0xf000, real_mode)

real_mode:
      ;; restore IDT to normal real-mode defaults
      SEG CS
        lidt [rmode_IDT_info]

       
      mov ax, #0x00
      mov ds, ax
      mov ss, 0x0469
      mov sp, 0x0467
#endasm

      set_enable_a20(prev_a20_enable);
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      FLAGS &= 0xfffe ;
      break;


    case 0x88:  




       
       
       
      in_byte = inb_cmos(0x30);
      AX = ((AX & 0xff00) | ( in_byte )) ;
      in_byte = inb_cmos(0x31);
      AX = ((AX & 0x00ff) | (( in_byte ) << 8)) ;
      FLAGS &= 0xfffe ;

      break;

    case 0x90:
       
      break;

    case 0x91:
       
      break;

    case 0xbf:
       ;
      FLAGS |= 0x0001 ;
      AX = ((AX & 0x00ff) | (( 0x86  ) << 8)) ;
      break;

    case 0xC0:





      FLAGS &= 0xfffe ;
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      BX =  0xe6f5 ;
      ES = 0xF000;
      break;

    case 0xc1:

      ES = read_word(0x0040, 0x000E);
      FLAGS &= 0xfffe ;




      break;

    case 0xC2:
       
       
       
       
       
       
       
       
       
       





      ebda_seg = read_word(0x0040, 0x000E);
      switch (( AX & 0x00ff ) ) {
        case 0:  
 ;
          switch (( BX >> 8 ) ) {
            case 0:  
 ;
              inhibit_mouse_int_and_events();  
              ret = send_to_mouse_ctrl(0xF5);  
              if (ret == 0) {
                ret = get_mouse_data(&mouse_data1);
                if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
                  FLAGS &= 0xfffe ;
                  AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
                  return;
                  }
                }

               
              FLAGS |= 0x0001 ;
              AX = ((AX & 0x00ff) | (( ret ) << 8)) ;
              return;
              break;

            case 1:  
 ;
              mouse_flags_2 = read_byte(ebda_seg, 0x0027);
              if ( (mouse_flags_2 & 0x80) == 0 ) {
                 
                FLAGS |= 0x0001 ;   
                AX = ((AX & 0x00ff) | (( 5 ) << 8)) ;  
                return;
                }
              inhibit_mouse_int_and_events();  
              ret = send_to_mouse_ctrl(0xF4);  
              if (ret == 0) {
                ret = get_mouse_data(&mouse_data1);
                if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
                  enable_mouse_int_and_events();  
                  FLAGS &= 0xfffe ;
                  AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
                  return;
                  }
                }
              FLAGS |= 0x0001 ;
              AX = ((AX & 0x00ff) | (( ret ) << 8)) ;
              return;

            default:  
               
              FLAGS |= 0x0001 ;   
              AX = ((AX & 0x00ff) | (( 1 ) << 8)) ;  
              return;
            }
          break;

        case 1:  
        case 5:  
 ;
          if (( AX & 0x00ff )  == 5) {
            if (( BX >> 8 )  != 3)
              bios_printf(1,  "INT 15h C2 AL=5, BH=%02x\n" ,(unsigned) ( BX >> 8 )  ) ;
            mouse_flags_2 = read_byte(ebda_seg, 0x0027);
            mouse_flags_2 = (mouse_flags_2 & 0x00) | ( BX >> 8 ) ;
            mouse_flags_1 = 0x00;
            write_byte(ebda_seg, 0x0026, mouse_flags_1);
            write_byte(ebda_seg, 0x0027, mouse_flags_2);
            }

          inhibit_mouse_int_and_events();  
          ret = send_to_mouse_ctrl(0xFF);  
          if (ret == 0) {
            ret = get_mouse_data(&mouse_data3);
            if (mouse_data3 != 0xfa)
              bios_printf(1,  "Mouse reset returned %02x (should be ack)\n" ,(unsigned)mouse_data3 ) ;
            if ( ret == 0 ) {
              ret = get_mouse_data(&mouse_data1);
              if ( ret == 0 ) {
                ret = get_mouse_data(&mouse_data2);
                if ( ret == 0 ) {
                   
                  enable_mouse_int_and_events();
                  FLAGS &= 0xfffe ;
                  AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
                  BX = ((BX & 0xff00) | ( mouse_data1 )) ;
                  BX = ((BX & 0x00ff) | (( mouse_data2 ) << 8)) ;
                  return;
                  }
                }
              }
            }

           
          FLAGS |= 0x0001 ;
          AX = ((AX & 0x00ff) | (( ret ) << 8)) ;
          return;

        case 2:  
 ;
          switch (( BX >> 8 ) ) {
            case 0:  
            case 1:  
            case 2:  
            case 3:  
            case 4:  
            case 5:  
            case 6:  
              FLAGS &= 0xfffe ;
              AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
              break;
            default:
              bios_printf(1,  "INT 15h C2 AL=2, BH=%02x\n" ,(unsigned) ( BX >> 8 )  ) ;
            }
          break;

        case 3:  
 ;
           
           
           
           
           
          FLAGS &= 0xfffe ;
          AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
          break;

        case 4:  
 ;
          FLAGS &= 0xfffe ;
          AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
          BX = ((BX & 0x00ff) | (( 0 ) << 8)) ;
          break;

        case 6:  
 ;
          switch (( BX >> 8 ) ) {
            case 0:  
              comm_byte = inhibit_mouse_int_and_events();  
              ret = send_to_mouse_ctrl(0xE9);  
              if (ret == 0) {
                ret = get_mouse_data(&mouse_data1);
                if (mouse_data1 != 0xfa)
                  bios_printf(1,  "Mouse status returned %02x (should be ack)\n" ,(unsigned)mouse_data1 ) ;
                if (ret == 0) {
                  ret = get_mouse_data(&mouse_data1);
                  if ( ret == 0 ) {
                    ret = get_mouse_data(&mouse_data2);
                    if ( ret == 0 ) {
                      ret = get_mouse_data(&mouse_data3);
                      if ( ret == 0 ) {
                        FLAGS &= 0xfffe ;
                        AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
                        BX = ((BX & 0xff00) | ( mouse_data1 )) ;
                        CX = ((CX & 0xff00) | ( mouse_data2 )) ;
                        DX = ((DX & 0xff00) | ( mouse_data3 )) ;
                        set_kbd_command_byte(comm_byte);  
                        return;
                        }
                      }
                    }
                  }
                }

               
              FLAGS |= 0x0001 ;
              AX = ((AX & 0x00ff) | (( ret ) << 8)) ;
              set_kbd_command_byte(comm_byte);  
              return;

            case 1:  
              FLAGS &= 0xfffe ;
              AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
              break;

            default:
              bios_printf(1,  "INT 15h C2 AL=6, BH=%02x\n" ,(unsigned) ( BX >> 8 )  ) ;
            }
          break;

        case 7:  
 ;
          mouse_driver_seg = ES;
          mouse_driver_offset = BX;
          write_word(ebda_seg, 0x0022, mouse_driver_offset);
          write_word(ebda_seg, 0x0024, mouse_driver_seg);
          mouse_flags_2 = read_byte(ebda_seg, 0x0027);
          mouse_flags_2 |= 0x80;
          write_byte(ebda_seg, 0x0027, mouse_flags_2);
          FLAGS &= 0xfffe ;
          AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
          break;

        default:
 ;
          AX = ((AX & 0x00ff) | (( 1 ) << 8)) ;  
          FLAGS |= 0x0001 ;
        }

      break;

    case 0xC4:
       ;

      FLAGS |= 0x0001 ;
      AX = ((AX & 0x00ff) | (( 0x86  ) << 8)) ;
      break;

    case 0xD8:
       ;
      FLAGS |= 0x0001 ;
      AX = ((AX & 0x00ff) | (( 0x86  ) << 8)) ;
      break;

    case 0xe0:
       ;
      FLAGS |= 0x0001 ;
      AX = ((AX & 0x00ff) | (( 0x86  ) << 8)) ;
      break;

    default:
       ;

      FLAGS |= 0x0001 ;
      AX = ((AX & 0x00ff) | (( 0x86  ) << 8)) ;
      break;
    }
}


  void
int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
  Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
{
  Bit8u scan_code, ascii_code, shift_flags;

  switch (( AX >> 8 ) ) {
    case 0x00:  

      if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
        bios_printf(1,  "KBD: int16h: out of keyboard input\n"  ) ;
        }
      AX = (scan_code << 8) | ascii_code;
      break;

    case 0x01:  
      if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
        FLAGS |= 0x0040 ;
        return;
        }
      AX = (scan_code << 8) | ascii_code;
      FLAGS &= 0xffbf ;
      break;

    case 0x02:  
       
      shift_flags = read_byte(0x0040, 0x17);
      AX = ((AX & 0xff00) | ( shift_flags )) ;
      break;

    default:
       
       
       ;
    }
}

  unsigned int
dequeue_key(scan_code, ascii_code, incr)
  Bit8u *scan_code;
  Bit8u *ascii_code;
  unsigned int incr;
{
  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
  Bit16u ss;
  Bit8u  acode, scode;





  buffer_start = read_word(0x0040, 0x0080);
  buffer_end   = read_word(0x0040, 0x0082);


  buffer_head = read_word(0x0040, 0x001a);
  buffer_tail = read_word(0x0040, 0x001c);

  if (buffer_head != buffer_tail) {
    ss = get_SS();
    acode = read_byte(0x0040, buffer_head);
    scode = read_byte(0x0040, buffer_head+1);
    write_byte(ss, ascii_code, acode);
    write_byte(ss, scan_code, scode);

    if (incr) {
      buffer_head += 2;
      if (buffer_head >= buffer_end)
        buffer_head = buffer_start;
      write_word(0x0040, 0x001a, buffer_head);
      }
    return(1);
    }
  else {
    return(0);
    }
}



  Bit8u
inhibit_mouse_int_and_events()
{
  Bit8u command_byte, prev_command_byte;

   
  if ( inb(0x64) & 0x02 )
    bios_printf(1,  "inhibmouse: keyboard input buffer full\n"  ) ;
  outb(0x64, 0x20);  
  while ( (inb(0x64) & 0x01) != 0x01 );
  prev_command_byte = inb(0x60);
  command_byte = prev_command_byte;
   
  if ( inb(0x64) & 0x02 )
    bios_printf(1,  "inhibmouse, keyboard input buffer full\n"  ) ;
  command_byte &= 0xfd;  
  command_byte |= 0x20;  
  outb(0x64, 0x60);  
  outb(0x60, command_byte);
  return(prev_command_byte);
}

  void
enable_mouse_int_and_events()
{
  Bit8u command_byte;

   
  if ( inb(0x64) & 0x02 )
    bios_printf(1,  "enabmouse: keyboard input buffer full\n"  ) ;
  outb(0x64, 0x20);  
  while ( (inb(0x64) & 0x01) != 0x01 );
  command_byte = inb(0x60);
   
  if ( inb(0x64) & 0x02 )
    bios_printf(1,  "enabmouse, keyboard input buffer full\n"  ) ;
  command_byte |= 0x02;  
  command_byte &= 0xdf;  
  outb(0x64, 0x60);  
  outb(0x60, command_byte);
}

  Bit8u
send_to_mouse_ctrl(sendbyte)
  Bit8u sendbyte;
{
  Bit8u response;

   
  if ( inb(0x64) & 0x02 )
    bios_printf(1,  "sendmouse, keyboard input buffer full\n"  ) ;
  outb(0x64, 0xD4);
  outb(0x60, sendbyte);
  return(0);
}


  Bit8u
get_mouse_data(data)
  Bit8u *data;
{
  Bit8u response;
  Bit16u ss;

  while ( (inb(0x64) & 0x21) != 0x21 ) {
    }

  response = inb(0x60);

  ss = get_SS();
  write_byte(ss, data, response);
  return(0);
}

  void
set_kbd_command_byte(command_byte)
  Bit8u command_byte;
{
  if ( inb(0x64) & 0x02 )
    bios_printf(1,  "setkbdcomm, input buffer full\n"  ) ;

  outb(0x64, 0x60);  
  outb(0x60, command_byte);
}

  void
int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
  Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
{
  Bit8u scancode, asciicode, shift_flags;

   
   
   

  scancode = ( AX & 0x00ff ) ;

  if (scancode == 0) {
     ;
    return;
    }


  shift_flags = read_byte(0x0040, 0x17);

  switch (scancode) {
    case 0x3a:  
      shift_flags |= 0x40;
      write_byte(0x0040, 0x17, shift_flags);
      break;
    case 0xba:  
      shift_flags &= ~0x40;
      write_byte(0x0040, 0x17, shift_flags);
      break;

    case 0x2a:  
      shift_flags |= 0x02;
      write_byte(0x0040, 0x17, shift_flags);
      break;
    case 0xaa:  
      shift_flags &= ~0x02;
      write_byte(0x0040, 0x17, shift_flags);
      break;

    case 0x36:  
      shift_flags |= 0x01;
      write_byte(0x0040, 0x17, shift_flags);
      break;
    case 0xb6:  
      shift_flags &= ~0x01;
      write_byte(0x0040, 0x17, shift_flags);
      break;

    case 0x1d:  
      shift_flags |= 0x04;
      write_byte(0x0040, 0x17, shift_flags);
      break;
    case 0x9d:  
      shift_flags &= ~0x04;
      write_byte(0x0040, 0x17, shift_flags);
      break;

    case 0x38:  
      shift_flags |= 0x08;
      write_byte(0x0040, 0x17, shift_flags);
      break;
    case 0xb8:  
      shift_flags &= ~0x08;
      write_byte(0x0040, 0x17, shift_flags);
      break;

    case 0x45:  
      shift_flags |= 0x20;
      write_byte(0x0040, 0x17, shift_flags);
      break;
    case 0xc5:  
      shift_flags &= ~0x20;
      write_byte(0x0040, 0x17, shift_flags);
      break;

    default:
      if (scancode & 0x80) return;  
      if (scancode > 0x53 ) {
        bios_printf(1,  "KBD: int09h_handler(): unknown scancode read!\n"  ) ;
        return;
        }
      if (shift_flags & 0x08) {  
        asciicode = scan_to_scanascii[scancode].alt;
        scancode = scan_to_scanascii[scancode].alt >> 8;
        }
      else if (shift_flags & 0x04) {  
        asciicode = scan_to_scanascii[scancode].control;
        scancode = scan_to_scanascii[scancode].control >> 8;
        }
      else if (shift_flags & 0x43) {  
         
        if ((shift_flags & 0x03) && (shift_flags & 0x40)) {
          asciicode = scan_to_scanascii[scancode].normal;
          scancode = scan_to_scanascii[scancode].normal >> 8;
          }
        else {
          asciicode = scan_to_scanascii[scancode].shift;
          scancode = scan_to_scanascii[scancode].shift >> 8;
          }
        }
      else {
        asciicode = scan_to_scanascii[scancode].normal;
        scancode = scan_to_scanascii[scancode].normal >> 8;
        }
      if (scancode==0 && asciicode==0) {
        bios_printf(1,  "KBD: int09h_handler(): scancode & asciicode are zero?\n"  ) ;
        }
      enqueue_key(scancode, asciicode);
      break;
    }
}

  void
enqueue_key(scan_code, ascii_code)
  Bit8u scan_code, ascii_code;
{
  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;

   
   





  buffer_start = read_word(0x0040, 0x0080);
  buffer_end   = read_word(0x0040, 0x0082);


  buffer_head = read_word(0x0040, 0x001A);
  buffer_tail = read_word(0x0040, 0x001C);

  temp_tail = buffer_tail;
  buffer_tail += 2;
  if (buffer_tail >= buffer_end)
    buffer_tail = buffer_start;

  if (buffer_tail == buffer_head) {
    bios_printf(1,  "KBD: dropped key scan=%02x, ascii=%02x\n" ,(int) scan_code, (int) ascii_code ) ;

    return;
    }

   write_byte(0x0040, temp_tail, ascii_code);
   write_byte(0x0040, temp_tail+1, scan_code);
   write_word(0x0040, 0x001C, buffer_tail);
}


  void
int74_function(make_farcall, Z, Y, X, status)
  Bit16u make_farcall, Z, Y, X, status;
{
  Bit8u  in_byte, index, package_count;
  Bit16u ebda_seg;
  Bit8u  mouse_flags_1, mouse_flags_2;

 ;
  make_farcall = 0;

  in_byte = inb(0x64);
  if ( (in_byte & 0x21) != 0x21 ) {
    return;
    }
  in_byte = inb(0x60);
 ;

  ebda_seg      = read_word(0x0040, 0x000E);
  mouse_flags_1 = read_byte(ebda_seg, 0x0026);
  mouse_flags_2 = read_byte(ebda_seg, 0x0027);

  if ( (mouse_flags_2 & 0x80) != 0x80 ) {
    bios_printf(1,  "int74_function:\n"  ) ;
    }

  package_count = mouse_flags_2 & 0x07;
  index = mouse_flags_1 & 0x07;
  write_byte(ebda_seg, 0x28 + index, in_byte);

  if ( (index+1) >= package_count ) {
 ;
    status = read_byte(ebda_seg, 0x0028 + 0);
    X      = read_byte(ebda_seg, 0x0028 + 1);
    Y      = read_byte(ebda_seg, 0x0028 + 2);
    Z      = 0;
    mouse_flags_1 = 0;
     
    if (mouse_flags_2 & 0x80)
      make_farcall = 1;
    }
  else {
    mouse_flags_1++;
    }
  write_byte(ebda_seg, 0x0026, mouse_flags_1);
}





  void
int13_function(DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS)
  Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS;
{
  Bit8u    drive, num_sectors, sector, head, status, mod;
  Bit8u    n_drives;
  Bit16u   cyl_mod, ax;
  Bit16u   max_cylinder, cylinder, total_sectors;
  Bit16u   hd_cylinders;
  Bit8u    hd_heads, hd_sectors;
  Bit16u   val16;
  Bit8u    sector_count;
  unsigned int i;
  Bit16u   tempbx;

  write_byte(0x0040, 0x008e, 0);   

   

   

  n_drives = inb_cmos(0x12);
  n_drives = ((n_drives & 0xf0)==0) ? 0 :
    ((n_drives & 0x0f) ? 2 : 1);

  if (!((( DX & 0x00ff ) &0x7f) < n_drives)) {  
    AX = ((AX & 0x00ff) | (( 0x01 ) << 8)) ;
    set_disk_ret_status(0x01);
    FLAGS |= 0x0001 ;  
    return;
    }

  switch (( AX >> 8 ) ) {

    case 0x00:  
 ;

      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      set_diskette_ret_status(0);
      set_diskette_current_cyl(0, 0);  
      set_diskette_current_cyl(1, 0);  
      FLAGS &= 0xfffe ;  
      return;
      break;

    case 0x01:  
 ;
      status = read_byte(0x0040, 0x0074);
      AX = ((AX & 0x00ff) | (( status ) << 8)) ;
      set_disk_ret_status(0);
       
      if (status) FLAGS |= 0x0001 ;
      else        FLAGS &= 0xfffe ;
      return;
      break;

    case 0x04:  
    case 0x02:  
      drive = ( DX & 0x00ff ) ;
      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);

      num_sectors = ( AX & 0x00ff ) ;
      cylinder    = ( CX >> 8 ) ;
      cylinder    |= ( ((Bit16u) ( CX & 0x00ff ) ) << 2) & 0x300;
      sector      = (( CX & 0x00ff )  & 0x3f);
      head        = ( DX >> 8 ) ;


      if (hd_cylinders > 1024) {
        if (hd_cylinders <= 2048) {
          cylinder <<= 1;
          }
        else if (hd_cylinders <= 4096) {
          cylinder <<= 2;
          }
        else if (hd_cylinders <= 8192) {
          cylinder <<= 3;
          }
        else {  
          cylinder <<= 4;
          }

        ax = UDIV(head, hd_heads);
        cyl_mod = ax & 0xff;
        head    = ax >> 8;
        cylinder |= cyl_mod;
        }

      if ( (cylinder >= hd_cylinders) ||
           (sector > hd_sectors) ||
           (head >= hd_heads) ) {
        AX = ((AX & 0x00ff) | (( 1 ) << 8)) ;
        set_disk_ret_status(1);
        FLAGS |= 0x0001 ;  
        return;
        }

      if ( (num_sectors > 128) || (num_sectors == 0) )
        bios_printf(1,  "int13_function(): num_sectors out of range!\n"  ) ;


      if (head > 15)
        bios_printf(1,  "hard drive BIOS:(read/verify) head > 15\n"  ) ;

      if ( ( AX >> 8 )  == 0x04 ) {
        AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
        set_disk_ret_status(0);
        FLAGS &= 0xfffe ;
        return;
        }

      status = inb(0x1f7);
      if (status & 0x80) {
        bios_printf(1,  "hard drive BIOS:(read/verify) BUSY bit set\n"  ) ;
        }
      outb(0x01f2, num_sectors);
      outb(0x01f3, sector);
      outb(0x01f4, cylinder & 0x00ff);
      outb(0x01f5, cylinder >> 8);
      outb(0x01f6, 0xa0 | ((drive&1)<<4) | (head & 0x0f));
      outb(0x01f7, 0x20);

      while (1) {
        status = inb(0x1f7);
        if ( !(status & 0x80) ) break;
        }

      if ( !(status & 0x08) ) {
         ;
        bios_printf(1,  "hard drive BIOS:(read/verify) data-request bit not set\n"  ) ;
        }

      sector_count = 0;
      tempbx = BX;

#asm
  sti  ;; enable higher priority interrupts
#endasm

      while (1) {
#asm
        ;; store temp bx in real DI register
        push bp
        mov  bp, sp
        mov  di, _int13_function.tempbx + 2 [bp]
        pop  bp

        ;; adjust if there will be an overrun
        cmp   di, #0xfe00
        jbe   i13_f02_no_adjust
i13_f02_adjust:
        sub   di, #0x0200 ; sub 512 bytes from offset
        mov   ax, es
        add   ax, #0x0020 ; add 512 to segment
        mov   es, ax

i13_f02_no_adjust:
        mov  cx, #0x0100   ;; counter (256 words = 512b)
        mov  dx, #0x01f0  ;; AT data read port

        rep
          insw ;; CX words transfered from port(DX) to ES:[DI]

i13_f02_done:
        ;; store real DI register back to temp bx
        push bp
        mov  bp, sp
        mov  _int13_function.tempbx + 2 [bp], di
        pop  bp
#endasm

        sector_count++;
        num_sectors--;
        if (num_sectors == 0) {
          status = inb(0x1f7);
          if ( (status & 0xc9) != 0x40 )
            bios_printf(1,  "no sectors left to read/verify, status is %02x\n" ,(unsigned) status ) ;
          break;
          }
        else {
          status = inb(0x1f7);
          if ( (status & 0xc9) != 0x48 )
            bios_printf(1,  "more sectors left to read/verify, status is %02x\n" ,(unsigned) status ) ;
          continue;
          }
        }

      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      AX = ((AX & 0xff00) | ( sector_count )) ;
      FLAGS &= 0xfffe ;  
      return;
      break;


    case 0x03:  
 ;
      drive = ( DX & 0x00ff ) ;
      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);

      num_sectors = ( AX & 0x00ff ) ;
      cylinder    = ( CX >> 8 ) ;
      cylinder    |= ( ((Bit16u) ( CX & 0x00ff ) ) << 2) & 0x300;
      sector      = (( CX & 0x00ff )  & 0x3f);
      head        = ( DX >> 8 ) ;

      if (hd_cylinders > 1024) {
        if (hd_cylinders <= 2048) {
          cylinder <<= 1;
          }
        else if (hd_cylinders <= 4096) {
          cylinder <<= 2;
          }
        else if (hd_cylinders <= 8192) {
          cylinder <<= 3;
          }
        else {  
          cylinder <<= 4;
          }

        ax = UDIV(head, hd_heads);
        cyl_mod = ax & 0xff;
        head    = ax >> 8;
        cylinder |= cyl_mod;
        }

      if ( (cylinder >= hd_cylinders) ||
           (sector > hd_sectors) ||
           (head >= hd_heads) ) {
        AX = ((AX & 0x00ff) | ((  1 ) << 8)) ;
        set_disk_ret_status(1);
        FLAGS |= 0x0001 ;  
        return;
        }

      if ( (num_sectors > 128) || (num_sectors == 0) )
        bios_printf(1,  "int13_function(): num_sectors out of range!\n"  ) ;

      if (head > 15)
        bios_printf(1,  "hard drive BIOS:(read) head > 15\n"  ) ;

      status = inb(0x1f7);
      if (status & 0x80) {
        bios_printf(1,  "hard drive BIOS:(read) BUSY bit set\n"  ) ;
        }
 
      outb(0x01f2, num_sectors);
      outb(0x01f3, sector);
      outb(0x01f4, cylinder & 0x00ff);
      outb(0x01f5, cylinder >> 8);
      outb(0x01f6, 0xa0 | ((drive&1)<<4) | (head & 0x0f));
      outb(0x01f7, 0x30);

       
      while (1) {
        status = inb(0x1f7);
        if ( !(status & 0x80) ) break;
        }

      if ( !(status & 0x08) ) {
         ;
        bios_printf(1,  "hard drive BIOS:(write) data-request bit not set\n"  ) ;
        }

      sector_count = 0;
      tempbx = BX;

#asm
  sti  ;; enable higher priority interrupts
#endasm

      while (1) {
#asm
        ;; store temp bx in real SI register
        push bp
        mov  bp, sp
        mov  si, _int13_function.tempbx + 2 [bp]
        pop  bp

        ;; adjust if there will be an overrun
        cmp   si, #0xfe00
        jbe   i13_f03_no_adjust
i13_f03_adjust:
        sub   si, #0x0200 ; sub 512 bytes from offset
        mov   ax, es
        add   ax, #0x0020 ; add 512 to segment
        mov   es, ax

i13_f03_no_adjust:
        mov  cx, #0x0100   ;; counter (256 words = 512b)
        mov  dx, #0x01f0  ;; AT data read port

        seg ES
        rep
          outsw ;; CX words tranfered from ES:[SI] to port(DX)

        ;; store real SI register back to temp bx
        push bp
        mov  bp, sp
        mov  _int13_function.tempbx + 2 [bp], si
        pop  bp
#endasm

        sector_count++;
        num_sectors--;
        if (num_sectors == 0) {
          status = inb(0x1f7);
          if ( (status & 0xe9) != 0x40 )
            bios_printf(1,  "no sectors left to write, status is %02x\n" ,(unsigned) status ) ;
          break;
          }
        else {
          status = inb(0x1f7);
          if ( (status & 0xc9) != 0x48 )
            bios_printf(1,  "more sectors left to write, status is %02x\n" ,(unsigned) status ) ;
          continue;
          }
        }

      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      AX = ((AX & 0xff00) | ( sector_count )) ;
      FLAGS &= 0xfffe ;  
      return;
      break;

    case 0x05:  
 ;
      bios_printf(1,  "format disk track called\n"  ) ;
       
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      FLAGS &= 0xfffe ;  
      return;
      break;

    case 0x08:  
 ;
      drive = ( DX & 0x00ff ) ;
      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);

       
       
      if (hd_cylinders <= 1024) {
         
         
        }
      else if (hd_cylinders <= 2048) {
        hd_cylinders >>= 1;
        hd_heads <<= 1;
        }
      else if (hd_cylinders <= 4096) {
        hd_cylinders >>= 2;
        hd_heads <<= 2;
        }
      else if (hd_cylinders <= 8192) {
        hd_cylinders >>= 3;
        hd_heads <<= 3;
        }
      else {  
        hd_cylinders >>= 4;
        hd_heads <<= 4;
        }

      max_cylinder = hd_cylinders - 2;  
      AX = ((AX & 0xff00) | ( 0 )) ;
      CX = ((CX & 0x00ff) | (( max_cylinder & 0xff ) << 8)) ;
      CX = ((CX & 0xff00) | ( ((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f) )) ;
      DX = ((DX & 0x00ff) | (( hd_heads - 1 ) << 8)) ;
      DX = ((DX & 0xff00) | ( n_drives )) ;  
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      FLAGS &= 0xfffe ;  
      return;
      break;

    case 0x09:  
 ;
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      FLAGS &= 0xfffe ;  
      return;
      break;

    case 0x0a:  
 ;
    case 0x0b:  
 ;
      bios_printf(1,  "int13h Functions 0Ah & 0Bh not implemented!\n"  ) ;
      return;
      break;

    case 0x0c:  
 ;
       ;
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      FLAGS &= 0xfffe ;  
      return;
      break;

    case 0x0d:  
 ;
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      FLAGS &= 0xfffe ;  
      return;
      break;

    case 0x10:  
 ;
       
       
       
       
       

       
      status = inb(0x01f7);
      if ( (status & 0xc0) == 0x40 ) {
        AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
        set_disk_ret_status(0);
        FLAGS &= 0xfffe ;  
        return;
        }
      else {
        AX = ((AX & 0x00ff) | (( 0xAA ) << 8)) ;
        set_disk_ret_status(0xAA);
        FLAGS |= 0x0001 ;  
        return;
        }
      break;

    case 0x11:  
 ;
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      FLAGS &= 0xfffe ;  
      return;
      break;

    case 0x14:  
 ;
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_disk_ret_status(0);
      FLAGS &= 0xfffe ;  
      AX = ((AX & 0xff00) | ( 0 )) ;
      return;
      break;

    case 0x15:  
      drive = ( DX & 0x00ff ) ;
      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
#asm
      push bp
      mov  bp, sp
      mov  al, _int13_function.hd_heads + 2 [bp]
      mov  ah, _int13_function.hd_sectors + 2 [bp]
      mul  al, ah ;; ax = heads * sectors
      mov  bx, _int13_function.hd_cylinders + 2 [bp]
      dec  bx     ;; use (cylinders - 1) ???
      mul  ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
      ;; now we need to move the 32bit result dx:ax to what the
      ;; BIOS wants which is cx:dx.
      ;; and then into CX:DX on the stack
      mov  _int13_function.CX + 2 [bp], dx
      mov  _int13_function.DX + 2 [bp], ax
      pop  bp
#endasm
      AX = ((AX & 0x00ff) | (( 3 ) << 8)) ;   
      set_disk_ret_status(0);  
      FLAGS &= 0xfffe ;  
      return;
      break;

    case 0x18:  
    case 0x41:  
 ;
      AX = ((AX & 0x00ff) | (( 1 ) << 8)) ;   
      set_disk_ret_status(1);
      FLAGS |= 0x0001 ;  
      return;
      break;

    default:
      bios_printf(1,  "case 0x%x found in int13_function()\n" ,(unsigned) ( AX >> 8 )  ) ;
      break;
    }
}


 
 
 

  Boolean
floppy_media_known(drive)
  Bit16u drive;
{
  Bit8u  val8;
  Bit16u media_state_offset;

  val8 = read_byte(0x0040, 0x003e);  
  if (drive)
    val8 >>= 1;
  val8 &= 0x01;
  if (val8 == 0)
    return(0);

  media_state_offset = 0x0090;
  if (drive)
    media_state_offset += 1;

  val8 = read_byte(0x0040, media_state_offset);
  val8 = (val8 >> 4) & 0x01;
  if (val8 == 0)
    return(0);

   
  return(1);
}

  Boolean
floppy_media_sense(drive)
  Bit16u drive;
{
  Boolean retval;
  Bit16u  media_state_offset;
  Bit8u   drive_type, config_data, media_state;

  if (floppy_drive_recal(drive) == 0) {
    return(0);
    }

   
   
  drive_type = inb_cmos(0x10);
  if (drive == 0)
    drive_type >>= 4;
  else
    drive_type &= 0x0f;
  if ( drive_type == 2 ) {
     
    config_data = 0x00;  
    media_state = 0x25;  
    retval = 1;
    }
  else if ( drive_type == 3 ) {
     
    config_data = 0x00;  
    media_state = 0x17;  
    retval = 1;
    }
  else if ( drive_type == 4 ) {
     
    config_data = 0x00;  
    media_state = 0x17;  
    retval = 1;
    }
  else if ( drive_type == 5 ) {
     
    config_data = 0xCC;  
    media_state = 0xD7;  
    retval = 1;
    }
  else {
     
    config_data = 0x00;  
    media_state = 0x00;  
    retval = 0;
    }

  if (drive == 0)
    media_state_offset = 0x90;
  else
    media_state_offset = 0x91;
  write_byte(0x0040, 0x008B, config_data);
  write_byte(0x0040, media_state_offset, media_state);

  return(retval);
}

  Boolean
floppy_drive_recal(drive)
  Bit16u drive;
{
  Bit8u  val8, dor;
  Bit16u curr_cyl_offset;

   
  val8 = read_byte(0x0000, 0x043e);
  val8 &= 0x7f;
  write_byte(0x0000, 0x043e, val8);

   
  if (drive)
    dor = 0x20;
  else
    dor = 0x10;
  dor |= 0x0c;
  dor |= drive;
  outb(0x03f2, dor);

   
  val8 = inb(0x3f4);
  if ( (val8 & 0xf0) != 0x80 )
    bios_printf(1,  "floppy recal:f07: ctrl not ready\n"  ) ;

   
  outb(0x03f5, 0x07);   
  outb(0x03f5, drive);  

  
#asm
  sti
#endasm

   
  val8 = (read_byte(0x0000, 0x043e) & 0x80);
  while ( val8 == 0 ) {
    val8 = (read_byte(0x0000, 0x043e) & 0x80);
    }

 val8 = 0;  
  
#asm
  cli
#endasm

   
  val8 = read_byte(0x0000, 0x043e);
  val8 &= 0x7f;
  if (drive) {
    val8 |= 0x02;  
    curr_cyl_offset = 0x0095;
    }
  else {
    val8 |= 0x01;  
    curr_cyl_offset = 0x0094;
    }
  write_byte(0x0040, 0x003e, val8);
  write_byte(0x0040, curr_cyl_offset, 0);  

  return(1);
}



  Boolean
floppy_drive_exists(drive)
  Bit16u drive;
{
  Bit8u  drive_type;

   
  drive_type = inb_cmos(0x10);
  if (drive == 0)
    drive_type >>= 4;
  else
    drive_type &= 0x0f;
  if ( drive_type == 0 )
    return(0);
  else
    return(1);
}



  void
int13_diskette_function(DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS)
  Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, FLAGS;
{
  Bit8u  drive, num_sectors, track, sector, head, status;
  Bit16u base_address, base_count, base_es;
  Bit8u  page, mode_register, val8, dor;
  Bit8u  return_status[7];
  Bit8u  drive_type, num_floppies, ah;
  Bit16u es, last_addr;

 

  ah = ( AX >> 8 ) ;

  switch ( ah ) {
    case 0x00:  
 ;
      drive = ( DX & 0x00ff ) ;
      if (drive > 1) {
        AX = ((AX & 0x00ff) | (( 1 ) << 8)) ;  
        set_diskette_ret_status(1);
        FLAGS |= 0x0001 ;
        return;
        }
      drive_type = inb_cmos(0x10);

      if (drive == 0)
        drive_type >>= 4;
      else
        drive_type &= 0x0f;
      if (drive_type == 0) {
        AX = ((AX & 0x00ff) | (( 0x80 ) << 8)) ;  
        set_diskette_ret_status(0x80);
        FLAGS |= 0x0001 ;
        return;
        }
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_diskette_ret_status(0);
      FLAGS &= 0xfffe ;  
      set_diskette_current_cyl(drive, 0);  
      return;

    case 0x01:  
      FLAGS &= 0xfffe ;
      val8 = read_byte(0x0000, 0x0441);
      AX = ((AX & 0x00ff) | (( val8 ) << 8)) ;
      if (val8) {
        FLAGS |= 0x0001 ;
        }
      return;

    case 0x02:  
    case 0x03:  
    case 0x04:  
      num_sectors = ( AX & 0x00ff ) ;
      track       = ( CX >> 8 ) ;
      sector      = ( CX & 0x00ff ) ;
      head        = ( DX >> 8 ) ;
      drive       = ( DX & 0x00ff ) ;

      if ( (drive > 1) || (head > 1) ||
           (num_sectors == 0) || (num_sectors > 72) ) {
 ;
        AX = ((AX & 0x00ff) | (( 1 ) << 8)) ;
        set_diskette_ret_status(1);
        AX = ((AX & 0xff00) | ( 0 )) ;  
        FLAGS |= 0x0001 ;  
        return;
        }

       
      if (floppy_drive_exists(drive) == 0) {
        AX = ((AX & 0x00ff) | (( 0x80 ) << 8)) ;  
        set_diskette_ret_status(0x80);
        AX = ((AX & 0xff00) | ( 0 )) ;  
        FLAGS |= 0x0001 ;  
        return;
        }

       
      if (floppy_media_known(drive) == 0) {
        if (floppy_media_sense(drive) == 0) {
          AX = ((AX & 0x00ff) | (( 0x0C ) << 8)) ;  
          set_diskette_ret_status(0x0C);
          AX = ((AX & 0xff00) | ( 0 )) ;  
          FLAGS |= 0x0001 ;  
          return;
          }
        }

      if (ah == 0x02) {
         

         
         
         

         
         
         
        page = (ES >> 12);    
        base_es = (ES << 4);  
        base_address = base_es + BX;  
                                      
        if ( base_address < base_es ) {
           
          page++;
          }
        base_count = (num_sectors * 512) - 1;

         
        last_addr = base_address + base_count;
        if (last_addr < base_address) {
          AX = ((AX & 0x00ff) | (( 0x09 ) << 8)) ;
          set_diskette_ret_status(0x09);
          AX = ((AX & 0xff00) | ( 0 )) ;  
          FLAGS |= 0x0001 ;  
          return;
          }

         ;
        outb(0x000a, 0x06);

   ;
        outb(0x000c, 0x00);  
        outb(0x0004, base_address);
        outb(0x0004, base_address>>8);
   ;
        outb(0x000c, 0x00);  
        outb(0x0005, base_count);
        outb(0x0005, base_count>>8);

         
        mode_register = 0x46;  
                               
   ;
        outb(0x000b, mode_register);

   ;
         
        outb(0x0081, page);

   ;
        outb(0x000a, 0x02);  

         ;
        outb(0x000a, 0x02);

         
         
         

         
        val8 = read_byte(0x0000, 0x043e);
        val8 &= 0x7f;
        write_byte(0x0000, 0x043e, val8);

         
        if (drive)
          dor = 0x20;
        else
          dor = 0x10;
        dor |= 0x0c;
        dor |= drive;
        outb(0x03f2, dor);

         
        val8 = inb(0x3f4);
        if ( (val8 & 0xf0) != 0x80 )
          bios_printf(1,  "int13_diskette:f02: ctrl not ready\n"  ) ;

         
        outb(0x03f5, 0xe6);  
        outb(0x03f5, (head << 2) | drive);  
        outb(0x03f5, track);
        outb(0x03f5, head);
        outb(0x03f5, sector);
        outb(0x03f5, 2);  
        outb(0x03f5, 0);  
        outb(0x03f5, 0);  
        outb(0x03f5, 0xff);  

        
  #asm
        sti
  #endasm

         
        val8 = (read_byte(0x0000, 0x043e) & 0x80);
        while ( val8 == 0 ) {
          val8 = (read_byte(0x0000, 0x043e) & 0x80);
          }

       val8 = 0;  
        
  #asm
        cli
  #endasm

         
        val8 = read_byte(0x0000, 0x043e);
        val8 &= 0x7f;
        write_byte(0x0000, 0x043e, val8);

         
        val8 = inb(0x3f4);
        if ( (val8 & 0xc0) != 0xc0 )
          bios_printf(1,  "int13_diskette: ctrl not ready\n"  ) ;

         
         
        return_status[0] = inb(0x3f5);
        return_status[1] = inb(0x3f5);
        return_status[2] = inb(0x3f5);
        return_status[3] = inb(0x3f5);
        return_status[4] = inb(0x3f5);
        return_status[5] = inb(0x3f5);
        return_status[6] = inb(0x3f5);
         
        write_byte(0x0040, 0x0042, return_status[0]);
        write_byte(0x0040, 0x0043, return_status[1]);
        write_byte(0x0040, 0x0044, return_status[2]);
        write_byte(0x0040, 0x0045, return_status[3]);
        write_byte(0x0040, 0x0046, return_status[4]);
        write_byte(0x0040, 0x0047, return_status[5]);
        write_byte(0x0040, 0x0048, return_status[6]);

        if ( (return_status[0] & 0xc0) != 0 ) {
          AX = ((AX & 0x00ff) | (( 0x20 ) << 8)) ;
          set_diskette_ret_status(0x20);
          AX = ((AX & 0xff00) | ( 0 )) ;  
          FLAGS |= 0x0001 ;  
          return;
          }

         
        set_diskette_current_cyl(drive, track);
         
        AX = ((AX & 0x00ff) | (( 0x00 ) << 8)) ;  
        FLAGS &= 0xfffe ;    
        return;
        }
      else if (ah == 0x03) {
         

         
         
         

         
         
         
        page = (ES >> 12);    
        base_es = (ES << 4);  
        base_address = base_es + BX;  
                                      
        if ( base_address < base_es ) {
           
          page++;
          }
        base_count = (num_sectors * 512) - 1;

         
        last_addr = base_address + base_count;
        if (last_addr < base_address) {
          AX = ((AX & 0x00ff) | (( 0x09 ) << 8)) ;
          set_diskette_ret_status(0x09);
          AX = ((AX & 0xff00) | ( 0 )) ;  
          FLAGS |= 0x0001 ;  
          return;
          }

         ;
        outb(0x000a, 0x06);

        outb(0x000c, 0x00);  
        outb(0x0004, base_address);
        outb(0x0004, base_address>>8);
        outb(0x000c, 0x00);  
        outb(0x0005, base_count);
        outb(0x0005, base_count>>8);

         
        mode_register = 0x4a;  
                               
        outb(0x000b, mode_register);

         
        outb(0x0081, page);

         ;
        outb(0x000a, 0x02);

         
         
         

         
        val8 = read_byte(0x0000, 0x043e);
        val8 &= 0x7f;
        write_byte(0x0000, 0x043e, val8);

         
        if (drive)
          dor = 0x20;
        else
          dor = 0x10;
        dor |= 0x0c;
        dor |= drive;
        outb(0x03f2, dor);

         
        val8 = inb(0x3f4);
        if ( (val8 & 0xf0) != 0x80 )
          bios_printf(1,  "int13_diskette:f03: ctrl not ready\n"  ) ;

         
        outb(0x03f5, 0xc5);  
        outb(0x03f5, (head << 2) | drive);  
        outb(0x03f5, track);
        outb(0x03f5, head);
        outb(0x03f5, sector);
        outb(0x03f5, 2);  
        outb(0x03f5, 0);  
        outb(0x03f5, 0);  
        outb(0x03f5, 0xff);  

        
  #asm
        sti
  #endasm

         
        val8 = (read_byte(0x0000, 0x043e) & 0x80);
        while ( val8 == 0 ) {
          val8 = (read_byte(0x0000, 0x043e) & 0x80);
          }

       val8 = 0;  
        
  #asm
        cli
  #endasm

         
        val8 = read_byte(0x0000, 0x043e);
        val8 &= 0x7f;
        write_byte(0x0000, 0x043e, val8);

         
        val8 = inb(0x3f4);
        if ( (val8 & 0xc0) != 0xc0 )
          bios_printf(1,  "int13_diskette: ctrl not ready\n"  ) ;

         
         
        return_status[0] = inb(0x3f5);
        return_status[1] = inb(0x3f5);
        return_status[2] = inb(0x3f5);
        return_status[3] = inb(0x3f5);
        return_status[4] = inb(0x3f5);
        return_status[5] = inb(0x3f5);
        return_status[6] = inb(0x3f5);
         
        write_byte(0x0040, 0x0042, return_status[0]);
        write_byte(0x0040, 0x0043, return_status[1]);
        write_byte(0x0040, 0x0044, return_status[2]);
        write_byte(0x0040, 0x0045, return_status[3]);
        write_byte(0x0040, 0x0046, return_status[4]);
        write_byte(0x0040, 0x0047, return_status[5]);
        write_byte(0x0040, 0x0048, return_status[6]);

        if ( (return_status[0] & 0xc0) != 0 ) {
	  if ( (return_status[1] & 0x02) != 0 ) {
	     
	     
	     
	    AX = 0x0300;
	    FLAGS |= 0x0001 ;
	    return;
	  } else {
            bios_printf(1,  "int13_diskette_function: read error\n"  ) ;
          }
	}

         
        set_diskette_current_cyl(drive, track);
         
        AX = ((AX & 0x00ff) | (( 0x00 ) << 8)) ;  
        FLAGS &= 0xfffe ;    
        return;
        }
      else {   
         

         
        set_diskette_current_cyl(drive, track);
         
        FLAGS &= 0xfffe ;    
        AX = ((AX & 0x00ff) | (( 0x00 ) << 8)) ;  
        return;
        }


    case 0x05:  
 ;

      num_sectors = ( AX & 0x00ff ) ;
      track       = ( CX >> 8 ) ;
      head        = ( DX >> 8 ) ;
      drive       = ( DX & 0x00ff ) ;

      if (drive > 1) {
        AX = ((AX & 0x00ff) | (( 1 ) << 8)) ;
        set_diskette_ret_status(1);
        FLAGS |= 0x0001 ;  
	}
      drive_type = inb_cmos(0x10);
      if (drive == 0)
        drive_type >>= 4;
      else
        drive_type &= 0x0f;
      if (drive_type == 0) {
        AX = ((AX & 0x00ff) | (( 0x80 ) << 8)) ;  
        set_diskette_ret_status(0x80);
        FLAGS |= 0x0001 ;  
        return;
        }

       
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      set_diskette_ret_status(0);
      set_diskette_current_cyl(drive, track);
      FLAGS &= 0xfffe ;  
      return;


    case 0x08:  
 ;
      drive = ( DX & 0x00ff ) ;

      if (drive>1) {
        AX = 0;
        BX = 0;
        CX = 0;
        DX = 0;
         
        DX = ((DX & 0xff00) | ( num_floppies )) ;
         
        FLAGS |= 0x0001 ;
        return;
        }

      drive_type = inb_cmos(0x10);
      num_floppies = 0;
      if (drive_type & 0xf0)
        num_floppies++;
      if (drive_type & 0x0f)
        num_floppies++;

      if (drive == 0)
        drive_type >>= 4;
      else
        drive_type &= 0x0f;


      BX = ((BX & 0x00ff) | (( 0 ) << 8)) ;
      BX = ((BX & 0xff00) | ( drive_type )) ;
      AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;
      AX = ((AX & 0xff00) | ( 0 )) ;
      DX = ((DX & 0xff00) | ( num_floppies )) ;

      switch (drive_type) {
        case 0:  
          CX = 0;
          DX = ((DX & 0x00ff) | (( 0 ) << 8)) ;  
          break;

        case 1:  
          CX = 0x2709;  
          DX = ((DX & 0x00ff) | (( 1 ) << 8)) ;  
          break;

        case 2:  
          CX = 0x4f0f;  
          DX = ((DX & 0x00ff) | (( 1 ) << 8)) ;  
          break;

        case 3:  
          CX = 0x4f09;  
          DX = ((DX & 0x00ff) | (( 1 ) << 8)) ;  
          break;

        case 4:  
          CX = 0x4f12;  
          DX = ((DX & 0x00ff) | (( 1 ) << 8)) ;  
          break;

        case 5:  
          CX = 0x4f24;  
          DX = ((DX & 0x00ff) | (( 1 ) << 8)) ;  
          break;

        default:  
          bios_printf(1,  "floppy: int13: bad floppy type\n"  ) ;
        }

       
      DI = read_word(0x0000, 0x0078);
      ES = read_word(0x0000, 0x007a);
      FLAGS &= 0xfffe ;  
       
      return;


    case 0x15:  
 ;
      drive = ( DX & 0x00ff ) ;
      if (drive > 1) {
        AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;  
         
        FLAGS |= 0x0001 ;
        return;
        }
      drive_type = inb_cmos(0x10);

      if (drive == 0)
        drive_type >>= 4;
      else
        drive_type &= 0x0f;
      FLAGS &= 0xfffe ;  
      if (drive_type==0) {
        AX = ((AX & 0x00ff) | (( 0 ) << 8)) ;  
        }
      else {
        AX = ((AX & 0x00ff) | (( 1 ) << 8)) ;  
        }
      return;

    case 0x16:  
 ;
      drive = ( DX & 0x00ff ) ;
      if (drive > 1) {
        AX = ((AX & 0x00ff) | (( 0x01 ) << 8)) ;  
        set_diskette_ret_status(0x01);
        FLAGS |= 0x0001 ;
        return;
        }

      AX = ((AX & 0x00ff) | (( 0x06 ) << 8)) ;  
      set_diskette_ret_status(0x06);
      FLAGS |= 0x0001 ;
      return;

    case 0x17:  
 ;
       
      AX = ((AX & 0x00ff) | (( 0x01 ) << 8)) ;  
      set_diskette_ret_status(1);  
      FLAGS |= 0x0001 ;
      return;

    case 0x18:  
 ;
      AX = ((AX & 0x00ff) | (( 0x01 ) << 8)) ;  
      set_diskette_ret_status(1);
      FLAGS |= 0x0001 ;
      return;

    default:
      if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
        AX = ((AX & 0x00ff) | (( 0x01 ) << 8)) ;  
        set_diskette_ret_status(1);
        FLAGS |= 0x0001 ;
         ;
        return;
        }
      bios_printf(1,  "int13_diskette: AH=%02x\n" ,ah ) ;
    }
}
# 2972 "rombios.c"


  void
set_disk_ret_status(val)
  Bit8u val;
{
  write_byte(0x0040, 0x0074, val);
}

 void
set_diskette_ret_status(value)
  Bit8u value;
{
  write_byte(0x0040, 0x0041, value);
}

  void
set_diskette_current_cyl(drive, cyl)
  Bit8u drive;
  Bit8u cyl;
{
  if (drive > 1)
    bios_printf(1,  "set_diskette_current_cyl(): drive > 1\n"  ) ;
  write_byte(0x0040, 0x0094+drive, cyl);
}

  void
determine_floppy_media(drive)
  Bit16u drive;
{
# 3040 "rombios.c"

}



  void
get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
  Bit8u drive;
  Bit16u *hd_cylinders;
  Bit8u  *hd_heads;
  Bit8u  *hd_sectors;
{
  Bit8u hd_type;
  Bit16u ss;
  Bit16u cylinders;
  Bit8u iobase;

  ss = get_SS();
  if (drive == 0x80) {
    hd_type = inb_cmos(0x12) & 0xf0;
    if (hd_type != 0xf0)
      bios_printf(1,  "HD0 cmos reg 12h not type F\n"  ) ;
    hd_type = inb_cmos(0x19);  
    if (hd_type != 47)
      bios_printf(1,  "HD0 cmos reg 19h not user definable type 47\n"  ) ;
    iobase = 0x1b;
  } else {
    hd_type = inb_cmos(0x12) & 0x0f;
    if (hd_type != 0x0f)
      bios_printf(1,  "HD1 cmos reg 12h not type F\n"  ) ;
    hd_type = inb_cmos(0x1a);  
    if (hd_type != 47)
      bios_printf(1,  "HD1 cmos reg 1ah not user definable type 47\n"  ) ;
    iobase = 0x24;
  }

   
  cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
  write_word(ss, hd_cylinders, cylinders);

   
  write_byte(ss, hd_heads, inb_cmos(iobase+2));

   
  write_byte(ss, hd_sectors, inb_cmos(iobase+8));
}

  void
int1a_function(regs, ds, iret_addr)
  pusha_regs_t regs;  
  Bit16u ds;  
  iret_addr_t  iret_addr;  
{
  Bit8u val8;

  #asm
  sti
  #endasm

  switch (regs.u.r8.ah) {
    case 0:  
      #asm
      cli
      #endasm
      regs.u.r16.cx = ((bios_data_t  *) 0) ->ticks_high;
      regs.u.r16.dx = ((bios_data_t  *) 0) ->ticks_low;
      regs.u.r8.al  = ((bios_data_t  *) 0) ->midnight_flag;
      ((bios_data_t  *) 0) ->midnight_flag = 0;  
      #asm
      sti
      #endasm
       
       iret_addr.flags .u.r8.flagsl &= 0xfe ;  
      break;

    case 1:  
      #asm
      cli
      #endasm
      ((bios_data_t  *) 0) ->ticks_high = regs.u.r16.cx;
      ((bios_data_t  *) 0) ->ticks_low  = regs.u.r16.dx;
      ((bios_data_t  *) 0) ->midnight_flag = 0;  
      #asm
      sti
      #endasm
      regs.u.r8.ah = 0;
       iret_addr.flags .u.r8.flagsl &= 0xfe ;  
      break;


    case 2:  
      if (rtc_updating()) {
         iret_addr.flags .u.r8.flagsl |= 0x01 ;
        break;
        }

      regs.u.r8.dh = inb_cmos(0x00);  
      regs.u.r8.cl = inb_cmos(0x02);  
      regs.u.r8.ch = inb_cmos(0x04);  
      regs.u.r8.dl = inb_cmos(0x0b) & 0x01;  
      regs.u.r8.ah = 0;
      regs.u.r8.al = regs.u.r8.ch;
       iret_addr.flags .u.r8.flagsl &= 0xfe ;  
      break;

    case 3:  
       
       
       
       
       
       
       
       
       
       
      if (rtc_updating()) {
        init_rtc();
         
        }
      outb_cmos(0x00, regs.u.r8.dh);  
      outb_cmos(0x02, regs.u.r8.cl);  
      outb_cmos(0x04, regs.u.r8.ch);  
       
      val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
       
      outb_cmos(0x0b, val8);
      regs.u.r8.ah = 0;
      regs.u.r8.al = val8;  
       iret_addr.flags .u.r8.flagsl &= 0xfe ;  
      break;

    case 4:  
      regs.u.r8.ah = 0;
      if (rtc_updating()) {
         iret_addr.flags .u.r8.flagsl |= 0x01 ;
        break;
        }
      regs.u.r8.cl = inb_cmos(0x09);  
      regs.u.r8.dh = inb_cmos(0x08);  
      regs.u.r8.dl = inb_cmos(0x07);  
      regs.u.r8.ch = inb_cmos(0x32);  
      regs.u.r8.al = regs.u.r8.ch;
       iret_addr.flags .u.r8.flagsl &= 0xfe ;  
      break;

    case 5:  
       
       
       
       
       
       
       
       
       
       
      if (rtc_updating()) {
        init_rtc();
         iret_addr.flags .u.r8.flagsl |= 0x01 ;
        break;
        }
      outb_cmos(0x09, regs.u.r8.cl);  
      outb_cmos(0x08, regs.u.r8.dh);  
      outb_cmos(0x07, regs.u.r8.dl);  
      outb_cmos(0x32, regs.u.r8.ch);  
      val8 = inb_cmos(0x0b) & 0x7f;  
      outb_cmos(0x0b, val8);
      regs.u.r8.ah = 0;
      regs.u.r8.al = val8;  
       iret_addr.flags .u.r8.flagsl &= 0xfe ;  
      break;

    case 6:  
       
       
       
       
       
       
       
       
       
       
      val8 = inb_cmos(0x0b);  
      regs.u.r16.ax = 0;
      if (val8 & 0x20) {
         
         iret_addr.flags .u.r8.flagsl |= 0x01 ;  
        break;
        }
      if (rtc_updating()) {
        init_rtc();
         
        }
      outb_cmos(0x01, regs.u.r8.dh);  
      outb_cmos(0x03, regs.u.r8.cl);  
      outb_cmos(0x05, regs.u.r8.ch);  
      outb(0xa1, inb(0xa1) & 0xfe);  
       
      outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
       iret_addr.flags .u.r8.flagsl &= 0xfe ;  
      break;

    case 7:  
       
       
       
       
       
       
       
       
       
       
      val8 = inb_cmos(0x0b);  
       
      outb_cmos(0x0b, val8 & 0x57);  
      regs.u.r8.ah = 0;
      regs.u.r8.al = val8;  
       iret_addr.flags .u.r8.flagsl &= 0xfe ;  
      break;

    default:
       iret_addr.flags .u.r8.flagsl |= 0x01 ;  
    }
}

  void
int70_function(regs, ds, iret_addr)
  pusha_regs_t regs;  
  Bit16u ds;  
  iret_addr_t  iret_addr;  
{
   
  Bit8u val8;

  val8 = inb_cmos(0x0c);  
  if (val8 == 0) bios_printf(1,  "int70: regC 0\n"  ) ;
  if (val8 & 0x40) bios_printf(1,  "int70: periodic request\n"  ) ;
  if (val8 & 0x20) {
     
     
#asm
    sti
     
     
     
    int #0x4a
    cli
#endasm
    }

#asm
  ;; send EOI to slave & master PICs
  mov  al, #0x20
  out  #0xA0, al ;; slave  PIC EOI
  out  #0x20, al ;; master PIC EOI
#endasm
}



#asm
;------------------------------------------
;- INT74h : PS/2 mouse hardware interrupt -
;------------------------------------------
int74_handler:
  sti
  pusha
  push ds         ;; save DS
  push #0x00 ;; placeholder for status
  push #0x00 ;; placeholder for X
  push #0x00 ;; placeholder for Y
  push #0x00 ;; placeholder for Z
  push #0x00 ;; placeholder for make_far_call boolean
  call _int74_function
  pop  cx      ;; remove make_far_call from stack
  jcxz int74_done

  ;; make far call to EBDA:0022
  push #0x00
  pop ds
  push 0x040E     ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
  pop ds
   
  call far ptr[0x22]
int74_done:
  cli
  mov  al, #0x20
  ;; send EOI to slave & master PICs
  out  #0xA0, al ;; slave  PIC EOI
  out  #0x20, al ;; master PIC EOI
  add sp, #8     ;; pop status, x, y, z

  pop ds          ;; restore DS
  popa
  iret


;; This will perform an IRET, but will retain value of current CF
;; by altering flags on stack.  Better than RETF #02.
iret_modify_cf:
  jc   carry_set
  push bp
  mov  bp, sp
  and  BYTE [bp + 0x06], #0xfe
  pop  bp
  iret
carry_set:
  push bp
  mov  bp, sp
  or   BYTE [bp + 0x06], #0x01
  pop  bp
  iret


;----------------------
;- INT13h (relocated) -
;----------------------
int13_relocated:
  pushf
  test  dl, #0x80
  jz    int13_floppy

int13_disk:
  ;; pushf already done
  push  es
  pusha
  call  _int13_function
  popa
  pop   es
  popf
   
  jmp iret_modify_cf
int13_floppy:
  popf
   
  jmp int13_diskette


;----------------------
;- INT19h (relocated) -
;----------------------
int19_relocated:
  ;; check bit 5 in CMOS reg 0x2d.  load either 0x00 or 0x80 into DL
  ;; in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
  ;;   0: system boot sequence, first drive C: then A:
  ;;   1: system boot sequence, first drive A: then C:

  mov  al, #0x2d
  out  0x70, al
  in   al, 0x71
  and  al, #0x20
  jz   int19_usedisk

int19_usefloppy:
  mov  dl, #0x00
  jmp  int19_loadsector

int19_usedisk:
  mov  dl, #0x80

int19_loadsector:
  mov  ax, #0x0000
  mov  es, ax         ;; seg = 0000
  mov  bx, #0x7c00    ;; load boot sector into 0000:7c000
  mov  ah, #0x02      ;; function 2, read diskette sector
  mov  al, #0x01      ;; read 1 sector
  mov  ch, #0x00      ;; track 0
  mov  cl, #0x01      ;; sector 1
  mov  dh, #0x00      ;; head 0
  int #0x13
  jc  bootstrap_problem
  JMP_AP(0x0000, 0x7c00)
bootstrap_problem:
  xor dh,dh
  push dx
  call _boot_failure_msg
  int #0x18 ;; Boot failure
  iret

;----------
;- INT18h -
;----------
int18_handler: ;; Boot Failure routing
  HALT(3426)
  iret


;----------
;- INT1Ch -
;----------
int1c_handler: ;; User Timer Tick
  iret


;----------------------
;- POST: Floppy Drive -
;----------------------
floppy_drive_post:
  mov  ax, #0x0000
  mov  ds, ax

  mov  al, #0x00
  mov  0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred

  mov  0x043f, al  ;; diskette motor status: read op, drive0, motors off

  mov  0x0440, al  ;; diskette motor timeout counter: not active
  mov  0x0441, al  ;; diskette controller status return code

  mov  0x0442, al  ;; disk & diskette controller status register 0
  mov  0x0443, al  ;; diskette controller status register 1
  mov  0x0444, al  ;; diskette controller status register 2
  mov  0x0445, al  ;; diskette controller cylinder number
  mov  0x0446, al  ;; diskette controller head number
  mov  0x0447, al  ;; diskette controller sector number
  mov  0x0448, al  ;; diskette controller bytes written

  mov  0x048b, al  ;; diskette configuration data

  ;; -----------------------------------------------------------------
  ;; (048F) diskette controller information
  ;;
  mov  al, #0x10   ;; get CMOS diskette drive type
  out  0x70, AL
  in   AL, 0x71
  mov  ah, al      ;; save byte to AH

look_drive0:
  shr  al, #4      ;; look at top 4 bits for drive 0
  jz   f0_missing  ;; jump if no drive0
  mov  bl, #0x07   ;; drive0 determined, multi-rate, has changed line
  jmp  look_drive1
f0_missing:
  mov  bl, #0x00   ;; no drive0

look_drive1:
  mov  al, ah      ;; restore from AH
  and  al, #0x0f   ;; look at bottom 4 bits for drive 1
  jz   f1_missing  ;; jump if no drive1
  or   bl, #0x70   ;; drive1 determined, multi-rate, has changed line
f1_missing:
                   ;; leave high bits in BL zerod
  mov  0x048f, bl  ;; put new val in BDA (diskette controller information)
  ;; -----------------------------------------------------------------

  mov  al, #0x00
  mov  0x0490, al  ;; diskette 0 media state
  mov  0x0491, al  ;; diskette 1 media state

                   ;; diskette 0,1 operational starting state
                   ;; drive type has not been determined,
                   ;; has no changed detection line
  mov  0x0492, al
  mov  0x0493, al

  mov  0x0494, al  ;; diskette 0 current cylinder
  mov  0x0495, al  ;; diskette 1 current cylinder

  mov  al, #0x02
  out  #0x0a, al   ;; clear DMA-1 channel 2 mask bit

  SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table)
  SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
  SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6

  ret



;--------------------
;- POST: HARD DRIVE -
;--------------------
; relocated here because the primary POST area isnt big enough.
hard_drive_post:
   
   

  mov  al, #0x0a   ; 0000 1010 = reserved, disable IRQ 14
  mov  dx, #0x03f6
  out  dx, al

  mov  ax, #0x0000
  mov  ds, ax
  mov  0x0474, al  
  mov  0x0477, al  
  mov  0x048c, al  
  mov  0x048d, al  
  mov  0x048e, al  
  mov  al, #0x01
  mov  0x0475, al  
  mov  al, #0xc0
  mov  0x0476, al  
  SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
  SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
  ;; INT 41h: hard disk 0 configuration pointer
  ;; INT 46h: hard disk 1 configuration pointer
  SET_INT_VECTOR(0x41, #0x9FC0 , #0x003D)
  SET_INT_VECTOR(0x46, #0x9FC0 , #0x004D)

  ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
  mov  al, #0x12
  out  #0x70, al
  in   al, #0x71
  and  al, #0xf0
  cmp  al, #0xf0
  je   post_d0_extended
  jmp check_for_hd1
post_d0_extended:
  mov  al, #0x19
  out  #0x70, al
  in   al, #0x71
  cmp  al, #47  ;; decimal 47 - user definable
  je   post_d0_type47
  HALT(3556)
post_d0_type47:
  ;; CMOS  purpose                  param table offset
  ;; 1b    cylinders low            0
  ;; 1c    cylinders high           1
  ;; 1d    heads                    2
  ;; 1e    write pre-comp low       5
  ;; 1f    write pre-comp high      6
  ;; 20    retries/bad map/heads>8  8
  ;; 21    landing zone low         C
  ;; 22    landing zone high        D
  ;; 23    sectors/track            E

  mov  ax, #0x9FC0 
  mov  ds, ax

  ;;; Filling EBDA table for hard disk 0.
  mov  al, #0x1f
  out  #0x70, al
  in   al, #0x71
  mov  ah, al
  mov  al, #0x1e
  out  #0x70, al
  in   al, #0x71
  mov   (0x003d + 0x05), ax ;; write precomp word

  mov  al, #0x20
  out  #0x70, al
  in   al, #0x71
  mov   (0x003d + 0x08), al ;; drive control byte

  mov  al, #0x22
  out  #0x70, al
  in   al, #0x71
  mov  ah, al
  mov  al, #0x21
  out  #0x70, al
  in   al, #0x71
  mov   (0x003d + 0x0C), ax ;; landing zone word

  mov  al, #0x1c   ;; get cylinders word in AX
  out  #0x70, al
  in   al, #0x71   ;; high byte
  mov  ah, al
  mov  al, #0x1b
  out  #0x70, al
  in   al, #0x71   ;; low byte
  mov  bx, ax      ;; BX = cylinders

  mov  al, #0x1d
  out  #0x70, al
  in   al, #0x71
  mov  cl, al      ;; CL = heads

  mov  al, #0x23
  out  #0x70, al
  in   al, #0x71
  mov  dl, al      ;; DL = sectors

  cmp  bx, #1024
  jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS

hd0_post_physical_chs:
  ;; no logical CHS mapping used, just physical CHS
  ;; use Standard Fixed Disk Parameter Table (FDPT)
  mov   (0x003d + 0x00), bx ;; number of physical cylinders
  mov   (0x003d + 0x02), cl ;; number of physical heads
  mov   (0x003d + 0x0E), dl ;; number of physical sectors
  jmp check_for_hd1

hd0_post_logical_chs:
  ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
  mov   (0x003d + 0x09), bx ;; number of physical cylinders
  mov   (0x003d + 0x0b), cl ;; number of physical heads
  mov   (0x003d + 0x04), dl ;; number of physical sectors
  mov   (0x003d + 0x0e), dl ;; number of logical sectors (same)
  mov al, #0xa0
  mov   (0x003d + 0x03), al ;; A0h signature, indicates translated table

  cmp bx, #2048
  jnbe hd0_post_above_2048
  ;; 1024 < c <= 2048 cylinders
  shr bx, #0x01
  shl cl, #0x01
  jmp hd0_post_store_logical

hd0_post_above_2048:
  cmp bx, #4096
  jnbe hd0_post_above_4096
  ;; 2048 < c <= 4096 cylinders
  shr bx, #0x02
  shl cl, #0x02
  jmp hd0_post_store_logical

hd0_post_above_4096:
  cmp bx, #8192
  jnbe hd0_post_above_8192
  ;; 4096 < c <= 8192 cylinders
  shr bx, #0x03
  shl cl, #0x03
  jmp hd0_post_store_logical

hd0_post_above_8192:
  ;; 8192 < c <= 16384 cylinders
  shr bx, #0x04
  shl cl, #0x04

hd0_post_store_logical:
  mov   (0x003d + 0x00), bx ;; number of physical cylinders
  mov   (0x003d + 0x02), cl ;; number of physical heads
  ;; checksum
  mov   cl, #0x0f     ;; repeat count
  mov   si, #0x003d   ;; offset to disk0 FDPT
  mov   al, #0x00     ;; sum
hd0_post_checksum_loop:
  add   al, [si]
  inc   si
  dec   cl
  jnz hd0_post_checksum_loop
  not   al  ;; now take 2s complement
  inc   al
  mov   [si], al
;;; Done filling EBDA table for hard disk 0.


check_for_hd1:
  ;; is there really a second hard disk?  if not, return now
  mov  al, #0x12
  out  #0x70, al
  in   al, #0x71
  and  al, #0x0f
  jnz   post_d1_exists
  ret
post_d1_exists:
  ;; check that the hd type is really 0x0f.
  cmp al, #0x0f
  jz post_d1_extended
  HALT(3693)
post_d1_extended:
  ;; check that the extended type is 47 - user definable
  mov  al, #0x1a
  out  #0x70, al
  in   al, #0x71
  cmp  al, #47  ;; decimal 47 - user definable
  je   post_d1_type47
  HALT(3701)
post_d1_type47:
  ;; Table for disk1.
  ;; CMOS  purpose                  param table offset
  ;; 0x24    cylinders low            0
  ;; 0x25    cylinders high           1
  ;; 0x26    heads                    2
  ;; 0x27    write pre-comp low       5
  ;; 0x28    write pre-comp high      6
  ;; 0x29    heads>8                  8
  ;; 0x2a    landing zone low         C
  ;; 0x2b    landing zone high        D
  ;; 0x2c    sectors/track            E
;;; Fill EBDA table for hard disk 1.
  mov  al, #0x28
  out  #0x70, al
  in   al, #0x71
  mov  ah, al
  mov  al, #0x27
  out  #0x70, al
  in   al, #0x71
  mov   (0x004d + 0x05), ax ;; write precomp word

  mov  al, #0x29
  out  #0x70, al
  in   al, #0x71
  mov   (0x004d + 0x08), al ;; drive control byte

  mov  al, #0x2b
  out  #0x70, al
  in   al, #0x71
  mov  ah, al
  mov  al, #0x2a
  out  #0x70, al
  in   al, #0x71
  mov   (0x004d + 0x0C), ax ;; landing zone word

  mov  al, #0x25   ;; get cylinders word in AX
  out  #0x70, al
  in   al, #0x71   ;; high byte
  mov  ah, al
  mov  al, #0x24
  out  #0x70, al
  in   al, #0x71   ;; low byte
  mov  bx, ax      ;; BX = cylinders

  mov  al, #0x26
  out  #0x70, al
  in   al, #0x71
  mov  cl, al      ;; CL = heads

  mov  al, #0x2c
  out  #0x70, al
  in   al, #0x71
  mov  dl, al      ;; DL = sectors

  cmp  bx, #1024
  jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS

hd1_post_physical_chs:
  ;; no logical CHS mapping used, just physical CHS
  ;; use Standard Fixed Disk Parameter Table (FDPT)
  mov   (0x004d + 0x00), bx ;; number of physical cylinders
  mov   (0x004d + 0x02), cl ;; number of physical heads
  mov   (0x004d + 0x0E), dl ;; number of physical sectors
  ret

hd1_post_logical_chs:
  ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
  mov   (0x004d + 0x09), bx ;; number of physical cylinders
  mov   (0x004d + 0x0b), cl ;; number of physical heads
  mov   (0x004d + 0x04), dl ;; number of physical sectors
  mov   (0x004d + 0x0e), dl ;; number of logical sectors (same)
  mov al, #0xa0
  mov   (0x004d + 0x03), al ;; A0h signature, indicates translated table

  cmp bx, #2048
  jnbe hd1_post_above_2048
  ;; 1024 < c <= 2048 cylinders
  shr bx, #0x01
  shl cl, #0x01
  jmp hd1_post_store_logical

hd1_post_above_2048:
  cmp bx, #4096
  jnbe hd1_post_above_4096
  ;; 2048 < c <= 4096 cylinders
  shr bx, #0x02
  shl cl, #0x02
  jmp hd1_post_store_logical

hd1_post_above_4096:
  cmp bx, #8192
  jnbe hd1_post_above_8192
  ;; 4096 < c <= 8192 cylinders
  shr bx, #0x03
  shl cl, #0x03
  jmp hd1_post_store_logical

hd1_post_above_8192:
  ;; 8192 < c <= 16384 cylinders
  shr bx, #0x04
  shl cl, #0x04

hd1_post_store_logical:
  mov   (0x004d + 0x00), bx ;; number of physical cylinders
  mov   (0x004d + 0x02), cl ;; number of physical heads
  ;; checksum
  mov   cl, #0x0f     ;; repeat count
  mov   si, #0x004d   ;; offset to disk0 FDPT
  mov   al, #0x00     ;; sum
hd1_post_checksum_loop:
  add   al, [si]
  inc   si
  dec   cl
  jnz hd1_post_checksum_loop
  not   al  ;; now take 2s complement
  inc   al
  mov   [si], al
;;; Done filling EBDA table for hard disk 0.

  ret


BcdToBin:
  ;; in:  AL in BCD format
  ;; out: AL in binary format, AH will always be 0
  ;; trashes BX
  mov  bl, al
  and  bl, #0x0f ;; bl has low digit
  shr  al, #4    ;; al has high digit
  mov  bh, #10
  mul  al, bh    ;; multiply high digit by 10 (result in AX)
  add  al, bl    ;;   then add low digit
  ret

timer_tick_post:
  ;; Setup the Timer Ticks Count (0x46C:dword) and
  ;;   Timer Ticks Roller Flag (0x470:byte)
  ;; The Timer Ticks Count needs to be set according to
  ;; the current CMOS time, as if ticks have been occurring
  ;; at 18.2hz since midnight up to this point.  Calculating
  ;; this is a little complicated.  Here are the factors I gather
  ;; regarding this.  14,318,180 hz was the original clock speed,
  ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
  ;; at the time, or 4 to drive the CGA video adapter.  The div3
  ;; source was divided again by 4 to feed a 1.193Mhz signal to
  ;; the timer.  With a maximum 16bit timer count, this is again
  ;; divided down by 65536 to 18.2hz.
  ;;
  ;; 14,318,180 Hz clock
  ;;   /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
  ;;   /4 = 1,193,181 Hz fed to timer
  ;;   /65536 (maximum timer count) = 18.20650736 ticks/second
  ;; 1 second = 18.20650736 ticks
  ;; 1 minute = 1092.390442 ticks
  ;; 1 hour   = 65543.42651 ticks
  ;;
  ;; Given the values in the CMOS clock, one could calculate
  ;; the number of ticks by the following:
  ;;   ticks = (BcdToBin(seconds) * 18.206507) +
  ;;           (BcdToBin(minutes) * 1092.3904)
  ;;           (BcdToBin(hours)   * 65543.427)
  ;; To get a little more accuracy, since Im using integer
  ;; arithmatic, I use:
  ;;   ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
  ;;           (BcdToBin(minutes) * 10923904) / 10000 +
  ;;           (BcdToBin(hours)   * 65543427) / 1000

  ;; assuming DS=0000

  ;; get CMOS seconds
  xor  eax, eax ;; clear EAX
  mov  al, #0x00
  out  #0x70, al
  in   al, #0x71 ;; AL has CMOS seconds in BCD
  call BcdToBin  ;; EAX now has seconds in binary
  mov  edx, #18206507
  mul  eax, edx
  mov  ebx, #1000000
  xor  edx, edx
  div  eax, ebx
  mov  ecx, eax  ;; ECX will accumulate total ticks

  ;; get CMOS minutes
  xor  eax, eax ;; clear EAX
  mov  al, #0x02
  out  #0x70, al
  in   al, #0x71 ;; AL has CMOS minutes in BCD
  call BcdToBin  ;; EAX now has minutes in binary
  mov  edx, #10923904
  mul  eax, edx
  mov  ebx, #10000
  xor  edx, edx
  div  eax, ebx
  add  ecx, eax  ;; add to total ticks

  ;; get CMOS hours
  xor  eax, eax ;; clear EAX
  mov  al, #0x04
  out  #0x70, al
  in   al, #0x71 ;; AL has CMOS hours in BCD
  call BcdToBin  ;; EAX now has hours in binary
  mov  edx, #65543427
  mul  eax, edx
  mov  ebx, #1000
  xor  edx, edx
  div  eax, ebx
  add  ecx, eax  ;; add to total ticks

  mov  0x46C, ecx ;; Timer Ticks Count
  xor  al, al
  mov  0x470, al  ;; Timer Ticks Rollover Flag
  ret


int76_handler:
  ;; record completion in BIOS task complete flag
  push  ax
  push  ds
  mov   ax, #0x0040
  mov   ds, ax
  mov   0x008E, #0xff
  mov   al, #0x20
  out   #0xA0, al ;; slave  PIC EOI
  out   #0x20, al ;; master PIC EOI
  pop   ds
  pop   ax
  iret

;; for 'C' strings and other data, insert them here with
;; a the following hack:
;; DATA_SEG_DEFS_HERE


;--------
;- POST -
;--------
.org 0xe05b ; POST Entry Point
post:

  ;; Examine CMOS shutdown status.
  ;;    0 = normal startup
  mov AL, #0x0f
  out 0x70, AL
  in  AL, 0x71
  cmp AL, #0x00
  jz normal_post
  HALT(3949)
  ;
  mov AL, #0x0f
  out 0x70, AL          ; select CMOS register Fh
  mov AL, #0x00
  out 0x71, AL          ; set shutdown action to normal
  ;
  ;#if 0
  ;  0xb0, 0x20,        
  ;  0xe6, 0x20,        
  ;#endif
  ;
  pop es
  pop ds
  popa
  iret

normal_post:
  ; case 0: normal startup

  cli
  mov  ax, #0xfffe
  mov  sp, ax
  mov  ax, #0x0000
  mov  ds, ax
  mov  ss, ax

  ;; zero out BIOS data area (40:00..40:ff)
  mov  es, ax
  mov  cx, #0x0080 ;; 128 words
  mov  di, #0x0400
  cld
  rep
    stosw

  ;; set all interrupts to default handler
  mov  bx, #0x0000    ;; offset index
  mov  cx, #0x0100    ;; counter (256 interrupts)
  mov  ax, #dummy_iret_handler
  mov  dx, #0xF000

post_default_ints:
  mov  [bx], ax
  inc  bx
  inc  bx
  mov  [bx], dx
  inc  bx
  inc  bx
  loop post_default_ints

  ;; base memory in K 40:13 (word)
  mov  ax, #(640 - 1) 
  mov  0x0413, ax


  ;; Manufacturing Test 40:12
  ;;   zerod out above

  ;; Warm Boot Flag 0040:0072
  ;;   value of 1234h = skip memory checks
  ;;   zerod out above


  ;; Printer Services vector
  SET_INT_VECTOR(0x17, #0xF000, #int17_handler)

  ;; Bootstrap failure vector
  SET_INT_VECTOR(0x18, #0xF000, #int18_handler)

  ;; Bootstrap Loader vector
  SET_INT_VECTOR(0x19, #0xF000, #int19_handler)

  ;; User Timer Tick vector
  SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)

  ;; Memory Size Check vector
  SET_INT_VECTOR(0x12, #0xF000, #int12_handler)

  ;; Equipment Configuration Check vector
  SET_INT_VECTOR(0x11, #0xF000, #int11_handler)

  ;; System Services
  SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
  mov ax, #0x0000       ; mov EBDA seg into 40E
  mov ds, ax
  mov 0x40E, #0x9FC0 

  ;; PIT setup
  SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
  ;; int 1C already points at dummy_iret_handler (above)
  mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
  out 0x43, al
  mov al, #0x00 ; maximum count of 0000H = 18.2Hz
  out 0x40, al
  out 0x40, al

  ;; Keyboard
  SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
  SET_INT_VECTOR(0x16, #0xF000, #int16_handler)

  mov ax, #0x0000
  mov ds, ax
  mov  0x0417, al  
  mov  0x0418, al  
  mov  0x0419, al  
  mov  0x0471, al  
  mov  0x0496, al  
  mov  0x0497, al  


   
  mov  bx, #0x001E
  mov  0x041A, bx

   
  mov  0x041C, bx

   
 
 


   
  mov  bx, #0x001E
  mov  0x0480, bx

   
  mov  bx, #0x003E
  mov  0x0482, bx

   
  mov  al, #0xaa
  out  0x64, al
  in   al, 0x60
  cmp  al, #0x55
  je   keyboard_ok
  call _keyboard_panic
 keyboard_ok:



  in   al, 0xa1
  and  al, #0xef
  out  0xa1, al

   

  mov  al, #0x14
  out  0x70, al
  in   al, 0x71
  or   al, #0x04
  out  0x71, al


  ;; mov CMOS Equipment Byte to BDA Equipment Word
  mov  ax, 0x0410
  mov  al, #0x14
  out  0x70, al
  in   al, 0x71
  mov  0x0410, ax


  ;; DMA
  ;; nothing for now

  ;; Parallel setup
  SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
  mov ax, #0x0000
  mov ds, ax
  mov 0x408, AX ; Parallel I/O address, port 1
  mov 0x40A, AX ; Parallel I/O address, port 2
  mov 0x40C, AX ; Parallel I/O address, port 3
  mov 0x478, AL ; Parallel printer 1 timeout
  mov 0x479, AL ; Parallel printer 2 timerout
  mov 0x47A, AL ; Parallel printer 3 timerout
  mov 0x47B, AL ; Parallel printer 4 timerout

  ;; Serial setup
  SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
  SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
  ;; assuming AX==0, DS==0 from above
  mov 0x400, AX   ; Serial I/O address, port 1
  mov 0x402, AX   ; Serial I/O address, port 2
  mov 0x404, AX   ; Serial I/O address, port 3
  mov 0x406, AX   ; Serial I/O address, port 4
  mov 0x47C, AL   ; Serial 1 timeout
  mov 0x47D, AL   ; Serial 2 timeout
  mov 0x47E, AL   ; Serial 3 timeout
  mov 0x47F, AL   ; Serial 4 timeout
  mov AX, 0x410   ; Equipment word bits 9..11 determing # serial ports
  and AX, #0xf1ff ; clear bits 9..11 for now (zero ports)
  mov 0x410, AX

  ;; CMOS RTC
  SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
  SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
  SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
  ;; BIOS DATA AREA 0x4CE ???
  call timer_tick_post

  ;; PS/2 mouse setup
  SET_INT_VECTOR(0x74, #0xF000, #int74_handler)

  ;; Video setup
  SET_INT_VECTOR(0x10, #0xF000, #int10_handler)

  ;; Call extension ROMs - scan C0000 to F4000 in 800 steps

  mov  bx, #0xc000
romscan:
  mov  ds, bx
  mov  ax, 0x0000
  cmp  ax, #0xAA55
  jne  notrom
  xor  ax,ax
  mov  ds,ax
  push bx
  push #3
  mov  bp,sp
  db   0xff  ; call 0[bp]
  db   0x5e
  db   0
  pop  ax
  pop  bx
notrom:
  add  bx,#0x80
  cmp  bx,#0xf400
  jne  romscan
  xor  ax,ax
  mov  ds,ax

  ;; PIC
  mov  al, #0x00
  out  0x21, AL ;master pic: all IRQs unmasked
  out  0xA1, AL ;slave  pic: all IRQs unmasked

  ;;
  ;; Hard Drive setup
  ;;
  call hard_drive_post

  ;;
  ;; Floppy setup
  ;;
  call floppy_drive_post

  call _print_bios_banner

  int  #0x19
   


.org 0xe2c3 ; NMI Handler Entry Point
  call _nmi_handler_msg
  HALT(4203)
  iret

;-------------------------------------------
;- INT 13h Fixed Disk Services Entry Point -
;-------------------------------------------
.org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
int13_handler:
   
  jmp int13_relocated
.org 0xe401 ; Fixed Disk Parameter Table


;----------
;- INT19h -
;----------
.org 0xe6f2 ; INT 19h Boot Load Service Entry Point
int19_handler:
   
  jmp int19_relocated
;-------------------------------------------
;- System BIOS Configuration Data Table
;-------------------------------------------
.org 0xe6f5 
db 0x08                  ; Table size (bytes) -Lo
     db 0x00             ; Table size (bytes) -Hi
db 0xFC 
db 0x00 
db 1 
; Feature byte 1
; b7: 1=DMA channel 3 used by hard disk
; b6: 1=2 interrupt controllers present
; b5: 1=RTC present
; b4: 1=BIOS calls int 15h/4Fh every key
; b3: 1=wait for extern event supported (Int 15h/41h)
; b2: 1=extended BIOS data area used
; b1: 0=AT or ESDI bus, 1=MicroChannel
; b0: 1=Dual bus (MicroChannel + ISA)
db (0 << 7) |    (1 << 6) |    (1 << 5) |    (1  << 4) |    (0 << 3) |    (1  << 2) |    (0 << 1) |    (0 << 0)







; Feature byte 2
; b7: 1=32-bit DMA supported
; b6: 1=int16h, function 9 supported
; b5: 1=int15h/C6h (get POS data) supported
; b4: 1=int15h/C7h (get mem map info) supported
; b3: 1=int15h/C8h (en/dis CPU) supported
; b2: 1=non-8042 kb controller
; b1: 1=data streaming supported
; b0: reserved
db 0x00
; Feature byte 3
; b7: not used
; b6: reserved
; b5: reserved
; b4: POST supports ROM-to-RAM enable/disable
; b3: SCSI on system board
; b2: info panel installed
; b1: Initial Machine Load (IML) system - BIOS on disk
; b0: SCSI supported in IML
db 0x00
; Feature byte 4
; b7: IBM private
; b6: EEPROM present
; b5-3: ABIOS presence (011 = not supported)
; b2: private
; b1: memory split above 16Mb supported
; b0: POSTEXT directly supported by POST
db 0x00
; Feature byte 5 (IBM)
; b1: enhanced mouse
; b0: flash EPROM
db 0x00



.org 0xe729 ; Baud Rate Generator Table

;----------
;- INT14h -
;----------
.org 0xe739 ; INT 14h Serial Communications Service Entry Point
int14_handler:
  ;; ??? should post message here
  iret


;----------------------------------------
;- INT 16h Keyboard Service Entry Point -
;----------------------------------------
.org 0xe82e
int16_handler:

  push  ds
  pushf
  pusha

  cmp   ah, #0x00
  je    int16_F00

  mov  bx, #0xf000
  mov  ds, bx
  call _int16_function
  popa
  popf
  pop  ds
  jz   int16_zero_set

int16_zero_clear:
  push bp
  mov  bp, sp
   
  and  BYTE [bp + 0x06], #0xbf
  pop  bp
  iret

int16_zero_set:
  push bp
  mov  bp, sp
   
  or   BYTE [bp + 0x06], #0x40
  pop  bp
  iret

int16_F00:
  mov  bx, #0x0040
  mov  ds, bx

int16_wait_for_key:
  cli
  mov  bx, 0x001a
  cmp  bx, 0x001c
  jne  int16_key_found
  sti
  nop








  jmp  int16_wait_for_key

int16_key_found:
  mov  bx, #0xf000
  mov  ds, bx
  call _int16_function
  popa
  popf
  pop  ds







  iret



;-------------------------------------------------
;- INT09h : Keyboard Harware Service Entry Point -
;-------------------------------------------------
.org 0xe987
int09_handler:
  cli
  push ax

  mov al, #0xAD      ;;disable keyboard
  out #0x64, al

  sti

  ;; see if there is really a key to read from the controller
  in   al, #0x64
  test al, #0x01
  jz   int09_done    ;; nope, skip processing

  in  al, #0x60             ;;read key from keyboard controller
   
   


  mov  ah, #0x4f     ;; allow for keyboard intercept
  stc
  int  #0x15
  jnc  int09_done



 
  push  ds
  pusha
  mov   bx, #0xf000
  mov   ds, bx
  call  _int09_function
  popa
  pop   ds

int09_done:
  cli
  ;; look at PIC in-service-register to see if EOI required
  mov  al, #0x0B
  out  #0x20, al
  in   al, #0x20
  and  al, #0x02     ;; IRQ 1 in service
  jz   int09_finish
  mov  al, #0x20     ;; send EOI to master PIC
  out  #0x20, al

int09_finish:
  mov al, #0xAE      ;;enable keyboard
  out #0x64, al
  pop ax
  iret




;----------------------------------------
;- INT 13h Diskette Service Entry Point -
;----------------------------------------
.org 0xec59
int13_diskette:
  pushf
  push  es
  pusha
  call  _int13_diskette_function
  popa
  pop   es
  popf
   
  jmp iret_modify_cf

# 4472 "rombios.c"




;---------------------------------------------
;- INT 0Eh Diskette Hardware ISR Entry Point -
;---------------------------------------------
.org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
int0e_handler:
  push ax
  push ds
  mov  ax, #0x0000 ;; segment 0000
  mov  ds, ax
  mov  al, #0x20
  out  0x20, al  ;; send EOI to PIC
  mov  al, 0x043e
  or   al, #0x80 ;; diskette interrupt has occurred
  mov  0x043e, al
  pop  ds
  pop  ax
  iret


.org 0xefc7 ; Diskette Controller Parameter Table
diskette_param_table:
;;  Since no provisions are made for multiple drive types, most
;;  values in this table are ignored.  I set parameters for 1.44M
;;  floppy here
db  0xAF
db  0x02 ;; head load time 0000001, DMA used
db  0x25
db  0x02
db    18
db  0x1B
db  0xFF
db  0x6C
db  0xF6
db  0x0F
db  0x01 ;; most systems default to 8


;----------------------------------------
;- INT17h : Printer Service Entry Point -
;----------------------------------------
.org 0xefd2
int17_handler:
  iret ;; for now...

.org 0xf045 ; INT 10 Functions 0-Fh Entry Point
  HALT(4521)
  iret

;----------
;- INT10h -
;----------
.org 0xf065 ; INT 10h Video Support Service Entry Point
int10_handler:
  ;; dont do anything, since the VGA BIOS handles int10h requests
  iret

.org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)

;----------
;- INT12h -
;----------
.org 0xf841 ; INT 12h Memory Size Service Entry Point
; ??? different for Pentium (machine check)?
int12_handler:
  push ds
  mov  ax, #0x0040
  mov  ds, ax
  mov  ax, 0x0013
  pop  ds
  iret

;----------
;- INT11h -
;----------
.org 0xf84d ; INT 11h Equipment List Service Entry Point
int11_handler:
  push ds
  mov  ax, #0x0040
  mov  ds, ax
  mov  ax, 0x0010
  pop  ds
  iret

;----------
;- INT15h -
;----------
.org 0xf859 ; INT 15h System Services Entry Point
int15_handler:
  pushf
  push  ds
  push  es
  pusha
  call _int15_function
  popa
  pop   es
  pop   ds
  popf
   
  jmp iret_modify_cf

;; Protected mode IDT descriptor
;;
;; I just make the limit 0, so the machine will shutdown
;; if an exception occurs during protected mode memory
;; transfers.
;;
;; Set base to f0000 to correspond to beginning of BIOS,
;; in case I actually define an IDT later
;; Set limit to 0

pmode_IDT_info:
dw 0x0000  ;; limit 15:00
dw 0x0000  ;; base  15:00
db 0x0f    ;; base  23:16

;; Real mode IDT descriptor
;;
;; Set to typical real-mode values.
;; base  = 000000
;; limit =   03ff

rmode_IDT_info:
dw 0x03ff  ;; limit 15:00
dw 0x0000  ;; base  15:00
db 0x00    ;; base  23:16

.org 0xfa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)


;----------
;- INT1Ah -
;----------
.org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
int1a_handler:
  push ds
  pusha
  mov  ax, #0x0000
  mov  ds, ax
  call _int1a_function
  popa
  pop  ds
  iret

;;
;; int70h: IRQ8 - CMOS RTC
;;
int70_handler:
  push ds
  pusha
  mov  ax, #0x0000
  mov  ds, ax
  call _int70_function
  popa
  pop  ds
  iret

;---------
;- INT08 -
;---------
.org 0xfea5 ; INT 08h System Timer ISR Entry Point
int08_handler:
  sti
  push eax
  push ds
  mov ax, #0x0000
  mov ds, ax
  mov eax, 0x046c ;; get ticks dword
  inc eax

  ;; compare eax to one days worth of timer ticks at 18.2 hz
  cmp eax, #0x001800B0
  jb  int08_store_ticks
  ;; there has been a midnight rollover at this point
  xor eax, eax    ;; zero out counter
  inc BYTE 0x0470 ;; increment rollover flag

int08_store_ticks:
  mov 0x046c, eax ;; store new ticks dword
  ;; chain to user timer tick INT #0x1c
   
   
   
  int #0x1c
  cli
  mov al, #0x20
  out 0x20, al  ; send EOI to PIC
  pop ds
  pop eax
  iret

.org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST

;------------------------------------------------
;- IRET Instruction for Dummy Interrupt Handler -
;------------------------------------------------
.org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
dummy_iret_handler:
  iret

.org 0xff54 ; INT 05h Print Screen Service Entry Point
  HALT(4676)
  iret

; .org 0xff00
; .ascii "(c) 1994-2000 Kevin P. Lawton"

.org 0xfff0 ; Power-up Entry Point
   
  jmp post

.org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
.ascii "06/23/99"

.org 0xfffe ; System Model ID
db 0xFC 
db 0x00   ; filler

.org 0xd000
 

 
 
 
 


   
# 4867 "rombios.c"


mp_config_end:    
  db 0

# 4882 "rombios.c"


#endasm
