'******************************************************************************
'* 
'* POISK-1 ISA MOTHERBOARD (MOTHERBOARD DEVICE)
'*
'* Supported platform/bus: X86
'*
'* Version history:
'*  2007,2009 by WadiM (initial emulation)
'*
'****************************************************************************** 

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

//parent ISA bus implementation
public use object BUS_ISA

//motherboard name
DeviceName="Poisk-1 Motherboard"
DebugName="MBOARD"

//variables
dim i as integer, j as integer, b as byte

'---------------------------- Video adapter (CGA) -----------------------------

//CGA video adapter (ES modification)
public use object VID_CGA as CGA : AddISADevice(CGA,-1,-1)
CGA.DevParams.MoveToName("MemorySizeKB") : CGA.DevParams.Value=32 '32Kb
pc.WritePort(0x3D4)=CGA.CRT_INDEX_PORT : pc.ReadPort(0x3D4)=CGA.CRT_INDEX_PORT
pc.WritePort(0x3D5)=CGA.CRT_DATA_PORT  : pc.ReadPort(0x3D5)=CGA.CRT_DATA_PORT
pc.WritePort(0x3D8)=CGA.MODE_PORT      : pc.ReadPort(0x3D8)=CGA.MODE_PORT
pc.WritePort(0x3D9)=CGA.COLOR_PORT     : pc.ReadPort(0x3D9)=CGA.COLOR_PORT
pc.WritePort(0x3DB)=CGA.PEN_RESET_PORT : pc.WritePort(0x3DC)=CGA.PEN_SET_PORT
pc.ReadPort(0x3DA)=CGA.STATE_PORT
for i=0xB8000/1024 to (0xC0000/1024)-1 //CGA video memory
 mem.KBReader(i)=CGA.ReadMemory : mem.KBWriter(i)=CGA.WriteMemory 
next

//Port values
dim p6Ah as byte=0x00, p68h as byte=0x80

//CGA emulation
property PORT_68(Value as byte) 
 p68h=Value 
 //CGA mode value
 b=8 : if (Value and 0x80)<>0 then b=b or 1 //resolution
 if (Value and 0x40)=0 then b=b or 2 //graph mode
 if (p6Ah and 0x40)<>0 then b=b or 4 //disable color
 if (Value and 0x80)<>0 then b=b or 0x10 //resolution //??? - such was in SovietPC
 CGA.MODE_PORT=b
 //CGA color value
 b=0 : if (Value and 1)<>0 then b=b or 4 //Red
 if (Value and 2)<>0 then b=b or 2 //Green
 if (Value and 4)<>0 then b=b or 1 //Blue
 if (Value and 0x10)<>0 then b=b or 0x20 //palette index
 if (Value and 0x20)<>0 then b=b or 0x10 //intensity
 CGA.COLOR_PORT=b
end

function PORT_68 as byte : result=p68h : end
property PORT_6A(Value as byte) : p6Ah=Value : PORT_68=p68h : end
function PORT_6A as byte : result=p6Ah : end

pc.WritePort(0x68)=PORT_68 : pc.ReadPort(0x68)=PORT_68
pc.WritePort(0x6A)=PORT_6A : pc.ReadPort(0x6A)=PORT_6A

'----------------------- Interrupt controller (PIC) ---------------------------

//PIC (programmable interrupt controller)
public use object PIC_I8259 as PIC : AddDevice(PIC)
pc.WritePort(0x20)=PIC.PORT0 : pc.ReadPort(0x20)=PIC.PORT0
pc.WritePort(0x21)=PIC.PORT1 : pc.ReadPort(0x21)=PIC.PORT1
pc.SetIRQ=PIC.SetIRQ : pc.GetIRQ=PIC.GetIRQ

'--------------------------- Interval timer (PIT) -----------------------------

//PIT (programmable interval timer)
public use object PIT_I8253 as PIT : AddDevice(PIT)
AddISADevice(PIT.Channel0, 0,-1) //system timer (IRQ0, no DMA)
   PIT.Channel0.Freq=1250000     //1.25Mhz
AddISADevice(PIT.Channel1, 6,-1) //keyboard processing (IRQ6, no DMA)
   PIT.Channel1.Freq=1250000     //1.25Mhz
AddISADevice(PIT.Channel2,-1,-1) //speaker (no IRQ, no DMA)
   PIT.Channel2.Freq=1250000     //1.25Mhz
   PIT.Channel2.Input=false : PIT.Channel2.Output=false
pc.WritePort(0x40)=PIT.CH0_DATA_PORT : pc.ReadPort(0x40)=PIT.CH0_DATA_PORT
pc.WritePort(0x41)=PIT.CH1_DATA_PORT : pc.ReadPort(0x41)=PIT.CH1_DATA_PORT
pc.WritePort(0x42)=PIT.CH2_DATA_PORT : pc.ReadPort(0x42)=PIT.CH2_DATA_PORT
pc.WritePort(0x43)=PIT.MODE_PORT     : pc.ReadPort(0x43)=PIT.MODE_PORT

'------------------- Keyboard controller and switches (KBC) -------------------

//KBC controller (keyboard controller)
public use object KBC_I8048 as KBC : AddISADevice(KBC,-1,-1)
pc.HandleKey=KBC.HandleKey 

//Port values
dim p61h as byte=0

function PORT_60H as byte
 if (p61h and 0x80)=0 then 'keyboard data port
     result=KBC.DATA_PORT //TOCHECK
 else 'configuration switches
     result=0x6D or 2 //CGA,2FDD,FPU
end : end

property PORT_61H(value as byte)
 p61h=value 
 if (value and 0x80)<>0 then KBC.KeyProcessed
 PIT.Channel2.Input=(value and 1)<>0
 PIT.Channel2.Output=(value and 2)<>0
end

function PORT_61H as byte 'sound
 result=p61h and (not 0x1) 
 if PIT.Channel2.Input then result=result or 1 else result=0 
 if PIT.Channel2.Output then result=result or 2 
end

function PORT_62H as byte
 if (p61h and 0x4)<>0 then result=2 else result=1 //LSB and MSB or mem.size
end

pc.ReadPort(0x60)=PORT_60H
pc.WritePort(0x61)=PORT_61H : pc.ReadPort(0x61)=PORT_61H
pc.ReadPort(0x62)=PORT_62H

'------------------------------ Other stuff -----------------------------------

//Fix bug in BIOS (lacked POP AX)
public function INT_10H as boolean 
 if (cpu.AX=8) and (mem.Word(0xFF0CE)=0x0D74) then
    mem.KBAccess(0xFF0CE/1024)=memReadWrite 
    mem.Word(0xFF0CE)=0x0D74
    mem.KBAccess(0xFF0CE/1024)=memReadOnly
 end if
 result=(cpu.AX=8)
end

//Report 640Kb of RAM
public function INT_12H as boolean 
 cpu.AX=0xA0000/1024
 result=true
end

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

//Device initialization
public function DEV_INIT(stream as object,byref EventFreq as integer) as boolean
 
 'parent call
 if not DEV_INIT(stream,EventFreq) then exit(false)

 'load ROM BIOS file
 dim s as string="bin\BIOSP1S.RF4" 
 mem.Bytes(0x100000-FileSize(s))=ArrayFile(s) 'main BIOS
 s="bin\B_NGMD_N.RF2" : if FileExists(s) then
  mem.Bytes(0xC0000-FileSize(s))=ArrayFile(s) 'floppy BIOS
 end if

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

 'patches
 pc.CallInt(0x10)=INT_10H
 pc.CallInt(0x12)=INT_12H

 'success 
 result=true
end


