'******************************************************************************
'* 
'* NES VIDEO (PPU) EMULATION (VIDEO DEVICE)
'*
'* Current emulation is not cycle-wise
'*
'* Supported platform/bus: NES
'*
'* Version history:
'*  2009 - initial emulation (WadiM)
'*
'****************************************************************************** 

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

'device interface support
public use object DEVICE

'internal implementation (direct)
use internal "NESPPU"

//device name
DeviceName="NES Video"
DebugName="VIDEO"  

//variables
dim i as integer

'---------------------- Resulting image and palette ---------------------------

//Video bitmap
use object BITMAP_IMAGE as image : image.Width=256 : image.Height=240

  //Palette (accordingly to Loopy doc)
  image.Palette(0x00)=RGB(0x75,0x75,0x75) : image.Palette(0x20)=RGB(0xFF,0xFF,0xFF)
  image.Palette(0x01)=RGB(0x27,0x1B,0x8F) : image.Palette(0x21)=RGB(0x3F,0xBF,0xFF)
  image.Palette(0x02)=RGB(0x00,0x00,0xAB) : image.Palette(0x22)=RGB(0x5F,0x97,0xFF)
  image.Palette(0x03)=RGB(0x47,0x00,0x9F) : image.Palette(0x23)=RGB(0xA7,0x8B,0xFD)
  image.Palette(0x04)=RGB(0x8F,0x00,0x77) : image.Palette(0x24)=RGB(0xF7,0x7B,0xFF)
  image.Palette(0x05)=RGB(0xAB,0x00,0x13) : image.Palette(0x25)=RGB(0xFF,0x77,0xB7)
  image.Palette(0x06)=RGB(0xA7,0x00,0x00) : image.Palette(0x26)=RGB(0xFF,0x77,0x63)
  image.Palette(0x07)=RGB(0x7F,0x0B,0x00) : image.Palette(0x27)=RGB(0xFF,0x9B,0x3B)
  image.Palette(0x08)=RGB(0x43,0x2F,0x00) : image.Palette(0x28)=RGB(0xF3,0xBF,0x3F)
  image.Palette(0x09)=RGB(0x00,0x47,0x00) : image.Palette(0x29)=RGB(0x83,0xD3,0x13)
  image.Palette(0x0A)=RGB(0x00,0x51,0x00) : image.Palette(0x2A)=RGB(0x4F,0xDF,0x4B)
  image.Palette(0x0B)=RGB(0x00,0x3F,0x17) : image.Palette(0x2B)=RGB(0x58,0xF8,0x98)
  image.Palette(0x0C)=RGB(0x1B,0x3F,0x5F) : image.Palette(0x2C)=RGB(0x00,0xEB,0xDB)
  image.Palette(0x0D)=RGB(0x00,0x00,0x00) : image.Palette(0x2D)=RGB(0x00,0x00,0x00)
  image.Palette(0x0E)=RGB(0x00,0x00,0x00) : image.Palette(0x2E)=RGB(0x00,0x00,0x00)
  image.Palette(0x0F)=RGB(0x00,0x00,0x00) : image.Palette(0x2F)=RGB(0x00,0x00,0x00)
  image.Palette(0x10)=RGB(0xBC,0xBC,0xBC) : image.Palette(0x30)=RGB(0xFF,0xFF,0xFF)
  image.Palette(0x11)=RGB(0x00,0x73,0xEF) : image.Palette(0x31)=RGB(0xAB,0xE7,0xFF)
  image.Palette(0x12)=RGB(0x23,0x3B,0xEF) : image.Palette(0x32)=RGB(0xC7,0xD7,0xFF)
  image.Palette(0x13)=RGB(0x83,0x00,0xF3) : image.Palette(0x33)=RGB(0xD7,0xCB,0xFF)
  image.Palette(0x14)=RGB(0xBF,0x00,0xBF) : image.Palette(0x34)=RGB(0xFF,0xC7,0xFF)
  image.Palette(0x15)=RGB(0xE7,0x00,0x5B) : image.Palette(0x35)=RGB(0xFF,0xC7,0xDB)
  image.Palette(0x16)=RGB(0xDB,0x2B,0x00) : image.Palette(0x36)=RGB(0xFF,0xBF,0xB3)
  image.Palette(0x17)=RGB(0xCB,0x4F,0x0F) : image.Palette(0x37)=RGB(0xFF,0xDB,0xAB)
  image.Palette(0x18)=RGB(0x8B,0x73,0x00) : image.Palette(0x38)=RGB(0xFF,0xE7,0xA3)
  image.Palette(0x19)=RGB(0x00,0x97,0x00) : image.Palette(0x39)=RGB(0xE3,0xFF,0xA3)
  image.Palette(0x1A)=RGB(0x00,0xAB,0x00) : image.Palette(0x3A)=RGB(0xAB,0xF3,0xBF)
  image.Palette(0x1B)=RGB(0x00,0x93,0x3B) : image.Palette(0x3B)=RGB(0xB3,0xFF,0xCF)
  image.Palette(0x1C)=RGB(0x00,0x83,0x8B) : image.Palette(0x3C)=RGB(0x9F,0xFF,0xF3)
  image.Palette(0x1D)=RGB(0x00,0x00,0x00) : image.Palette(0x3D)=RGB(0x00,0x00,0x00)
  image.Palette(0x1E)=RGB(0x00,0x00,0x00) : image.Palette(0x3E)=RGB(0x00,0x00,0x00)
  image.Palette(0x1F)=RGB(0x00,0x00,0x00) : image.Palette(0x3F)=RGB(0x00,0x00,0x00)

'-------------------------------- H/W Interface -------------------------------

//Read/write video memory
public function ReadMemory alias ReadMemory
public function WriteMemory alias WriteMemory

//Read/write video memory starting offser in main memory space
public property MemoryStart alias MemoryStart
public function MemoryStart alias MemoryStart

'------------------------------ Rendering -------------------------------------

//Rendering event handler
function RenderEvent(freq as integer,eventid as dword) as boolean
 result=true 'need next event call
 'render screen only if something changed
 if Changed then
  pc.DrawScreen(RenderScreen) 'internal rendering
  Changed=false
 end if
 VBLANK=true : if NMIAllowed then pc.SetNMI(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)
 if EventFreq<100 then EventFreq=100

 'init internal implementation
 InitInternal(image.Object) 'target image and palette

 //register screen rendering event
 if nes_PALMode then //PAL
    pc.NewFreqEvent(50)=RenderEvent '50Hz screen rendering
 else //NTSC
    pc.NewFreqEvent(60)=RenderEvent '60Hz screen rendering
 end if

 //success
 result=true

end 

//Device finalization
public procedure DEV_DONE(stream as object)
 //clear bitmap
 image.height=0
 //parent call
 DEV_DONE(stream)
end

