'******************************************************************************
'* 
'* SPECTRUM-128K MOTHERBOARD v.1.0 (MOTHERBOARD DEVICE)
'*
'* Supported platform/bus: Spectrum
'*
'* Version history:
'*  - v.0.1 by WadiM (initial emulation)
'*
'****************************************************************************** 

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

//parent DEVSET implementation
public use object DEVSET

//motherboard name
DeviceName="Spectrum-128K Motherboard"
DebugName="SPEC128_MB"

//variables
dim i as integer

'--------------------------------- Chipset ------------------------------------

//Video
public use object VID_SPECTRUM as ULA : AddDevice(ULA)
for i=0x4000/1024 to 0x5AFF/1024 //video memory
 mem.KBReader(i)=ULA.ReadMemory : mem.KBWriter(i)=ULA.WriteMemory 
next

//Keyboard
public use object KBC_SPECTRUM as KBC : AddDevice(KBC)
pc.HandleKey=KBC.HandleKey
for i=0 to 0xFFFE step 2 //one handler for all even ports
 pc.ReadPort(i)=KBC.PORTS 
next

//Memory mapping
dim P7FFD as byte=0 'memory configuration port
property PORT_7FFD(Value as byte) 
 ?? DebugPrefix;">PORT_7FFD=";Hex(Value,2);"h (";
 ?? "page=";Value and 7;",screen=";shr(Value,3) and 1;
 ?? ",rom=";shr(Value,4) and 1;",nopaging=";(Value and 0x20)<>0;
 if (P7FFD and 0x20)<>0 then : ?? ") IGNORED"
 else : ?? ") ACCEPTED"
   dim i as integer, j as integer
   //Virtual memory map (kilobytes) 
   '000-015 = ROM_0
   '016-031 = BANK_5 (SCREEN_0)
   '032-047 = BANK_2
   '048-063 = BANK_0
   '064-079 = BANK_1
   '080-095 = BANK_3
   '096-111 = BANK_4
   '112-127 = BANK_6
   '128-143 = BANK_7 (SCREEN_1)
   '144-159 = ROM_1                   
   //Select bank at 0xC000 (48..63Kb)
   if (Value and 7)<>(P7FFD and 7) then
      //get KB index of selected bank 0-7
      select case (Value and 7) 
      case 0: j=48 : case 1: j=64 : case 2: j=32 : case 3: j=80
      case 4: j=96 : case 5: j=16 : case 6: j=112 : case 7: j=128
      end select
      //remapping
      for i=0 to 15 : mem.KBIndex(48+i)=j+i : next
   end if
   //Select ROM at 0x0000 (0..15Kb)
   if (Value and 0x10)<>(P7FFD and 0x10) then
      if (Value and 0x10)=0 then 'ROM_0
         for i=0 to 15 : mem.KBIndex(i)=i : next
      else 'ROM_1
         for i=0 to 15 : mem.KBIndex(i)=144+i : next
   end if : end if
   P7FFD=Value
 end if 
end
function PORT_7FFD as byte 'to use by ULA emulation (starting KB index of vmem)
  if (P7FFD and 8)=0 then result=16 else result=128
end 
for i=0 to 0xFFFF : if (i and 0x8002)=0 then '"incomplete" port index decoding
 pc.WritePort(i)=PORT_7FFD : pc.ReadPort(i)=PORT_7FFD
end if : next

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

//IRQs counter 
dim IRQPending as boolean=false

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

//Get IRQ
public function GetIRQ(Process as boolean,byref Address as dword) as integer
 if IRQPending then
    result=0 : Address=0xFF : if Process then IRQPending=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
  
 'parent call
 if not DEV_INIT(stream,EventFreq) then exit(false)

 'load ROM BIOS file 
 mem.Bytes(0)=ArrayFile("bin\zx128_0.rom")        'ROM_0
 mem.Bytes(144*1024)=ArrayFile("bin\zx128_1.rom") 'ROM_1

 'mark first 16K of memory area as read-only (ROM)
 for i=0 to 15 : mem.KBAccess(i)=memReadOnly : next
 'mark next 48K of memory area as read-write (RAM)
 for i=16 to 47 : mem.KBAccess(i)=memReadWrite : next

 'success 
 result=true
end
