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

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

//parent DEVSET implementation
public use object DEVSET

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

//variables
dim i as integer

'-------------------------- Video adapter (TMS9918) ---------------------------

//TMS9918 video adapter
public use object VID_TMS9918 as VDP : AddDevice(VDP)
pc.WritePort(0x98)=VDP.MEM_PORT : pc.ReadPort(0x98)=VDP.MEM_PORT
pc.WritePort(0x99)=VDP.REG_PORT : pc.ReadPort(0x99)=VDP.STATE_PORT

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

//PIT (programmable interval timer)
public use object PIT_I8253 as PIT : AddDevice(PIT)
AddDevice(PIT.Channel0) : PIT.Channel0.Freq=2000000 //2Mhz
AddDevice(PIT.Channel1) : PIT.Channel1.Freq=2000000 //2Mhz
AddDevice(PIT.Channel2) : PIT.Channel2.Freq=2000000 //2Mhz
pc.WritePort(0x84)=PIT.CH0_DATA_PORT : pc.ReadPort(0x84)=PIT.CH0_DATA_PORT
pc.WritePort(0x85)=PIT.CH1_DATA_PORT : pc.ReadPort(0x85)=PIT.CH1_DATA_PORT
pc.WritePort(0x86)=PIT.CH2_DATA_PORT : pc.ReadPort(0x86)=PIT.CH2_DATA_PORT
pc.WritePort(0x87)=PIT.MODE_PORT     : pc.ReadPort(0x87)=PIT.MODE_PORT

'---------------------------- PPI (Keyboard etc.) -----------------------------

//Keyboard
public use object KBC_MSX as KBC : AddDevice(KBC)
pc.HandleKey=KBC.HandleKey

//Variables
dim ppiA as byte=0, ppiB as byte=0, ppiC as byte=0
dim KeyLine as byte=0 'selected line of keys to read via PPI Port B


//Write PPI Port C
property PPI_C(Value as byte) 
 KeyLine=Value and 0xF 'keyboard line
 ?? DebugPrefix;">PPI_C=";Hex(Value,2);"h (Line=";KeyLine;")"
 ppiC=Value 'store to read
end

//Read PPI Port C 
function PPI_C as byte 
 result=ppiC
 ?? DebugPrefix;"<PPI_C=";Hex(result,2)
end

//Read PPI Port B (keys)
function PPI_B as byte 
 result=KBC.GetKeyLine(KeyLine)
 ?? DebugPrefix;"<PPI_B=";Hex(result,2)
end

//Attach r/w handlers to PPI ports
pc.WritePort(0xAA)=PPI_C : pc.ReadPort(0xAA)=PPI_C 
pc.ReadPort(0xA9)=PPI_B

'----------------------------- Memory Mapping ---------------------------------

//PSLOT value
dim pslot as byte=0

//Remapping of BIOS (slot 0)
function RemapBIOSSlot(KBIndex as integer) as boolean
 result=false
end

//Remapping of cartridge 1 (slot 1)
function RemapCart1Slot(KBIndex as integer) as boolean
 result=false 'default remapping 
 if cart1size=0 then exit 'no cartridge
 if cart1size>48 then 'unknown mapper
    cart1.Detected=false : exit : end if
 dim i as integer=KBIndex
 if i<16 then i=i+16 'first 0-15 KB - mirror to 16-31Kb
 i=i-16 'cartridge start at 0x4000=16Kb
 if i>=cart1size then exit 'more then cartridge ROM size
 mem.KBIndex(KBIndex)=cart1offs+i 'remapping to cartridge data
 if (KBIndex and 0xF)=0 then cart1.Accessed=true 'blink light
 result=true
end

//Remapping of cartridge 2 (slot 2)
function RemapCart2Slot(KBIndex as integer) as boolean
 result=false 'default remapping 
 if cart2size=0 then exit 'no cartridge
 if cart2size>48 then 'unknown mapper
    cart2.Detected=false : exit : end if
 dim i as integer=KBIndex
 if i<16 then i=i+16 'first 0-15 KB - mirror to 16-31Kb
 i=i-16 'cartridge start at 0x4000=16Kb
 if i>=cart2size then exit 'more then cartridge ROM size
 mem.KBIndex(KBIndex)=cart2offs+i 'remapping to cartridge data
 if (KBIndex and 0xF)=0 then cart2.Accessed=true 'blink light
 result=true
end

//Remapping of RAM (slot 3)
function RemapRAMSlot(KBIndex as integer) as boolean
 result=false
end

//Remapping of slots
procedure RemapSlot(SlotIndex as integer, KBIndex as integer)
 select case SlotIndex
 case 0 : if not RemapBIOSSlot(KBIndex) then 
          mem.KBIndex(KBIndex)=1*64+KBIndex : end 'default BIOS area  
 case 1 : if not RemapCart1Slot(KBIndex) then 
          mem.KBIndex(KBIndex)=2*64+KBIndex : end 'default empty ROM area  
 case 2 : if not RemapCart2Slot(KBIndex) then 
          mem.KBIndex(KBIndex)=3*64+KBIndex : end 'default empty ROM area  
 case 3 : if not RemapRAMSlot(KBIndex) then 
          mem.KBIndex(KBIndex)=4*64+KBIndex : end 'default RAM area  
 end select
end

//Write primary Slot Register (PSLOT)
property PORT_PSLOT(Value as byte) 
 ?? DebugPrefix;">PSLOT=";Hex(Value,2);"h (";
 ?? Value and 3;",";shr(Value,2) and 3;",";
 ?? shr(Value,4) and 3;",";shr(Value,6) and 3;")"
 if pslot=value then exit //no need to remap
 dim i as integer, j as integer
 //remapping of 0..15 Kb area
 if (Value and 3)<>(pslot and 3) then : j=Value and 3
     for i=0x0000/1024 to 0x3FFF/1024 : RemapSlot(j,i) : next : end if
 //remapping of 16..31 Kb area
 if (Value and 0xC)<>(pslot and 0xC) then : j=shr(Value,2) and 3
     for i=0x4000/1024 to 0x7FFF/1024 : RemapSlot(j,i) : next : end if
 //remapping of 32..47 Kb area
 if (Value and 0x30)<>(pslot and 0x30) then : j=shr(Value,4) and 3
     for i=0x8000/1024 to 0xBFFF/1024 : RemapSlot(j,i) : next : end if
 //remapping of 48..63 Kb area
 if (Value and 0xC0)<>(pslot and 0xC0) then : j=shr(Value,6) and 3
     for i=0xC000/1024 to 0xFFFF/1024 : RemapSlot(j,i) : next : end if
 pslot=Value
 //TOCHECK - what N/A memory areas return when reading - 00 or FF??
end

//Read primary Slot Register (PSLOT)
function PORT_PSLOT as byte 
 result=pslot
 ?? DebugPrefix;"<PSLOT=";Hex(pslot,2)
end

//Attach r/w handlers to port A8h
pc.WritePort(0xA8)=PORT_PSLOT : pc.ReadPort(0xA8)=PORT_PSLOT

'------------------------------ IRQ Emulation ---------------------------------

//Is IRQ active
dim IRQFlag as boolean=false

//Set IRQ
public function SetIRQ(Index as integer, State as boolean) as boolean
 //?? DebugPrefix;"SetIRQ(";Hex(Index,2);"h, ";State;")"
 IRQFlag=true
end

//Get IRQ
public function GetIRQ(Process as boolean,byref Address as dword) as integer
 if IRQFlag then
    result=0 : Address=0xFF : if Process then IRQFlag=false
 else : result=-1 : end if
 //?? DebugPrefix;"GetIRQ(";Process;")=";result
end

//Configure IRQs
pc.SetIRQ=SetIRQ : pc.GetIRQ=GetIRQ

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

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

 'load ROM BIOS file to memory slot 1
 s="bin\msx.rom" : if not FileExists(s) then : s="bin\cbios_main_msx1.rom"
 if not FileExists(s) then s="bin\msx.rom" : end if
 mem.Bytes(64*1024)=ArrayFile(s)  'main BIOS

 'configure memory areas
 for i=0 to (mem.Size-1)/1024 : mem.KBAccess(i)=memReadWrite : next 'all space
 for i=64 to 4*64-1 : mem.KBAccess(i)=memReadOnly : next 'ROM at slots 0..2
 for i=4*64 to 5*64-1 : mem.KBAccess(i)=memReadWrite : next 'RAM at slot 3
 if cart1size>=0 then 'cartridge 1 ROM
    for i=cart1offs to cart1offs+cart1size-1 : mem.KBAccess(i)=memReadOnly
 next : end if
 if cart2size>=0 then 'cartridge 2 ROM
    for i=cart2offs to cart2offs+cart2size-1 : mem.KBAccess(i)=memReadOnly
 next : end if

 'set initial memory mapping (0,0,0,0)
 pslot=0xFF : PORT_PSLOT=0

 'mask port indexes to use only low byte
 pc.PortIndexMask=0xFF

 'success 
 result=true
end
