'******************************************************************************
'* 
'* SEGA MASTER SYSTEM MOTHERBOARD (MOTHERBOARD DEVICE)
'*
'* Supported platform/bus: SMS
'*
'* Version history:
'*  2009 - initial emulation (by 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="SMS Motherboard"
DebugName="MBOARD"

//variables
dim i as integer

'------------------------------ Video adapter ---------------------------------

public use object VID_SEGAMS as VDP : AddDevice(VDP)
for i=0x40 to 0x7E step 2 'interrupt counters ports
 pc.ReadPort(i)=VDP.VINDEX_PORT : pc.ReadPort(i+1)=VDP.HINDEX_PORT 
next
for i=0x80 to 0xBE step 2 'data and control ports
 pc.WritePort(i)=VDP.DATA_PORT : pc.ReadPort(i)=VDP.DATA_PORT
 pc.WritePort(i+1)=VDP.CTRL_PORT : pc.ReadPort(i+1)=VDP.STATE_PORT
next

'----------------------------------- Joypad -----------------------------------

//Current values
dim joy1keys as byte=0xFF

//Read joypad 1 port
function PORT_JOY1 as byte 
 result=joy1keys
end

//Handle key from emulator
public procedure HandleKey(vkey as byte, pressed as boolean)
 ?? DebugPrefix;">VKey=";Hex(vkey,2);"h (";iif(pressed,"DOWN","UP");")"
 select case vkey
  case VK_UP,VK_NUMPAD8 'UP button
   if pressed then : joy1keys=joy1keys and not 1 : else
      joy1keys=joy1keys or 1 : end if
  case VK_DOWN,VK_NUMPAD2 'DOWN button
   if pressed then : joy1keys=joy1keys and not 2 : else
      joy1keys=joy1keys or 2 : end if
  case VK_LEFT,VK_NUMPAD4 'LEFT button
   if pressed then : joy1keys=joy1keys and not 4 : else
      joy1keys=joy1keys or 4 : end if
  case VK_RIGHT,VK_NUMPAD6 'RIGHT button
   if pressed then : joy1keys=joy1keys and not 8 : else
      joy1keys=joy1keys or 8 : end if
  case VK_SPACE,VK_NUMPAD5,VK_LCTRL,VK_RCTRL,VK_A,VK_Z,VK_1 'Fire A
   if pressed then : joy1keys=joy1keys and not 0x10 : else
      joy1keys=joy1keys or 0x10 : end if
  case VK_RETURN,VK_EXTRETURN,VK_LALT,VK_B,VK_X,VK_2,VK_LSHIFT,VK_RSHIFT 'Fire B
   if pressed then : joy1keys=joy1keys and not 0x20 : else
      joy1keys=joy1keys or 0x20 : end if
 end select
//dbg.break
end

pc.HandleKey=HandleKey 
pc.ReadPort(0xDC)=PORT_JOY1 : pc.ReadPort(0xC0)=PORT_JOY1 //port and mirror

'------------------------------ 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

'------------------ Handling of bank registers at $FFF0..$FFFF ----------------

//Values to reuse in writer
dim reg_val(0xFFF0 to 0xFFFF) as byte

//Memory writer
function MemRegWriter(Addr as dword, Value as byte) as boolean //ROM areas
 dim i as integer, j as integer
 mem.Byte(Addr-0x2000)=Value 'mirroring to main RAM
 select case Addr 
 case 0xFFFD //Map page 0 (1..15Kb) to 16Kb block of cartridge ROM
    i=((mem.size/1024)-64)/16 : if Value>=i then Value=i-1 'no more then ROM 
    j=Value*16+64
    for i=1 to 15 : mem.KBIndex(i)=j+i : next i
    ?? DebugPrefix;">MapReg0=[$";Hex(Addr,4);"]:=$";Hex(Value,2)
 case 0xFFFE //Map page 1 (16..31Kb) to 16Kb block of cartridge ROM
    i=((mem.size/1024)-64)/16 : if Value>=i then Value=i-1 'no more then ROM 
    j=Value*16+64
    for i=0 to 15 : mem.KBIndex(16+i)=j+i : next i
    ?? DebugPrefix;">MapReg1=[$";Hex(Addr,4);"]:=$";Hex(Value,2)
 case 0xFFFC,0xFFFF //Map page 2 (32..47Kb) to cartridge ROM or b/backed RAM
    reg_val(Addr)=Value :  j=reg_val(0xFFFC) 
    if (j and 8)=0 then //cartridge ROM mapping
      Value=reg_val(0xFFFF) 
      i=((mem.size/1024)-64)/16 : if Value>=i then Value=i-1 'no more then ROM 
      j=Value*16+64
      for i=0 to 15 : mem.KBIndex(32+i)=j+i : next i
    else //battery backed RAM
      if (j and 4)=0 then j=0 else j=16 'RAM page 0 or 1
      for i=0 to 15 : mem.KBIndex(32+i)=j+i : next i
    end if
    ?? DebugPrefix;">Reg=[$";Hex(Addr,4);"]:=$";Hex(Value,2)
 case 0xFFF0 to 0xFFFF //other memory registers
    ?? DebugPrefix;">Reg=[$";Hex(Addr,4);"]=$";Hex(Value,2)
 end select
 result=true 
end

//Memory reader
function MemRegReader(Addr as dword, byref Value as byte) as boolean
 value=mem.Byte(Addr-0x2000) 'read form mirrored RAM
 select case Addr 
 case 0xFFF0 to 0xFFFF //memory registers
    //?? DebugPrefix;"<Reg=[$";Hex(Addr,4);"]=$";Hex(Value,2)
 end select
 result=true 
end

//Initial register settings and mappings
mem.Byte(0xDFFF)=0 : mem.Byte(0xDFFC)=8 'page 2 to memory backed RAM page 0
reg_val(0xFFFF)=0 : reg_val(0xFFFC)=8 'same values to reuse in writer
   for i=0 to 15 : mem.KBIndex(32+i)=i : next i
mem.Byte(0xDFFD)=0 'page 0 to cartridge ROM page 0
   for i=0 to 15 : mem.KBIndex(i)=64+i : next i 
if mem.Size>=(64+32)*1024 then 'cartridge ROM >=32Kb
   mem.Byte(0xDFFE)=1 'page 1 to cartridge ROM page 1
   for i=16 to 31 : mem.KBIndex(i)=64+i : next i 
else 'cartridge ROM <32Kb
   mem.Byte(0xDFFE)=0 'page 1 to cartridge ROM page 0
   for i=0 to 15 : mem.KBIndex(16+i)=64+i : next i 
end if

'---------------------------- 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)

 'mark all address space as RAM
 for i=0 to 63 : mem.KBAccess(i)=memReadWrite : next
 'mark all cartridge data as ROM
 for i=64 to (mem.Size-1)/1024 : mem.KBAccess(i)=memReadOnly : next
 'mirror 8Kb RAM at 0xE000 to 0xC000
 for i=56 to 62 : mem.KBIndex(i)=i-8 : next
 'capture last KB to emulate memory switching registers
 mem.KBWriter(63)=MemRegWriter : mem.KBReader(63)=MemRegReader
 'map first KB to first KB of cartrige ROM
 mem.KBIndex(0)=mem.KBIndex(64)

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

 'success 
 result=true
end


