unit _MXDRVm_OPMEmu;

interface

uses
  Windows, SysUtils;

function X68Sound_Open(PCMRate:integer):boolean;
function X68Sound_Open_withSound(PCMRate:integer):boolean;
function X68Sound_Open_withRomeo(PCMRate,late:integer):boolean;
procedure X68Sound_Disable;
procedure X68Sound_Stop;
procedure X68Sound_OPMSet(adrs,data:byte);
procedure X68Sound_OpmWait;
procedure X68Sound_DmaPokeW(adrs:byte; data:word);
procedure X68Sound_DmaPokeL(adrs:byte; data:longint);

// procedure X68Sound_SetTotalVolume(Volume:LongInt);
procedure X68Sound_SetAdpcmMode(mode:word);
procedure X68Sound_AdpcmoutMain(stat:byte; mode,len:word; adrs:pointer);

procedure X68Sound_sound_iocs_init;
procedure X68Sound__iocs_opmset(adrs,data:LongInt);
function  X68Sound__iocs_opmsns:LongInt;
// function  X68Sound__iocs_opmintst(addr:pointer):LongInt;
procedure X68Sound__iocs_adpcmout(addr:pointer; mode,len:LongInt);
procedure X68Sound__iocs_adpcmmod(mode:LongInt);
function  X68Sound__iocs_adpcmsns:LongInt;
// procedure X68Sound__iocs_adpcmaot(struct _chain *tbl, int mode, int cnt);
// procedure X68Sound__iocs_adpcmlot(struct _chain2 *tbl, int mode);

implementation

uses _SimpleDialog,_MXDRVm_OPMEmu_const;

var
  AdpcmStat:byte;
  OpmReg1B:byte;
  DmaErrCode:byte;

procedure X68Sound_Stop;
var
  Ch,Part:LongInt;
  ADPCMPlayFlag:boolean;
begin
  X68Sound_OpmInt(nil);
  X68Sound_BetwInt(nil);

  X68Sound_OPMSet($14,0);

  for Ch:=0 to 15 do
    if Ch<=7 then begin
      X68Sound_OPMSet($08, Ch);
      for Part:=0 to 3 do
        X68Sound_OPMSet($60+Ch+(Part*8), 127); // TL to 0
      end
      else
      X68Sound_Pcm8_SetMode(Ch-8,0);

  ADPCMPlayFlag:=True;
  While ADPCMPlayFlag=True do begin
    ADPCMPlayFlag:=False;
    for Ch:=0 to 7 do begin
      if X68Sound_Pcm8_GetRest(Ch)<>0 then ADPCMPlayFlag:=True;
    end;
  end;
end;

{
procedure TOPMEmu.SetTotalVolume(Volume:LongInt);
var
  SetVolume:LongInt;
begin
  SetVolume:=Volume div 4; // MXDRV Volume 1024 -> X68Sound.DLL Volume 256

  if SetVolume>=0 then begin
    X68Sound_TotalVolume(SetVolume);
    Main.TotalVolumeRefresh;
  end;
end;
}

procedure DmaIntProc; export; cdecl;
begin
  if (AdpcmStat and $80)=0 then begin
    X68Sound_PpiCtrl($01);
    X68Sound_PpiCtrl($03);
    X68Sound_AdpcmPoke($01);
  end;
  AdpcmStat:=0;
  X68Sound_DmaPoke($00, $FF);
end;

procedure DmaErrIntProc; export; cdecl;
begin
  DmaErrCode:=X68Sound_DmaPeek($01);
  DmaIntProc;
end;

function X68Sound_Open(PCMRate:integer):boolean;
var
  res:LongInt;
  ErrorMes:string;
begin
  AdpcmStat:=0;
  OpmReg1B:=0;
  DmaErrCode:=0;

  res:=X68Sound_StartPcm(PCMRate,1,1,5);
  if res=0 then begin
    Result:=True;
    end else begin
    Result:=False;
    case res of
      X68SNDERR_DLL:           ErrorMes:='メモリ確保に失敗しました。';
      X68SNDERR_FUNC:          ErrorMes:='既にX68Sound.dllが実行中です。';
      else
      ErrorMes:='ExtendError : 原因不明な未定義エラー('+IntToStr(res)+')';
    end;
    ShowMessage('X68Sound.dll初期化エラー','X68Sound.dll が起動できません。'+CRLF+ErrorMes+CRLF+'解放してから再起動、又はWindowsを再起動してみて下さい。');
  end;
end;

function X68Sound_Open_withSound(PCMRate:integer):boolean;
var
  res:LongInt;
  ErrorMes:string;
begin
  AdpcmStat:=0;
  OpmReg1B:=0;
  DmaErrCode:=0;

  res:=X68Sound_Start(PCMRate,1,1,5,5,200,1.0);
  if res=0 then begin
    Result:=True;
    end else begin
    Result:=False;
    case res of
      X68SNDERR_DLL:           ErrorMes:='メモリ確保に失敗しました。';
      X68SNDERR_FUNC:          ErrorMes:='既にX68Sound.dllが実行中です。';
      else
      ErrorMes:='ExtendError : 原因不明な未定義エラー('+IntToStr(res)+')';
    end;
    ShowMessage('X68Sound.dll初期化エラー','X68Sound.dll が起動できません。'+CRLF+ErrorMes+CRLF+'解放してから再起動、又はWindowsを再起動してみて下さい。');
  end;
end;

function X68Sound_Open_withRomeo(PCMRate,late:integer):boolean;
var
  res:LongInt;
  ErrorMes:string;
begin
  AdpcmStat:=0;
  OpmReg1B:=0;
  DmaErrCode:=0;

  res:=X68Sound_Start(PCMRate,2,1,5,5,late,1.0);
  if res=0 then begin
    Result:=True;
    end else begin
    Result:=False;
    case res of
      X68SNDERR_DLL:           ErrorMes:='メモリ確保に失敗しました。';
      X68SNDERR_FUNC:          ErrorMes:='既にX68Sound.dllが実行中です。';
      else
      ErrorMes:='ExtendError : 原因不明な未定義エラー('+IntToStr(res)+')';
    end;
    ShowMessage('X68Sound.dll初期化エラー','X68Sound.dll が起動できません。'+CRLF+ErrorMes+CRLF+'解放してから再起動、又はWindowsを再起動してみて下さい。');
  end;
end;

procedure X68Sound_Disable;
begin
  X68Sound_Free;
end;

procedure X68Sound_OPMSet(adrs,data:byte);
begin
  if adrs=$1b then // ct1,ct2,wave set adrs
    data:=$c0+(data and $3f); // bin 1100 0000 + data

  X68Sound_OpmWait;
  X68Sound_OpmReg(adrs);
  X68Sound_OpmWait;
  X68Sound_OpmPoke(data);
end;

procedure X68Sound_OpmWait;
begin
  while (X68Sound_OpmPeek() and $80)<>0 do begin end;
end;

procedure X68Sound_DmaPokeW(adrs:byte; data:word);
begin
  X68Sound_DmaPoke(adrs+0,byte(data div $100));
  X68Sound_DmaPoke(adrs+1,byte(data));
end;

procedure X68Sound_DmaPokeL(adrs:byte; data:longint);
begin
  X68Sound_DmaPoke(adrs+0,byte(data div $1000000));
  X68Sound_DmaPoke(adrs+1,byte(data div $10000));
  X68Sound_DmaPoke(adrs+2,byte(data div $100));
  X68Sound_DmaPoke(adrs+3,byte(data));
end;

// --------------------------------------- iocs

procedure X68Sound_sound_iocs_init;
begin
  X68Sound_DmaInt(DmaIntProc);
  X68Sound_DmaErrInt(DmaErrIntProc);
end;

procedure X68Sound__iocs_opmset(adrs,data:LongInt);
begin
  if adrs=$1b then // ct1,ct2,wave set adrs
    data:=$c0+(data and $3f); // bin 1100 0000 + data

  X68Sound_OpmWait;
  X68Sound_OpmReg(adrs);
  X68Sound_OpmWait;
  X68Sound_OpmPoke(data);
end;


function X68Sound__iocs_opmsns:LongInt;
begin
  Result:=X68Sound_OpmPeek;
end;

procedure X68Sound_SetAdpcmMode(mode:word);
var
  ppireg:byte;
  PanTbl: array[0..3] of byte;
begin
  if (mode >= $0200) then begin
    mode:=mode-$0200;
    OpmReg1B:=OpmReg1B and $7F;
    end else begin
    OpmReg1B:=OpmReg1B or $80;
  end;
  X68Sound_OpmWait;
  X68Sound_OpmReg($1B);
  X68Sound_OpmWait();
  X68Sound_OpmPoke(OpmReg1B);

  PanTbl[0]:=3;
  PanTbl[1]:=1;
  PanTbl[2]:=2;
  PanTbl[3]:=0;

  ppireg:=((mode div 64) and $0c) or PanTbl[mode and $03];
  ppireg:=ppireg or (X68Sound_PpiPeek() and $f0);
  X68Sound_DmaPoke($07, $88);
  X68Sound_PpiPoke(ppireg);
end;

procedure X68Sound_AdpcmoutMain(stat:byte; mode,len:word; adrs:pointer);
begin
  while (AdpcmStat<>0) do;
  AdpcmStat:=stat+2;
  X68Sound_DmaPoke($05, $32);

  X68Sound_DmaPoke($00, $FF);
  X68Sound_DmaPokeL($0c, LongInt(adrs));
  X68Sound_DmaPokeW($0a, len);
  X68Sound_SetAdpcmMode(mode);

  X68Sound_AdpcmPoke($02);
end;

procedure X68Sound__iocs_adpcmout(addr:pointer; mode,len:LongInt);
var
  dmalen:LongInt;
  stat:byte;
  dmaadrs:pointer;
begin
  while (AdpcmStat<>0) do;
  dmaadrs:=addr;
  while (len >= $0000ff00) do begin
    dmalen:=$0000ff00;
    stat:=$80;
    X68Sound_AdpcmoutMain(stat,mode,dmalen,dmaadrs);
    dmaadrs:=ptr(LongInt(dmaadrs)+dmalen);
    len:=len-dmalen;
    if len=0 then exit;
  end;
  stat:=$00;
  X68Sound_AdpcmoutMain(stat,mode,len,dmaadrs);
end;

procedure X68Sound__iocs_adpcmmod(mode:LongInt);
begin
  case mode of
    0: begin
      AdpcmStat:=0;
      X68Sound_PpiCtrl($01);
      X68Sound_PpiCtrl($03);
      X68Sound_AdpcmPoke($01);
      X68Sound_DmaPoke($07, $10);
    end;
    1: begin
      X68Sound_DmaPoke($07, $20);
    end;
    2: begin
      X68Sound_DmaPoke($07, $08);
    end;
  end;
end;

function X68Sound__iocs_adpcmsns:LongInt;
begin
  Result:=AdpcmStat and $7f;
end;

end.
