unit DACExports;

interface

uses
	WinAPI.Windows,
	{$IFDEF DEBUG}
		System.SysUtils,
	{$ENDIF}
	Core;

function DAC_SetRange1(iBoard: TBoardIndex; Ch: TChannelIndex = ciAll; Range: TRangeCode = rcBipolar10): TWinErrCode; cdecl;
function DAC_SetRangeAll(iBoard: TBoardIndex; RangeCt: TUInt32; pRange: TPRangeCode): TWinErrCode; cdecl;
// header files overload DAC_SetRange1 with DAC_SetRangeAll as DAC_SetRange
function DAC_OutputCounts(iBoard: TBoardIndex; Ch: TChannelIndex {= ciAll}; Counts: TUInt64): TWinErrCode; cdecl;
function DAC_OutputV(iBoard: TBoardIndex; Ch: TChannelIndex {= ciAll}; V: TDouble): TWinErrCode; cdecl;
// header files overload DAC_OutputV with DAC_OutputCounts as DAC_Output



// reserved for future implementation
//function DAC_SetControl(iBoard: TBoardIndex; ControlByteCt: TUInt32; pControlByteCt: TUInt32; pControlBytes: TPInt8): TWinErrCode; cdecl;
//function DAC_GetControl(iBoard: TBoardIndex; ControlByteCt: TUInt32; pControlByteCt: TUInt32; pControlBytes: TPInt8): TWinErrCode; cdecl;
//function DAC_OutputVAll(iBoard: TBoardIndex; Ct: TUInt32; pV: TPDouble): TWinErrCode; cdecl;
//function DAC_OutputCountsAll(iBoard: TBoardIndex; Ct: TUInt32; pCounts: TUInt64): TWinErrCode; cdecl;
// header files overload DAC_OutputVAll with DAC_OutputCountsAll as DAC_Output, along with DAC_OutputV and DAC_Output1



implementation

uses
	Math,
	PIOExports;

const
	LTC2664Cmd_Data      = $3; //Write code to N, update N, power up.
	LTC2664Cmd_Range     = $6; //Write span to N.
	LTC2664Cmd_Data_All  = $A; //Write code to all, update all, power up.
	LTC2664Cmd_Range_All = $E; //Write span to all.

	RangeParam: array[0..4] of record
		bBipolar: Boolean;
		Span: Double;
	end =
	(
		(bBipolar: False; Span:  5),
		(bBipolar: False; Span: 10),
		(bBipolar: True;  Span: 10),
		(bBipolar: True;  Span: 20),
		(bBipolar: True;  Span:  5)
	);

function LTC2664_WriteDAC(iBoard: TBoardIndex; Command, Address, Data: TUInt32): TWinErrCode;
begin
	Data := (Data and $FFFF) or ((Address and $F) shl 16) or ((Command and $F) shl 20);
	Result := PIO_Out32(iBoard, DACOffset, Data);
	{$IFDEF DEBUG}
		//AnsiMessageBox('LTC2664_WriteDAC wrote ' + AnsiString(IntToHex(Data, 8)) + '.', 'AIOAIO', MB_ICONINFORMATION);
	{$ENDIF}
end;

function ValidateDACRange(var Range: TRangeCode): TWinErrCode;
begin
	Result := ERROR_SUCCESS;
	case Range of
		rcUnipolar5 : Range := rcRangeCode0; rcRangeCode0: ;
		rcUnipolar10: Range := rcRangeCode1; rcRangeCode1: ;
		rcBipolar5  : Range := rcRangeCode2; rcRangeCode2: ;
		rcBipolar10 : Range := rcRangeCode3; rcRangeCode3: ;
		rcBipolar2_5: Range := rcRangeCode4; rcRangeCode4: ;
		rcNoChange: Result := ERROR_HANDLE_EOF;
		else Result := ERROR_INVALID_PARAMETER;
	end;
end;

function DAC_SetRange1(iBoard: TBoardIndex; Ch: TChannelIndex = ciAll; Range: TRangeCode = rcBipolar10): TWinErrCode;
begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	try
		if (Ch <> ciAll) and (Ch >= DACCt) then begin
			Result := ERROR_INVALID_INDEX;
			Exit;
		end;

		Result := ValidateDACRange(Range);
		if Result = ERROR_HANDLE_EOF then begin
			Result := ERROR_SUCCESS;
			Exit;
		end;
		if Result <> ERROR_SUCCESS then Exit;

		if Ch = ciAll then begin
			Result := LTC2664_WriteDAC(iBoard, LTC2664Cmd_Range_All, 0, Range);
			for Ch := 0 to DACCt-1 do
				Dev[iBoard].LastDACRanges[Ch] := Range
			;
		end
		else begin
			Result := LTC2664_WriteDAC(iBoard, LTC2664Cmd_Range, Ch, Range);
			Dev[iBoard].LastDACRanges[Ch] := Range;
		end;

		{
		if Ch = ciAll then
			Result := PIO_Out32(iBoard, RangesOffset, $01010101 * Range) //This conceptually hardcodes DACCt = 4.
		else
			Result := PIO_Out8(iBoard, RangesOffset + Ch, Range)
		;
		}
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_SetRangeAll(iBoard: TBoardIndex; RangeCt: TUInt32; pRange: TPRangeCode): TWinErrCode;
var
	Ranges: array of TRangeCode;
	iCh: TSNative;

begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	try
		if RangeCt = 0 then Exit;

		if pRange = nil then begin
			Result := ERROR_INVALID_ADDRESS;
			Exit;
		end;

		if RangeCt > DACCt then RangeCt := DACCt;
		//Copy in the provided ranges.
		SetLength(Ranges, DACCt);
		try
			Move(pRange^, Ranges[0], RangeCt);
		except
			Result := ERROR_INVALID_ADDRESS;
			Exit;
		end;
		//Validate.
		for iCh := 0 to RangeCt-1 do begin
			Result := ValidateDACRange(Ranges[iCh]);
			if Result = ERROR_HANDLE_EOF then Result := ERROR_SUCCESS;
			if Result <> ERROR_SUCCESS then Exit;
		end;
		//If they didn't provide enough, then continue the last one.
		if RangeCt < DACCt then
			for iCh := RangeCt to DACCt-1 do
				Ranges[iCh] := Ranges[RangeCt-1]
		;
		//Store.
		for iCh := 0 to DACCt-1 do
			Dev[iBoard].LastDACRanges[iCh] := Ranges[iCh]
		;
		//Write.
		for iCh := 0 to DACCt-1 do begin
			if Ranges[iCh] = rcNoChange then Continue;

			Result := LTC2664_WriteDAC(iBoard, LTC2664Cmd_Range, iCh, Ranges[iCh]);
			if Result <> ERROR_SUCCESS then Exit;
		end;

		{
		Result := PIO_Out32(iBoard, RangesOffset, TPUInt32(@Ranges[0])^); //This conceptually hardcodes DACCt = 4.
		}
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_OutputCounts(iBoard: TBoardIndex; Ch: TChannelIndex {= ciAll}; Counts: TUInt64): TWinErrCode;
var
	Command: LongWord;

begin
	{$IFDEF DEBUG}
		//AnsiMessageBox('DAC_OutputCounts(' + AnsiString(IntToHex(iBoard, 1)) + ', ' + AnsiString(IntToHex(Ch, 1)) + ', ' + AnsiString(IntToHex(Counts, 1)) + ')', 'AIOAIO', MB_ICONINFORMATION);
	{$ENDIF}

	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	try
		if (Ch <> ciAll) and (Ch >= DACCt) then begin
			Result := ERROR_INVALID_INDEX;
			Exit;
		end;

		Command := Counts shr 32;
		case Command of
			0: begin
				if Ch = ciAll then
					Result := LTC2664_WriteDAC(iBoard, LTC2664Cmd_Data_All, 0, Counts)
				else
					Result := LTC2664_WriteDAC(iBoard, LTC2664Cmd_Data, Ch, Counts)
				;
			end;
			else begin
				Result := ERROR_INVALID_DATA;
				Exit;
			end;
		end;
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function DAC_OutputV(iBoard: TBoardIndex; Ch: TChannelIndex {= ciAll}; V: TDouble): TWinErrCode;
var
	nCh, xCh: TChannelIndex;
	Data: TSNative;
	Range: TRangeCode;

begin
	{$IFDEF DEBUG}
		//AnsiMessageBox('DAC_OutputV(' + AnsiString(IntToHex(iBoard, 1)) + ', ' + AnsiString(IntToHex(Ch, 1)) + ', ' + AnsiString(FloatToStr(V)) + ')', 'AIOAIO', MB_ICONINFORMATION);
	{$ENDIF}

	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	try
		if Ch = ciAll then begin
			nCh := 0;
			xCh := DACCt-1;
		end
		else begin
			if Ch >= DACCt then begin
				Result := ERROR_INVALID_INDEX;
				Exit;
			end;
			nCh := Ch;
			xCh := Ch;
		end;

		for Ch := nCh to xCh do begin
			Range := Dev[iBoard].LastDACRanges[Ch];
			if Range = rcNoChange then Range := rcBipolar10;
			Data := Round(V / RangeParam[Range].Span * $10000);
			if RangeParam[Range].bBipolar then Data := Data + $8000;
			if (Data < $0000) or (Data > $FFFF) then begin
				Result := ERROR_INVALID_DATA;
				Exit;
			end;
			Result := LTC2664_WriteDAC(iBoard, LTC2664Cmd_Data, Ch, Data);
			if Result <> ERROR_SUCCESS then Exit;
		end;
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
	{$IFDEF DEBUG}
		//AnsiMessageBox('DAC_OutputV returned ' + AnsiString(IntToStr(Result)) + '.', 'AIOAIO', MB_ICONINFORMATION);
	{$ENDIF}
end;

end.
