'******************************************************************************
'* 
'* Modified SPC/ISA Motherboard
'*
'* Supported platform/bus: X86
'*
'* Adapters: VGA
'* 
'* Version history:
'*  2007,2008 - initial emulation (by WadiM)
'*
'****************************************************************************** 

//DEBUG.ON 'uncomment to enable debug messages (can be slow for hi-freq events)

//parent PC/AT chipset implementation
public use object CHIPSET_PCAT

//motherboard name
DeviceName="SPC/ISA Motherboard"
DebugName="SPCAT_ISAMB"

//variables
dim i as integer

'--------------------------------- Adapters -----------------------------------

//VGA video adapter
public use object VID_VGA as VGA : AddDevice(VGA)
for i=0x3C0 to 0x3CF : pc.WritePort(i)=VGA.PORTS : pc.ReadPort(i)=VGA.PORTS : next
for i=0x3D0 to 0x3DF : pc.WritePort(i)=VGA.PORTS : pc.ReadPort(i)=VGA.PORTS : next
pc.WritePort(0x3B4)=VGA.PORTS : pc.ReadPort(0x3B4)=VGA.PORTS
pc.WritePort(0x3B5)=VGA.PORTS : pc.ReadPort(0x3B5)=VGA.PORTS
pc.WritePort(0x3BA)=VGA.PORTS : pc.ReadPort(0x3BA)=VGA.PORTS
for i=0xA0000/1024 to (0xBFFFF/1024)-1 //VGA video memory
 mem.KBReader(i)=VGA.ReadMemory : mem.KBWriter(i)=VGA.WriteMemory 
next

//Extended CMOS checksum calculation (cells [34h..3Dh]) and writing to [3E..3F])
protected procedure UpdateCMOSChecksumExt
 dim w as word=0 : for i=0x34 to 0x3D : w=w+CMOS.Cell(i) : next 
 CMOS.Cell(0x3E)=shr(w,8) : CMOS.Cell(0x3F)=w
end

//Main BIOS information and debug messages
property PORT_CHARS_OUT(value as byte) : ?? Chr(value); : end
pc.WritePort(0x402)=PORT_CHARS_OUT
pc.WritePort(0x403)=PORT_CHARS_OUT

//VGA BIOS debug messages
pc.WritePort(0x500)=PORT_CHARS_OUT
pc.WritePort(0x501)=PORT_CHARS_OUT
pc.WritePort(0x502)=PORT_CHARS_OUT
pc.WritePort(0x503)=PORT_CHARS_OUT

//Main BIOS panic message (16-bit port)
property PORT_LINE_OUT16(value as word) 
 ? "BIOS panic at rombios.c, line ";value
end
pc.WritePort16(0x400)=PORT_LINE_OUT16

//Main BIOS panic message (8-bit port)
dim oldp8 as byte=0
property PORT_LINE_OUT8(value as byte) : oldp8=value : end
property PORT_LINE_OUT8_2(value as byte)
 ?? "BIOS panic at rombios.c, line ";shl(value,16)+oldp8
 oldp8=0
end
pc.WritePort(0x400)=PORT_LINE_OUT8
pc.WritePort(0x401)=PORT_LINE_OUT8_2

//Disable PS/2 mouse and APM support
function INT_15h as boolean
 if (cpu.AH=0x53) or (cpu.AH=0xC2) then
    cpu.AH=0x86 : cpu.FLAGS.CF=true 'not supported
    result=true 'no need real int15 processing
 else
    result=false 'need real int15 processing
 end if
end
pc.CallInt(0x15)=INT_15h

//PS2 System Control Port A (used as another method to control A20 line)
property WritePort92(value as byte)
 if (value and 2)<>0 then : cpu.AddressMask=cpu.AddressMask or 0x100000
    ?? DebugPrefix;"A20=on" 
 else : cpu.AddressMask=cpu.AddressMask and (not 0x100000) 
   ?? DebugPrefix;"A20=off" 
 end if
end

function ReadPort92 as byte 
 if (cpu.AddressMask and 0x100000)=0 then result=0 else result=2 'A20 Line
end

pc.ReadPort(0x92)=ReadPort92 : pc.WritePort(0x92)=WritePort92

'---------------------------- DEVICE Interface --------------------------------

//Device initialization
protected function DEV_INIT(stream as object,byref EventFreq as integer) as boolean
 dim i as integer, j as integer, s as string
 
 'parent call
 if not DEV_INIT(stream,EventFreq) then exit(false)

 //Main CMOS settings

 //Boot sequence
 if not emuDrives.FDD(0).Detected then DetectFloppyDisk(emuDrives.FDD(0)) 'first FDD
 if not emuDrives.HDD(0).Detected then DetectHardDisk(emuDrives.HDD(0)) 'first HDD
 if emuDrives.FDD(0).Usable then 
    CMOS.Cell(0x3D)=0x0101 //boot from floppy
    CMOS.Cell(0x2D)=CMOS.Cell(0x2D) and (not 0x20) //fdd
 elseif emuDrives.HDD(0).Usable then 
    CMOS.Cell(0x3D)=0x0202 //boot from hard drive
    CMOS.Cell(0x2D)=CMOS.Cell(0x2D) or 0x20 //hdd
 else : CMOS.Cell(0x3D)=0 : end if //no boot device

 //Writing HDD Params directly to CMOS (as User Type 47)
 dim Disk as object=emuDrives.HDD(0) //first HDD
 CMOS.Cell(0x20)=0 'control byte
 if not(Disk.Detected) then DetectHardDisk(Disk)
 if not(Disk.Detected) then 'not available
    CMOS.Cell(0x12)=CMOS.Cell(0x12) and (not 0xF0)
 else 'available
   CMOS.Cell(0x12)=CMOS.Cell(0x12) or 0xF0
   CMOS.Cell(0x1B)=disk.CylinderCount
   CMOS.Cell(0x1C)=shr(disk.CylinderCount,8)
   CMOS.Cell(0x1D)=disk.HeadCount
   if disk.HeadCount>=8 then : CMOS.Cell(0x20)=CMOS.Cell(0x20) or 8 
      else : CMOS.Cell(0x20)=CMOS.Cell(0x20) and not(8) : end
   CMOS.Cell(0x23)=disk.SectorCount
 end
 Disk=emuDrives.HDD(1) //second HDD
 CMOS.Cell(0x29)=0 'control byte
 if not(Disk.Detected) then DetectHardDisk(Disk)
 if not(Disk.Detected) then 'not available
    CMOS.Cell(0x12)=CMOS.Cell(0x12) and (not 0xF)
 else 'available
   CMOS.Cell(0x12)=CMOS.Cell(0x12) or 0xF
   CMOS.Cell(0x24)=disk.CylinderCount
   CMOS.Cell(0x25)=shr(disk.CylinderCount,8)
   CMOS.Cell(0x26)=disk.HeadCount
   if disk.HeadCount>=8 then : CMOS.Cell(0x29)=CMOS.Cell(0x29) or 8 
      else : CMOS.Cell(0x29)=CMOS.Cell(0x29) and not(8) : end
   CMOS.Cell(0x2C)=disk.SectorCount
 end
 CMOS.Cell(0x19)=47 : CMOS.Cell(0x1A)=47 

 //Calculation of checksum
 UpdateCMOSChecksum
 
 'Main BIOS
 s="bin\BIOS-bochs-legacy" 
 if not FileExists(s) then : s="bin\BIOS-bochs-latest"
     if not FileExists(s) then : s="bin\BIOS-bochs-legacy" 
 end if : end if
 mem.Bytes(0x100000-FileSize(s))=ArrayFile(s) 'main BIOS

 'Video BIOS
 s="bin\VGABIOS-lgpl-latest" 
 if not FileExists(s) then : s="bin\VGABIOS-lgpl-latest-debug"
   if not FileExists(s) then : s="bin\VGABIOS-lgpl-latest-cirrus"
     if not FileExists(s) then : s="bin\VGABIOS-lgpl-latest-cirrus-debug"
       if not FileExists(s) then : s="bin\VGABIOS-lgpl-latest" 
 end if : end if : end if : end if
 mem.Bytes(0xC0000)=ArrayFile(s) 'VGA BIOS

 'mark non-conventional memory area as read-only 
 for i=0xA0000/1024 to 0xFFFFF/1024 : mem.KBAccess(i)=memReadOnly : next
 'mark VGA memory as read-write 
 for i=0xA0000/1024 to 0xBFFFF/1024 : mem.KBAccess(i)=memReadWrite : next

 'success 
 result=true
end
