'******************************************************************************
'* 
'* Channel of i8237 DMA Controller
'*
'* Used to implement complete chip as combination of channels
'*
'* Version history:
'*  - v.1.0 by WadiM (initial emulation)
'*
'****************************************************************************** 

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

'---------------------------- Channel params ----------------------------------

//Data registers
dim pageAddr    as dword=0  'address of memory page
dim baseOffset  as word=0   'base offset register
dim baseCounter as word=0   'base counter register
dim currOffset  as word=0   'current offset register
dim currCounter as word=0   'current counter register

//Mode registers
dim workMode  as byte=0 'work mode (0-verify, 1-to mem, 2-from mem, 3-invalid)
dim flipFlop  as boolean=false 'flag of 16-bit port part (false=low byte)
dim autoInit  as boolean=false 'automatic reinitialization
dim addrStep  as integer 'address changing step (>0-increment, <0-decrement)
dim transMode as byte=0 'transfer mode (0-demand,1-single,2-block,3-cascading)
dim Cascading as boolean=false 'cascade mode (to other DMA controller)

//Mode descriptions
dim workModeToStr(4) as string, transModeToStr(4) as string
workModeToStr=Array("verify","to mem","from mem","invalid")
transModeToStr=Array("on demand","single","block","cascading")

'--------------------------- Channel interface --------------------------------

//Common params (references to common for all channels params)
public use object REF_LIST as CommonParams 'configured by channel owner (DMA)
public dim FlipFlopIndex as integer=-1 'index of flip-flop flag in common params
public dim TempRegIndex as integer=-1 'index of temp. register in common params

//Service registers
public dim DebugPrefix as string 'debug messages prefix
public dim chIndex as byte=0 'channel index
public dim SWRequest as boolean=false 'request to transfer data by software DREQ
public dim HWRequest as boolean=false' request to transfer data by hardware DREQ
public dim Masked as boolean=false 'channel was masked (disabled or paused)
public dim Priority as byte=0 'channel priority (0-highest)
public dim TempReg as byte=0 'temporary register (used in transfers)

//Buffer (contains data to transfer)
public use object FIFO_STREAM as Buffer

//Write base (and current offset) of channel
public property OFFSET_PORT(Value as byte)
 flipFlop=CommonParams.Value(FlipFlopIndex)
 CommonParams.Value(FlipFlopIndex)=not(flipFlop)
 if flipFlop then
    baseOffset=(baseOffset and 0x00FF) or shl(Value,8) 'high byte
    currOffset=(currOffset and 0x00FF) or shl(Value,8) 'high byte
    ?? DebugPrefix;">CH";chIndex;"_OFFSET_PORT=";Hex(Value,2);"h (bits 8..15)"
 else
    baseOffset=(baseOffset and 0xFF00) or Value 'low byte
    currOffset=(currOffset and 0xFF00) or Value 'low byte
    ?? DebugPrefix;">CH";chIndex;"_OFFSET_PORT=";Hex(Value,2);"h (bits 0..7)"
 end if 
end

//Read current offset of channel
public function OFFSET_PORT as byte
 flipFlop=CommonParams.Value(FlipFlopIndex)
 CommonParams.Value(FlipFlopIndex)=not(flipFlop)
 if flipFlop then
    result=shr(currOffset,8) and 0xFF
    ?? DebugPrefix;"<CH";chIndex;"_OFFSET_PORT=";Hex(result,2);"h (bits 8..15)"
 else
    result=currOffset and 0xFF
    ?? DebugPrefix;"<CH";chIndex;"_OFFSET_PORT=";Hex(result,2);"h (bits 0..7)"
end if : end

//Write base (and current) counter of channel
public property COUNT_PORT(Value as byte)
 flipFlop=CommonParams.Value(FlipFlopIndex)
 CommonParams.Value(FlipFlopIndex)=not(flipFlop)
 if flipFlop then
    baseCounter=(baseCounter and 0x00FF) or shl(Value,8) 'high byte
    currCounter=(currCounter and 0x00FF) or shl(Value,8) 'high byte
    ?? DebugPrefix;">CH";chIndex;"_COUNT_PORT=";Hex(Value,2);"h (bits 8..15)"
 else
    baseCounter=(baseCounter and 0xFF00) or Value 'low byte
    currCounter=(currCounter and 0xFF00) or Value 'low byte
    ?? DebugPrefix;">CH";chIndex;"_COUNT_PORT=";Hex(Value,2);"h (bits 0..7)"
end if : end

//Read current counter of channel
public function COUNT_PORT as byte
 flipFlop=CommonParams.Value(FlipFlopIndex)
 CommonParams.Value(FlipFlopIndex)=not(flipFlop)
 if flipFlop then
    result=shr(currCounter,8) and 0xFF
    ?? DebugPrefix;"<CH";chIndex;"_COUNT_PORT=";Hex(result,2);"h (bits 8..15)"
 else
    result=currCounter and 0xFF
    ?? DebugPrefix;"<CH";chIndex;"_COUNT_PORT=";Hex(result,2);"h (bits 0..7)"
end if : end

//Write mode register
public property MODE_PORT(Value as byte)
 if (Value and 3)<>chIndex then Error("Incorrect mode value") 'check index
 workMode=shr(Value and 0xC,2) and 3 : autoInit=(Value and 0x10)<>0
 addrStep=iif((Value and 0x20)=0,+1,-1) : transMode=shr(Value,6) and 3
 Cascading=(transMode=3)
 ?? DebugPrefix;">CH";chIndex;"_MODE_PORT=";Hex(Value,2);"h"
 ?? " ";workModeToStr(workMode);", autoinit=";autoInit;", addrstep=";addrStep;
 ?? ", transf ";transModeToStr(transMode)
end

//Read mode register (unreadable) //TOCHECK
public function MODE_PORT as byte 
 result=0xFF : ?? DebugPrefix;"<CH";chIndex;"_MODE_PORT=";Hex(result,2);"h"
end

//dim asdf as integer=0

//Write page register //TODO - mask page addr for various models
public property PAGE_PORT(Value as byte)
 pageAddr=shl(Value,16) 'used all 8 bit (=24bit address, up to 16Mb on AT)
 ?? DebugPrefix;">CH";chIndex;"_PAGE_PORT=";Hex(Value,2);"h";
 ?? " (page addr=";Hex(pageAddr,8);"h)"
end

//Read page register
public function PAGE_PORT as byte
// asdf=asdf+1 : ?? "asdf=";asdf : if (asdf=255) and (chIndex=3) then dbg.break
 result=shr(pageAddr,16) and 0xFF //TOCHECK
 ?? DebugPrefix;"<CH";chIndex;"_PAGE_PORT=";Hex(result,2);"h"
end

'--------------------------- Transfering data ---------------------------------

//Verifying (no read/write)
public function TransferVerify as boolean 
 result=true
 'use approppriate transfer method
 select case transMode 
 case 0 'demand mode
  currOffset=currOffset+addrStep : currCounter=currCounter-1
  if currCounter=0xFFFF then 'finished
     if autoInit then : currOffset=baseOffset: currCounter=baseCounter : end if
     result=true
  else : result=false : end if
  ?? DebugPrefix;"-> verify (demand mode): left ";Hex(currCounter,4);"h bytes"
 case 1 'single mode
  currOffset=currOffset+addrStep : currCounter=currCounter-1
  if currCounter=0xFFFF then 'finished
     if autoInit then : currOffset=baseOffset: currCounter=baseCounter : end if
  end if
  result=true
  ?? DebugPrefix;"-> verify (single mode): transferred ";Hex(baseCounter,4);"h bytes"
 case 2 'block mode
  ?? DebugPrefix;"-> verify (block mode): transferred ";Hex(currCounter+1,4);"h bytes"
  currOffset=currOffset+addrStep*(currCounter+1) : currCounter=0xFFFF 
  if autoInit then : currOffset=baseOffset: currCounter=baseCounter : end if
  result=true
//  dbg.Break
 case 3 'cascading
  //TODO
  ?? DebugPrefix;"-> verify (cascading mode): transferred ";Hex(baseCounter,4);"h bytes"
 end select
 if result then : SWRequest=false : HWRequest=false : end if
end

//Writing to computer memory (from some source)
public function TransferWriteToMem as boolean 
 result=true
 result=true
 'use approppriate transfer method
 select case transMode 
 case 0 'demand mode
  currOffset=currOffset+addrStep : currCounter=currCounter-1
  if currCounter=0xFFFF then 'finished
     if autoInit then : currOffset=baseOffset: currCounter=baseCounter : end if
  end if
  result=true
  ?? DebugPrefix;"-> verify (demand mode): transferred ";Hex(baseCounter,4);"h bytes"
 case 1 'single mode
  currOffset=currOffset+addrStep : currCounter=currCounter-1
  if currCounter=0xFFFF then 'finished
     if autoInit then : currOffset=baseOffset: currCounter=baseCounter : end if
  end if
  ?? DebugPrefix;"-> verify (single mode): transferred ";Hex(baseCounter,4);"h bytes"
 case 2 'block mode
  ?? DebugPrefix;"-> verify (block mode): transferred ";Hex(currCounter+1,4);"h bytes"
  currOffset=currOffset+addrStep*(currCounter+1) : currCounter=0xFFFF 
  if autoInit then : currOffset=baseOffset: currCounter=baseCounter : end if
  result=true 'transfer finished
 case 3 'cascading
  //TODO
  ?? DebugPrefix;"-> verify (cascading mode): transferred ";Hex(baseCounter,4);"h bytes"
 end select
 if result then : SWRequest=false : HWRequest=false : end if
end

//Reading from computer memory (to some source)
public function TransferReadFromMem as boolean 
 result=true
 result=true
 'use approppriate transfer method
 select case transMode 
 case 0 'demand mode
  currOffset=currOffset+addrStep : currCounter=currCounter-1
  if currCounter=0xFFFF then 'finished
     if autoInit then : currOffset=baseOffset: currCounter=baseCounter : end if
  end if
  result=true
  ?? DebugPrefix;"-> verify (demand mode): transferred ";Hex(baseCounter,4);"h bytes"
 case 1 'single mode
  currOffset=currOffset+addrStep : currCounter=currCounter-1
  if currCounter=0xFFFF then 'finished
     if autoInit then : currOffset=baseOffset: currCounter=baseCounter : end if
  end if
  ?? DebugPrefix;"-> verify (single mode): transferred ";Hex(baseCounter,4);"h bytes"
 case 2 'block mode
  ?? DebugPrefix;"-> verify (block mode): transferred ";Hex(currCounter+1,4);"h bytes"
  currOffset=currOffset+addrStep*(currCounter+1) : currCounter=0xFFFF 
  if autoInit then : currOffset=baseOffset: currCounter=baseCounter : end if
  result=true 'transfer finished
 case 3 'cascading
  //TODO
  ?? DebugPrefix;"-> verify (cascading mode): transferred ";Hex(baseCounter,4);"h bytes"
 end select
 if result then : SWRequest=false : HWRequest=false : end if
end


//Transfer call
public function Transfer as boolean //TODO
 result=false

 ?? DebugPrefix;"CH";chIndex;" Transferring"

 if Cascading then exit //TODO

// dbg.Break

 'call appropriate transfer function
 select case workMode 
 case 0 to 2 : Result=TransferVerify  'verify (no read/write)
// case 1 : Result=TransferWriteToMem   'write to computer memory
// case 2 : Result=TransferReadFromMem 'read from computer memory
 case else : ?? "-> illegal transfer" : result=true 'invalid/illegal //TOCHECK
 end select

end

