'******************************************************************************
'* 
'* NES MOTHERBOARD (MOTHERBOARD DEVICE)
'*
'* Supported platform/bus: NES
'*
'* Version history:
'*  2009 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

//mapping support
use module "mapping"

//motherboard name
DeviceName="NES Motherboard"
DebugName="MBOARD"

//variables
dim i as integer

'----------------------------- Video adapter (PPU) ----------------------------

//Attach memory-mapped register handlers to $2000..$3FFF addresses
public use object "video" as ppu : AddDevice(ppu)
for i=8 to 15 
 mem.KBReader(i)=ppu.ReadMemory
 mem.KBWriter(i)=ppu.WriteMemory
next i

'---------------------------------- Joystick ----------------------------------

//Current values
dim joy1keys as byte=0, joy1keysbuf as byte=0
dim joy1num as byte=0, joy1mask as byte=1

//Write joysticks 1 port
property PORT_JOY1(Value as byte) 
 joy1num=0 //reset???
 joy1mask=1
end

//Read joysticks 1 port
function PORT_JOY1 as byte 
 result=0x40 or (shr(joy1keys,joy1num) and 1) 
 if not dbg.ReadingMemory then
  joy1keys=(joy1keys and (not joy1mask)) or (joy1keysbuf and joy1mask)
  joy1num=(joy1num+1) and 7
  joy1mask=shl(1,joy1num)
 end if
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_LCTRL,VK_A,VK_1 'A button
   if pressed then : joy1keys=joy1keys or 1 : joy1keysbuf=joy1keysbuf or 1
      else : joy1keysbuf=joy1keysbuf and (not 1) : end if
  case VK_LALT,VK_B,VK_S,VK_2 'B button
   if pressed then : joy1keys=joy1keys or 2 : joy1keysbuf=joy1keysbuf or 2
      else : joy1keysbuf=joy1keysbuf and (not 2) : end if
  case VK_SPACE,VK_NUMPAD5,VK_Z,VK_3 'SELECT button
   if pressed then : joy1keys=joy1keys or 4 : joy1keysbuf=joy1keysbuf or 4
      else : joy1keysbuf=joy1keysbuf and (not 4) : end if
  case VK_RETURN,VK_EXTRETURN,VK_X,VK_4 'START button
   if pressed then : joy1keys=joy1keys or 8 : joy1keysbuf=joy1keysbuf or 8
      else : joy1keysbuf=joy1keysbuf and (not 8) : end if
  case VK_UP,VK_NUMPAD8 'UP button
   if pressed then : joy1keys=joy1keys or 16 : joy1keysbuf=joy1keysbuf or 16
      else : joy1keysbuf=joy1keysbuf and (not 16) : end if
  case VK_DOWN,VK_NUMPAD2 'DOWN button
   if pressed then : joy1keys=joy1keys or 32 : joy1keysbuf=joy1keysbuf or 32
      else : joy1keysbuf=joy1keysbuf and (not 32) : end if
  case VK_LEFT,VK_NUMPAD4 'LEFT button
   if pressed then : joy1keys=joy1keys or 64 : joy1keysbuf=joy1keysbuf or 64
      else : joy1keysbuf=joy1keysbuf and (not 64) : end if
  case VK_RIGHT,VK_NUMPAD6 'RIGHT button
   if pressed then : joy1keys=joy1keys or 128 : joy1keysbuf=joy1keysbuf or 128
      else : joy1keysbuf=joy1keysbuf and (not 128) : end if
 end select
//dbg.break
end

pc.HandleKey=HandleKey

'------------------------- Handling of I/O registers at $4000..$4FFF ----------

//Memory writer
function MemIOWriter(Addr as dword, Value as byte) as boolean //ROM areas
 select case Addr 
 case 0x4014 //DMA transfer of sprite table (256 bytes) from memory to PPU
    mem.pos=(nes_VRAMStart+nes_VRAMSize-1)*1024 'destination (Sprite Table)
    mem.Copy(mem.Object,256,shl(Value,8))
    ?? DebugPrefix;"DMATransfer=";Hex(Value,2)
 case 0x4016 //Joystick N1
    PORT_JOY1=Value
    ?? DebugPrefix;">Joy1Reg=[$";Hex(Addr,4);"]:=$";Hex(Value,2)
 case else
    ?? DebugPrefix;">[$";Hex(Addr,4);"]:=$";Hex(Value,2)
 end select
 result=true 
// dbg.break
end

//Memory reader
function MemIOReader(Addr as dword, byref Value as byte) as boolean
 select case Addr 
 case 0x4016 //Joystick N1
    value=PORT_JOY1
    ?? DebugPrefix;"<Joy1Reg=[$";Hex(Addr,4);"]=$";Hex(Value,2)
 case else : value=0 
    ?? DebugPrefix;"<[$";Hex(Addr,4);"]=$";Hex(Value,2)
 end select
 result=true 
// dbg.break
end

for i=16 to 19
 mem.KBReader(i)=MemIOReader : mem.KBWriter(i)=MemIOWriter
next i

'--------------------------------- Others -------------------------------------

//Make information string to show in emulator caption bar
function GetTitleString as string
 Result="Mapper="+Str(nes_Mapper)+", ROM="+Str(nes_ROMSize)+"Kb"
 Result=Result+", VROM="+Str(nes_VROMSize)+"Kb, VRAM="
 select case nes_VRAMMode
 case vramDefault : Result=Result+"Default"
 case vram1xPage : Result=Result+"1xPage"
 case vramHorizontal : Result=Result+"Horizontal" 
 case vramVertical : Result=Result+"Vertical"
 case vram4xPages : Result=Result+"4xPages"
 case else : Result=Result+"Unknown" : end
 if nes_PALMode then Result=Result+", PAL" else Result=Result+", NTSC"
end

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

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

 'Internal RAM ($0000..$1FFF, 2Kb x 4 mirrors)
 for i=0 to 7 : mem.KBAccess(i)=memReadWrite : next
 mem.KBIndex(2)=0 : mem.KBIndex(4)=0 : mem.KBIndex(6)=0 //first KB mirrors
 mem.KBIndex(3)=1 : mem.KBIndex(5)=1 : mem.KBIndex(7)=1 //second KB mirrors

 'Video (PPU) I/O registers ($2000..$3FFF, 8 regs of byte size mirrored)
 for i=8 to 15 : mem.KBAccess(i)=memReadWrite : next //TODO - attach handlers

 'Other I/O registers (sound, joystick etc) ($4000..$4FFF)
 for i=16 to 19 : mem.KBAccess(i)=memReadWrite : next

 'Expansion ROMs ($5000..$5FFF)
 for i=20 to 23 : mem.KBAccess(i)=memReadOnly : next //TODO

 'Cartridge SRAM ($6000..$7FFF)
 if nes_HasSRAM then
    for i=24 to 31 : mem.KBAccess(i)=memReadWrite : next
 else
    for i=24 to 31 : mem.KBAccess(i)=memReadOnly : next
 end if

 'Cartridge ROM 2x16Kb banks ($8000..$BFFF, $C000...$FFFF)
 for i=32 to 47 : mem.KBAccess(i)=memReadOnly : next 'handled by mapper
 for i=48 to 63 : mem.KBAccess(i)=memReadOnly : next 'handled by mapper

 'Additional memory space (handled by mapper and ppu)
 for i=nes_ROMStart to nes_ROMStart+nes_ROMSize-1 'ROM data banks
     mem.KBAccess(i)=memReadOnly : next
 for i=nes_VROMStart to nes_VROMStart+nes_VROMSize-1 'VROM data banks
     mem.KBAccess(i)=memReadOnly : next
 for i=nes_RAMStart to nes_RAMStart+nes_RAMSize-1 'RAM additional banks
     mem.KBAccess(i)=memReadWrite : next
 for i=nes_VRAMStart to nes_VRAMStart+nes_VRAMSize-1 'VRAM (PPU memory)
     mem.KBAccess(i)=memReadWrite : next
 mem.KBAccess(nes_SPRTABStart)=memReadWrite 'Sprite memory (256 bytes)

 'Start of video memory in main emulated memory for PPU usage
 ppu.MemoryStart=nes_VRAMStart*1024 'Kb -> byte offset

 MapVRAM 'make initial VRAM mirroring (see "mappers" file)

 'Init memory map by mapper index
 select case nes_Mapper 
 case 0   : InitMap0   'default
 case 1   : InitMap1   'MMC1 //TODO
 case 2   : InitMap2   'UNROM
 case 3   : InitMap3   'CNROM
 case 11  : InitMap11  'Color Dreams
 case 79  : InitMap79  'Nina-3
 case else 'not supported mappers
   ErrorMsg("Mapper "+Str(nes_Mapper)+" unsupported currently!")
 end select

 'attach title string function
 pc.GetTitleString=GetTitleString

 'success 
 result=true
end

