MODULE YMF754;
IMPORT
SYSTEM, PCI, Strings, Files, Machine, Modules, SoundDevices,
Plugins, Objects, Kernel, KernelLog;
CONST
Logging = TRUE;
FNameInstRAM = "YMF754.Bin";
PluginDescPrefix = "Sound driver for Yamaha ";
BufferSizeMS = 100;
SizeDSPInstRAM = 80H;
SizeCtrlInstRAM = 3000H;
NofPlaySlots = 64;
NofPlaySlotPairs = NofPlaySlots DIV 2;
NofBanks = 2;
NofMixerChannels = 6;
NofNativeFreq = 7;
WorkBitTimeout = 250000;
PCIRegDS1EControl = 48H;
PCCRegAC97CmdData = 0060H;
PCCRegAC97CmdAddress = 0062H;
PCCRegAC97StatusData = 0064H;
PCCRegAC97StatusAddress = 0066H;
PCCRegVolLegOut = 0080H;
PCCRegVolDACOut = 0084H;
PCCRegVolZVOut = 0088H;
PCCRegVolSecAC97Out = 008CH;
PCCRegVolADCOut = 0090H;
PCCRegVolADCIn = 00A8H;
PCCRegVolRECIn = 00ACH;
PCCRegVolP44Out = 00B0H;
PCCRegVolSPDIFOut = 00B8H;
PCCRegADCSlotSamplingRate = 00C0H;
PCCRegADCSlotFormat = 00C8H;
PCCRegStatus = 0100H;
PCCRegControlSelect = 0104H;
PCCRegMode = 0108H;
PCCRegConfig = 0114H;
PCCRegPlayCtrlSize = 0140H;
PCCRegRecCtrlSize = 0144H;
PCCRegMapOfRec = 0150H;
PCCRegMapOfEff = 0154H;
PCCRegPlayCtrlBase = 0158H;
PCCRegRecCtrlBase = 015CH;
PCCRegEffCtrlBase = 0160H;
PCCRegWorkBase = 0164H;
PCCRegDSPInstRAM = 1000H;
PCCRegCtrlInstRAM = 4000H;
ACCRegReset = 00H;
ACCRegVolMasterOut = 02H;
ACCRegVolMic = 0EH;
ACCRegVolLineIn = 10H;
ACCRegVolCD = 12H;
ACCRegVolPCM = 18H;
ACCRegRecordSelect = 1AH;
ACCRegRecordGain= 1CH;
TYPE
BufferListener = SoundDevices.BufferListener;
Buffer = SoundDevices.Buffer;
MixerChangedProc = SoundDevices.MixerChangedProc;
NativeFreqTable = ARRAY NofNativeFreq OF RECORD
hz: LONGINT;
valRec: LONGINT;
valLpfK: LONGINT;
valLpfQ: LONGINT;
END;
ListMixerChangedProc = POINTER TO RECORD
proc: MixerChangedProc;
next: ListMixerChangedProc
END;
ListBuffer = POINTER TO RECORD
buff: Buffer;
next: ListBuffer;
END;
ListPlayerChannel = POINTER TO RECORD
channel: PlayerChannel;
next: ListPlayerChannel;
END;
NameStr = ARRAY 32 OF CHAR;
DescStr = ARRAY 128 OF CHAR;
PlayRecBuffer = POINTER TO ARRAY OF CHAR;
PlayCtrlDataTable = RECORD
numOfPlay: LONGINT;
playSlotBase: ARRAY NofPlaySlots OF LONGINT;
END;
RecSlotCtrlData = RECORD
recSlotREC: ARRAY NofBanks OF RecBank;
recSlotADC: ARRAY NofBanks OF RecBank;
END;
RecBank = RECORD;
pgBase,
pgLoopEndAdr,
pgStartAdr,
numOfLoops: LONGINT;
END;
PlayBank = RECORD
format, loopDefault, pgBase, pgLoop, pgLoopEnd, pgLoopFrac,
pgDeltaEnd, lpfKEnd, egGainEnd, lchGainEnd, rchGainEnd, effect1GainEnd,
effect2GainEnd, effect3GainEnd, lpfQ, status, numOfFrames, loopCount,
pgStart, pgStartFrac, pgDelta, lpfK, egGain, lchGain,
rchGain, effect1Gain, effect2Gain, effect3Gain, lpfD1, lpfD2: LONGINT;
END;
PlaySlotCtrlData = ARRAY NofBanks OF PlayBank;
BufferListenerCaller = OBJECT
VAR
bufferListener: BufferListener;
first, last: ListBuffer;
close: BOOLEAN;
actualListener: BufferListener;
actualBuffer: Buffer;
PROCEDURE &Constr*;
BEGIN
bufferListener := NIL;
first := NIL;
last := NIL;
close := FALSE;
END Constr;
PROCEDURE Close;
BEGIN {EXCLUSIVE}
close := TRUE;
END Close;
PROCEDURE RegisterBufferListener(bufferListener: BufferListener);
BEGIN {EXCLUSIVE}
SELF.bufferListener := bufferListener;
END RegisterBufferListener;
PROCEDURE ReturnBuffer(buffer: Buffer);
VAR item: ListBuffer;
BEGIN {EXCLUSIVE}
IF bufferListener = NIL THEN RETURN END;
NEW(item);
item.buff := buffer;
item.next := NIL;
IF first = NIL THEN
first := item;
last := item;
ELSE
last.next := item;
last := item;
END;
END ReturnBuffer;
(* Active part of object *)
BEGIN {ACTIVE, SAFE, PRIORITY(Objects.High)}
WHILE ~close DO
BEGIN {EXCLUSIVE}
AWAIT((first # NIL) OR close);
IF ~close THEN
(* get actual listener and buffer for calling afterwards *)
actualListener := bufferListener;
actualBuffer := first.buff;
first := first.next;
END;
END;
(* Do listener calls outside of EXCLUSIVE region! *)
IF close THEN
(* return all buffers *)
WHILE first # NIL DO
bufferListener(first.buff);
first := first.next;
END;
ELSE
(* return actual buffer *)
actualListener(actualBuffer);
END;
END;
bufferListener := NIL;
END BufferListenerCaller;
PCIAudioControl = OBJECT
VAR base: SYSTEM.ADDRESS;
CntrlInst1E: BOOLEAN;
PROCEDURE &Constr*(base: SYSTEM.ADDRESS; CntrlInst1E: BOOLEAN);
BEGIN
SELF.base := base;
SELF.CntrlInst1E := CntrlInst1E;
END Constr;
PROCEDURE RegRead8(offset: LONGINT): LONGINT;
BEGIN
RETURN SYSTEM.GET8(base + offset);
END RegRead8;
PROCEDURE RegRead16(offset: LONGINT): LONGINT;
BEGIN
RETURN SYSTEM.GET16(base + offset);
END RegRead16;
PROCEDURE RegRead32(offset: LONGINT): LONGINT;
BEGIN
RETURN SYSTEM.GET32(base + offset);
END RegRead32;
PROCEDURE RegWrite8(offset: LONGINT; val: LONGINT);
BEGIN
SYSTEM.PUT8(base + offset, val);
END RegWrite8;
PROCEDURE RegWrite16(offset: LONGINT; val: LONGINT);
BEGIN
SYSTEM.PUT16(base + offset, val);
END RegWrite16;
PROCEDURE RegWrite32(offset: LONGINT; val: LONGINT);
BEGIN
SYSTEM.PUT32(base + offset, val);
END RegWrite32;
PROCEDURE Initialize;
VAR
cnt: LONGINT;
t: Kernel.Timer;
BEGIN {EXCLUSIVE}
RegWrite32(PCCRegVolDACOut, 0);
RegWrite32(PCCRegConfig, 0);
cnt := 0;
WHILE (cnt < WorkBitTimeout) & (1 IN SYSTEM.VAL(SET, RegRead32(PCCRegStatus))) DO
INC(cnt);
END;
RegWrite32(PCCRegMode, 10000H);
RegWrite32(PCCRegMode, 0);
RegWrite32(PCCRegMapOfRec, 0);
RegWrite32(PCCRegMapOfEff, 0);
RegWrite32(PCCRegPlayCtrlBase, 0);
RegWrite32(PCCRegRecCtrlBase, 0);
RegWrite32(PCCRegEffCtrlBase, 0);
RegWrite32(PCCRegWorkBase, 0);
LoadInstructionCode;
RegWrite32(PCCRegConfig, 1);
NEW(t);
cnt := 50;
WHILE (cnt >= 0) & ~((SYSTEM.SIZEOF(PlayBank) DIV 4 = RegRead32(PCCRegPlayCtrlSize)) & (SYSTEM.SIZEOF(RecBank) DIV 4 = RegRead32(PCCRegRecCtrlSize))) DO
t.Sleep(2);
DEC(cnt);
END;
ASSERT(cnt >= 0);
t.Sleep(10);
RegWrite32(PCCRegVolLegOut, 0);
RegWrite32(PCCRegVolZVOut, 0);
RegWrite32(PCCRegVolSecAC97Out, 0);
RegWrite32(PCCRegVolADCOut, 0);
RegWrite32(PCCRegVolRECIn, 0);
RegWrite32(PCCRegVolP44Out, 0);
RegWrite32(PCCRegVolSPDIFOut, 0);
RegWrite32(PCCRegVolDACOut, 3FFF3FFFH);
RegWrite32(PCCRegVolADCIn, 0);
END Initialize;
PROCEDURE UnInitialize;
VAR cnt: LONGINT;
BEGIN {EXCLUSIVE}
RegWrite32(PCCRegVolDACOut, 0);
RegWrite32(PCCRegVolADCIn, 0);
RegWrite32(PCCRegConfig, 0);
cnt := 0;
WHILE (cnt < WorkBitTimeout) & (1 IN SYSTEM.VAL(SET, RegRead32(PCCRegStatus))) DO
INC(cnt);
END;
RegWrite32(PCCRegMode, 10000H);
RegWrite32(PCCRegMapOfRec, 0);
RegWrite32(PCCRegMapOfEff, 0);
RegWrite32(PCCRegPlayCtrlBase, 0);
RegWrite32(PCCRegRecCtrlBase, 0);
RegWrite32(PCCRegEffCtrlBase, 0);
RegWrite32(PCCRegWorkBase, 0);
END UnInitialize;
PROCEDURE LoadInstructionCode;
VAR
f: Files.File;
r: Files.Reader;
offset, data: LONGINT;
BEGIN
f := Files.Old(FNameInstRAM);
ASSERT(f # NIL);
ASSERT(f.Length() = SizeDSPInstRAM + SizeCtrlInstRAM * 2);
Files.OpenReader(r, f, 0);
offset := PCCRegDSPInstRAM;
WHILE offset < (PCCRegCtrlInstRAM + SizeCtrlInstRAM) DO
r.RawLInt(data);
RegWrite32(offset, data);
INC(offset, SYSTEM.SIZEOF(LONGINT));
IF offset = PCCRegDSPInstRAM + SizeDSPInstRAM THEN
offset := PCCRegCtrlInstRAM;
IF CntrlInst1E THEN
r.SkipBytes(SizeCtrlInstRAM);
END;
END;
END;
END LoadInstructionCode;
END PCIAudioControl;
AC97Control = OBJECT
VAR PCC: PCIAudioControl;
PROCEDURE &Constr*(PCC: PCIAudioControl);
BEGIN
SELF.PCC := PCC;
END Constr;
PROCEDURE RegRead16(offset: LONGINT): LONGINT;
BEGIN {EXCLUSIVE}
PCC.RegWrite16(PCCRegAC97CmdAddress, offset + 8000H);
ASSERT(BusyWait());
RETURN PCC.RegRead16(PCCRegAC97StatusData);
END RegRead16;
PROCEDURE RegWrite16(offset: LONGINT; val: LONGINT);
BEGIN {EXCLUSIVE}
PCC.RegWrite16(PCCRegAC97CmdAddress, offset);
PCC.RegWrite16(PCCRegAC97CmdData, val);
ASSERT(BusyWait());
END RegWrite16;
PROCEDURE BusyWait(): BOOLEAN;
VAR t: Kernel.MilliTimer;
BEGIN
Kernel.SetTimer(t, 2);
WHILE ~Kernel.Expired(t) DO
IF ~(15 IN SYSTEM.VAL(SET, PCC.RegRead16(PCCRegAC97StatusAddress))) THEN
RETURN TRUE;
END;
END;
RETURN FALSE;
END BusyWait;
PROCEDURE Reset;
BEGIN
RegWrite16(ACCRegReset, 0);
RegWrite16(ACCRegRecordSelect, 0505H);
END Reset;
END AC97Control;
MixerChannel* = OBJECT(SoundDevices. MixerChannel)
VAR
drv: Driver;
name: NameStr;
desc: DescStr;
regVol: LONGINT;
inverted: BOOLEAN;
volBits: LONGINT;
volume: LONGINT;
mute: BOOLEAN;
PROCEDURE &Constr*(drv: Driver; regVol: LONGINT; inverted, mute: BOOLEAN; name: NameStr; desc: DescStr);
VAR tmpVol: LONGINT;
BEGIN
SELF.drv := drv;
SELF.regVol := regVol;
SELF.inverted := inverted;
SELF.name := name;
SELF.desc := desc;
drv.ACC.RegWrite16(regVol, 803FH);
tmpVol := drv.ACC.RegRead16(regVol);
SELF.volBits := 0;
WHILE ODD(tmpVol) DO
tmpVol := SYSTEM.LSH(tmpVol, -1);
INC(SELF.volBits);
END;
SELF.mute := mute;
SetVolume(128);
END Constr;
PROCEDURE CallListeners;
VAR item: ListMixerChangedProc;
BEGIN
item := drv.mixerChannelListeners;
WHILE item # NIL DO
item.proc(SELF);
item := item.next;
END;
END CallListeners;
PROCEDURE GetName*(VAR name : ARRAY OF CHAR);
BEGIN
COPY(SELF.name, name);
END GetName;
PROCEDURE GetDesc*(VAR desc : ARRAY OF CHAR);
BEGIN
COPY(SELF.desc, desc);
END GetDesc;
PROCEDURE SetVolume*(volume : LONGINT);
BEGIN {EXCLUSIVE}
ASSERT((volume >= 0) & (volume <= 255));
SELF.volume := volume;
IF inverted THEN
volume := 255 - volume;
END;
volume := SYSTEM.LSH(volume, volBits - 8);
volume := volume + SYSTEM.LSH(volume, 8);
IF SELF.mute THEN
volume := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, volume) + {15});
END;
drv.ACC.RegWrite16(regVol, volume);
CallListeners;
END SetVolume;
PROCEDURE GetVolume*() : LONGINT;
BEGIN
RETURN SELF.volume;
END GetVolume;
PROCEDURE SetMute*(mute : BOOLEAN);
VAR volume: SET;
BEGIN {EXCLUSIVE}
SELF.mute := mute;
volume := SYSTEM.VAL(SET, drv.ACC.RegRead16(regVol));
IF mute THEN
volume := volume + {15};
ELSE
volume := volume - {15};
END;
drv.ACC.RegWrite16(regVol, SYSTEM.VAL(LONGINT, volume));
CallListeners;
END SetMute;
PROCEDURE GetIsMute*() : BOOLEAN;
BEGIN
RETURN SELF.mute;
END GetIsMute;
END MixerChannel;
Channel* = OBJECT(SoundDevices.Channel)
VAR
drv: Driver;
bufferListenerCaller: BufferListenerCaller;
buffFirst, buffLast: ListBuffer;
buffFirstPos: LONGINT;
playRecBuff: PlayRecBuffer;
playRecBuffSize: LONGINT;
playRecBuffPhys: LONGINT;
playRecBuffPos: LONGINT;
samplePosition: LONGINT;
bytesPerSampleExp: LONGINT;
running: BOOLEAN;
closed: BOOLEAN;
volume: LONGINT;
PROCEDURE &Constr*(drv: Driver);
BEGIN
SELF.drv := drv;
buffFirst := NIL;
buffLast := NIL;
buffFirstPos := 0;
samplePosition := 0;
running := FALSE;
closed := FALSE;
volume := 0;
NEW(bufferListenerCaller);
END Constr;
PROCEDURE RegisterBufferListener*(bufferListener : BufferListener);
BEGIN {EXCLUSIVE}
ASSERT(~closed);
bufferListenerCaller.RegisterBufferListener(bufferListener);
IF Logging THEN
IF bufferListener = NIL THEN
KernelLog.String("YMF754 - BufferListener unregistered");
ELSE
KernelLog.String("YMF754 - BufferListener registered");
END;
KernelLog.Ln;
END;
END RegisterBufferListener;
PROCEDURE QueueBuffer*(x : Buffer);
VAR buffItem: ListBuffer;
BEGIN {EXCLUSIVE}
ASSERT(~closed);
ASSERT(x # NIL);
ASSERT(x.data # NIL);
ASSERT(x.len MOD SYSTEM.LSH(1, bytesPerSampleExp) = 0);
NEW(buffItem);
buffItem.buff := x;
buffItem.next := NIL;
IF buffFirst = NIL THEN
buffFirst := buffItem;
ELSE
buffLast.next := buffItem;
END;
buffLast := buffItem;
END QueueBuffer;
PROCEDURE ReturnCurrentBuffer;
BEGIN
bufferListenerCaller.ReturnBuffer(buffFirst.buff);
buffFirst := buffFirst.next;
buffFirstPos := 0;
END ReturnCurrentBuffer;
PROCEDURE PrepareData;
BEGIN
HALT(99);
END PrepareData;
PROCEDURE Activate;
BEGIN
HALT(99);
END Activate;
PROCEDURE Deactivate;
BEGIN
HALT(99);
END Deactivate;
PROCEDURE SetPlayRecVol(volume: LONGINT);
BEGIN
HALT(99);
END SetPlayRecVol;
PROCEDURE SetVolume*(volume : LONGINT);
BEGIN {EXCLUSIVE}
ASSERT(~closed);
ASSERT(volume >= 0);
SELF.volume := volume;
IF volume > 255 THEN
volume := 255;
END;
SetPlayRecVol(volume);
END SetVolume;
PROCEDURE GetVolume*() : LONGINT;
BEGIN
ASSERT(~closed);
RETURN SELF.volume;
END GetVolume;
PROCEDURE GetPosition*() : LONGINT;
BEGIN
ASSERT(~closed);
RETURN samplePosition;
END GetPosition;
PROCEDURE Start*;
BEGIN {EXCLUSIVE}
ASSERT(~closed);
IF ~running THEN
Activate;
running := TRUE;
END;
END Start;
PROCEDURE Pause*;
BEGIN {EXCLUSIVE}
ASSERT(~closed);
PauseChannel;
END Pause;
PROCEDURE Stop*;
BEGIN {EXCLUSIVE}
ASSERT(~closed);
StopChannel;
END Stop;
PROCEDURE PauseChannel;
BEGIN
IF running THEN
Deactivate;
running := FALSE;
END;
END PauseChannel;
PROCEDURE StopChannel;
BEGIN
PauseChannel;
WHILE buffFirst # NIL DO
ReturnCurrentBuffer;
END;
samplePosition := 0;
END StopChannel;
END Channel;
PlayerChannel* = OBJECT(Channel)
VAR
playSlotPair: LONGINT;
playSlot: ARRAY 2 OF POINTER TO PlaySlotCtrlData;
playSlotPhys: ARRAY 2 OF LONGINT;
nofSubCh: LONGINT;
silentData: CHAR;
PROCEDURE &ConstrPlay*(drv: Driver; sRate, sRes, nofSubCh, playslotpair: LONGINT);
VAR
i, j: LONGINT;
pgDelta, lpfK, lpfQ: LONGINT;
BEGIN
Constr(drv);
SELF.bytesPerSampleExp := nofSubCh - sRes;
SELF.nofSubCh := nofSubCh;
SELF.playSlotPair := playslotpair;
INCL(drv.playSlotPairsUsed, playSlotPair);
drv.PlayerChannelListAdd(SELF);
silentData := CHR(SYSTEM.LSH(sRes, 7));
playRecBuffSize := sRate * nofSubCh * (2-sRes) * BufferSizeMS DIV 1000;
NEW(playRecBuff, playRecBuffSize);
playRecBuffPhys := GetPhysicalAdr(SYSTEM.ADR(playRecBuff^), playRecBuffSize);
playRecBuffPos := 0;
pgDelta := SYSTEM.LSH(SYSTEM.LSH(sRate, 15) DIV 48000, 13);
i := 0;
WHILE sRate > NativeFreqTab[i].hz DO
INC(i);
END;
lpfK := NativeFreqTab[i].valLpfK;
lpfQ := NativeFreqTab[i].valLpfQ;
FOR i := 0 TO nofSubCh - 1 DO
NEW(playSlot[i]);
playSlotPhys[i] := GetPhysicalAdr(SYSTEM.ADR(playSlot[i]^), SYSTEM.SIZEOF(PlaySlotCtrlData));
FOR j := 0 TO NofBanks - 1 DO
playSlot[i][j].format := i + SYSTEM.LSH(nofSubCh-1, 16) + SYSTEM.LSH(sRes, 31);
playSlot[i][j].loopDefault := 0;
playSlot[i][j].pgBase := playRecBuffPhys;
playSlot[i][j].pgLoop := 0;
playSlot[i][j].pgLoopEnd := SYSTEM.LSH(playRecBuffSize, -bytesPerSampleExp);
playSlot[i][j].pgLoopFrac := 0;
playSlot[i][j].pgDeltaEnd := pgDelta;
playSlot[i][j].lpfKEnd := lpfK;
playSlot[i][j].egGainEnd := 0;
playSlot[i][j].lchGainEnd := 40000000H;
playSlot[i][j].rchGainEnd := 40000000H;
playSlot[i][j].effect1GainEnd := 0;
playSlot[i][j].effect2GainEnd := 0;
playSlot[i][j].effect3GainEnd := 0;
playSlot[i][j].lpfQ := lpfQ;
playSlot[i][j].status := 0;
playSlot[i][j].numOfFrames := 0;
playSlot[i][j].loopCount := 0;
playSlot[i][j].pgStart := 0;
playSlot[i][j].pgStartFrac := 0;
playSlot[i][j].pgDelta := pgDelta;
playSlot[i][j].lpfK := lpfK;
playSlot[i][j].egGain := 0;
playSlot[i][j].lchGain := 40000000H;
playSlot[i][j].rchGain := 40000000H;
playSlot[i][j].effect1Gain := 0;
playSlot[i][j].effect2Gain := 0;
playSlot[i][j].effect3Gain := 0;
playSlot[i][j].lpfD1 := 0;
playSlot[i][j].lpfD2 := 0;
END;
END;
SetVolume(255);
END ConstrPlay;
PROCEDURE PrepareData;
VAR
curBuffPos: LONGINT;
copySize: LONGINT;
BEGIN
IF ~running THEN RETURN END;
curBuffPos := SYSTEM.LSH(playSlot[0][drv.inactiveBank].pgStart, bytesPerSampleExp);
WHILE (buffFirst # NIL) & (playRecBuffPos # curBuffPos) DO
copySize := curBuffPos - playRecBuffPos;
IF copySize < 0 THEN
copySize := playRecBuffSize - playRecBuffPos;
END;
copySize := Strings.Min(copySize, buffFirst.buff.len - buffFirstPos);
SYSTEM.MOVE(
SYSTEM.ADR(buffFirst.buff.data^) + buffFirstPos,
SYSTEM.ADR(playRecBuff^) + playRecBuffPos,
copySize);
INC(playRecBuffPos, copySize);
INC(buffFirstPos, copySize);
INC(samplePosition, SYSTEM.LSH(copySize, -bytesPerSampleExp));
IF playRecBuffPos = playRecBuffSize THEN
playRecBuffPos := 0;
END;
IF buffFirstPos = buffFirst.buff.len THEN
ReturnCurrentBuffer;
END;
END;
WHILE playRecBuffPos # curBuffPos DO
playRecBuff[playRecBuffPos] := silentData;
playRecBuffPos := (playRecBuffPos + 1) MOD playRecBuffSize;
END;
END PrepareData;
PROCEDURE Activate;
VAR copySize, i, j: LONGINT;
BEGIN
IF Logging THEN
KernelLog.String("YMF754 - Starting PlayerChannel number ");
KernelLog.Int(playSlotPair, 0);
KernelLog.Ln;
END;
playRecBuffPos := 0;
WHILE (buffFirst # NIL) & (playRecBuffPos < playRecBuffSize) DO
copySize := Strings.Min(buffFirst.buff.len, playRecBuffSize - playRecBuffPos);
SYSTEM.MOVE(
SYSTEM.ADR(buffFirst.buff.data^),
SYSTEM.ADR(playRecBuff^) + playRecBuffPos,
copySize);
INC(playRecBuffPos, copySize);
INC(buffFirstPos, copySize);
INC(samplePosition, SYSTEM.LSH(copySize, -bytesPerSampleExp));
IF buffFirstPos = buffFirst.buff.len THEN
ReturnCurrentBuffer;
END;
END;
FOR i := playRecBuffPos TO playRecBuffSize - 1 DO
playRecBuff[i] := silentData;
END;
FOR i := 0 TO nofSubCh - 1 DO
FOR j := 0 TO NofBanks - 1 DO
playSlot[i][j].pgStart := 0;
playSlot[i][j].pgStartFrac := 0;
END;
drv.PCD.playSlotBase[2*playSlotPair+i] := playSlotPhys[i];
END;
END Activate;
PROCEDURE Deactivate;
VAR i: LONGINT;
BEGIN
IF Logging THEN
KernelLog.String("YMF754 - Stopping/Pausing PlayerChannel number ");
KernelLog.Int(playSlotPair, 0);
KernelLog.Ln;
END;
FOR i := 0 TO nofSubCh - 1 DO
drv.PCD.playSlotBase[2*playSlotPair+i] := 0;
END;
END Deactivate;
PROCEDURE SetPlayRecVol(volume: LONGINT);
VAR i, j: LONGINT;
BEGIN
volume := SYSTEM.LSH(volume, 22);
FOR i := 0 TO nofSubCh - 1 DO
IF running THEN
playSlot[i][drv.inactiveBank].egGainEnd := volume;
ELSE
FOR j := 0 TO NofBanks - 1 DO
playSlot[i][j].egGainEnd := volume;
playSlot[i][j].egGain := volume;
END;
END;
END;
END SetPlayRecVol;
PROCEDURE GetChannelKind*(): LONGINT;
BEGIN
ASSERT(~closed);
RETURN SoundDevices.ChannelPlay;
END GetChannelKind;
PROCEDURE Close*;
BEGIN {EXCLUSIVE}
ASSERT(~closed);
IF Logging THEN
KernelLog.String("YMF754 - Closing PlayerChannel number ");
KernelLog.Int(playSlotPair, 0);
KernelLog.Ln;
END;
StopChannel;
bufferListenerCaller.Close;
drv.PlayerChannelListRemove(SELF);
EXCL(drv.playSlotPairsUsed, playSlotPair);
closed := TRUE;
IF Logging THEN
KernelLog.String("YMF754 - Closing done"); KernelLog.Ln;
END;
END Close;
END PlayerChannel;
RecordChannel* = OBJECT(Channel)
VAR
recSlot: POINTER TO RecSlotCtrlData;
recSlotPhys: LONGINT;
PROCEDURE &ConstrRec*(drv: Driver; sRate, sRes, nofSubCh: LONGINT);
VAR i: LONGINT;
BEGIN
Constr(drv);
bytesPerSampleExp := nofSubCh - sRes;
playRecBuffSize := NativeFreqTab[sRate].hz * nofSubCh * (2-sRes) * BufferSizeMS DIV 1000;
NEW(playRecBuff, playRecBuffSize);
playRecBuffPhys := GetPhysicalAdr(SYSTEM.ADR(playRecBuff^), playRecBuffSize);
playRecBuffPos := 0;
NEW(recSlot);
recSlotPhys := GetPhysicalAdr(SYSTEM.ADR(recSlot^), SYSTEM.SIZEOF(RecSlotCtrlData));
drv.PCC.RegWrite32(PCCRegRecCtrlBase, recSlotPhys);
drv.PCC.RegWrite16(PCCRegADCSlotSamplingRate, NativeFreqTab[sRate].valRec);
drv.PCC.RegWrite16(PCCRegADCSlotFormat, SYSTEM.LSH(nofSubCh-1, 1) + sRes);
FOR i := 0 TO NofBanks - 1 DO
recSlot.recSlotADC[i].pgBase := playRecBuffPhys;
recSlot.recSlotADC[i].pgLoopEndAdr := playRecBuffSize;
recSlot.recSlotADC[i].pgStartAdr := 0;
recSlot.recSlotADC[i].numOfLoops := 0;
END;
SetVolume(255);
END ConstrRec;
PROCEDURE PrepareData;
VAR
curBuffPos: LONGINT;
copySize: LONGINT;
BEGIN
IF ~running THEN RETURN END;
curBuffPos := recSlot.recSlotADC[drv.inactiveBank].pgStartAdr;
WHILE (buffFirst # NIL) & (playRecBuffPos # curBuffPos) DO
copySize := curBuffPos - playRecBuffPos;
IF copySize < 0 THEN
copySize := playRecBuffSize - playRecBuffPos;
END;
copySize := Strings.Min(copySize, buffFirst.buff.len - buffFirstPos);
SYSTEM.MOVE(
SYSTEM.ADR(playRecBuff^) + playRecBuffPos,
SYSTEM.ADR(buffFirst.buff.data^) + buffFirstPos,
copySize);
INC(playRecBuffPos, copySize);
INC(buffFirstPos, copySize);
INC(samplePosition, SYSTEM.LSH(copySize, -bytesPerSampleExp));
IF playRecBuffPos = playRecBuffSize THEN
playRecBuffPos := 0;
END;
IF buffFirstPos = buffFirst.buff.len THEN
ReturnCurrentBuffer;
END;
END;
END PrepareData;
PROCEDURE Activate;
VAR i: LONGINT;
BEGIN
IF Logging THEN
KernelLog.String("YMF754 - Starting RecordChannel"); KernelLog.Ln;
END;
FOR i := 0 TO NofBanks - 1 DO
recSlot.recSlotADC[i].pgStartAdr := 0;
END;
drv.PCC.RegWrite32(PCCRegMapOfRec, 2);
END Activate;
PROCEDURE Deactivate;
BEGIN
IF Logging THEN
KernelLog.String("YMF754 - Stopping/Pausing RecordChannel"); KernelLog.Ln;
END;
drv.PCC.RegWrite32(PCCRegMapOfRec, 0);
playRecBuffPos := 0;
END Deactivate;
PROCEDURE SetPlayRecVol(volume: LONGINT);
BEGIN
volume := SYSTEM.LSH(volume, 6);
drv.PCC.RegWrite32(PCCRegVolADCIn, volume + SYSTEM.LSH(volume, 16));
END SetPlayRecVol;
PROCEDURE GetChannelKind*(): LONGINT;
BEGIN
ASSERT(~closed);
RETURN SoundDevices.ChannelRecord;
END GetChannelKind;
PROCEDURE Close*;
BEGIN {EXCLUSIVE}
ASSERT(~closed);
IF Logging THEN
KernelLog.String("YMF754 - Closing RecordChannel"); KernelLog.Ln;
END;
StopChannel;
bufferListenerCaller.Close;
SetPlayRecVol(0);
drv.recSlot := NIL;
closed := TRUE;
IF Logging THEN
KernelLog.String("YMF754 - Closing done"); KernelLog.Ln;
END;
END Close;
END RecordChannel;
Driver* = OBJECT (SoundDevices.Driver)
VAR
base: SYSTEM.ADDRESS;
irq: LONGINT;
ACC: AC97Control;
PCC: PCIAudioControl;
PCD: POINTER TO PlayCtrlDataTable;
mixerChannels: ARRAY NofMixerChannels OF MixerChannel;
mixerChannelListeners: ListMixerChangedProc;
playChannels: ListPlayerChannel;
playSlotPairsUsed: SET;
recSlot: RecordChannel;
inactiveBank: LONGINT;
PROCEDURE &Constr*(name: ARRAY OF CHAR; physbase, irq: LONGINT; CntrlInst1E: BOOLEAN);
VAR
i, res: LONGINT;
PCDbase: LONGINT;
BEGIN
SELF.irq := irq;
SELF.SetName(name);
SELF.desc := PluginDescPrefix;
Strings.Append(SELF.desc, name);
Machine.MapPhysical(physbase, 8000H , base);
ASSERT(base # Machine.NilAdr);
IF Logging THEN
KernelLog.String(" Initializing driver object:"); KernelLog.Ln;
KernelLog.String(" Device name: "); KernelLog.String(name); KernelLog.Ln;
KernelLog.String(" Physical base address: "); KernelLog.Hex(physbase, 0); KernelLog.Char("h"); KernelLog.Ln;
KernelLog.String(" Mapped base address: "); KernelLog.Hex(base, 0); KernelLog.Char("h"); KernelLog.Ln;
KernelLog.String(" Mapped space: 32KB"); KernelLog.Ln;
KernelLog.String(" Hardware interrupt (IRQ): "); KernelLog.Int(irq, 0); KernelLog.Ln;
END;
NEW(PCC, base, CntrlInst1E);
NEW(ACC, PCC);
PCC.Initialize;
ACC.Reset;
playChannels := NIL;
playSlotPairsUsed := {};
recSlot := NIL;
inactiveBank := 0;
mixerChannelListeners := NIL;
NEW(mixerChannels[0], SELF, ACCRegVolMasterOut, TRUE, FALSE, "MasterOut", "Master output mixer channel");
NEW(mixerChannels[1], SELF, ACCRegRecordGain, FALSE, FALSE, "MasterIn", "Master input mixer channel");
NEW(mixerChannels[2], SELF, ACCRegVolLineIn, TRUE, TRUE, "LineIn", "LineIn mixer channel");
NEW(mixerChannels[3], SELF, ACCRegVolPCM, TRUE, FALSE, "PCM", "PCM mixer channel");
NEW(mixerChannels[4], SELF, ACCRegVolCD, TRUE, TRUE, "CD", "CD mixer channel");
NEW(mixerChannels[5], SELF, ACCRegVolMic, TRUE, TRUE, "Mic", "Microphone mixer channel");
NEW(PCD);
PCDbase := GetPhysicalAdr(SYSTEM.ADR(PCD^), SYSTEM.SIZEOF(PlayCtrlDataTable));
PCD.numOfPlay := NofPlaySlots;
FOR i := 0 TO NofPlaySlots - 1 DO
PCD.playSlotBase[i] := 0;
END;
PCC.RegWrite32(PCCRegPlayCtrlBase, PCDbase);
ASSERT((irq >= 1) & (irq <= 15));
Objects.InstallHandler(HandleInterrupt, Machine.IRQ0+irq);
SoundDevices.devices.Add(SELF, res);
ASSERT(res = Plugins.Ok);
SoundDevices.devices.GetAll(DriverTab);
IF Logging THEN
KernelLog.String(" Initializing finished."); KernelLog.Ln;
END;
END Constr;
PROCEDURE Finalize*;
VAR
item: ListPlayerChannel;
BEGIN
IF Logging THEN
KernelLog.String(" Finalizing driver object:"); KernelLog.Ln;
KernelLog.String(" Device name: "); KernelLog.String(name); KernelLog.Ln;
KernelLog.String(" Mapped base address: "); KernelLog.Hex(base, 0); KernelLog.Char("h"); KernelLog.Ln;
KernelLog.String(" Hardware interrupt (IRQ): "); KernelLog.Int(irq, 0); KernelLog.Ln;
END;
item := playChannels;
WHILE item # NIL DO
item.channel.Close;
item := item.next;
END;
IF recSlot # NIL THEN
recSlot.Close;
END;
ACC.Reset;
PCC.UnInitialize;
Machine.UnmapPhysical(base, 8000H );
SoundDevices.devices.Remove(SELF);
SoundDevices.devices.GetAll(DriverTab);
Objects.RemoveHandler(HandleInterrupt, Machine.IRQ0+irq);
IF Logging THEN
KernelLog.String(" Finalizing finished."); KernelLog.Ln;
END;
END Finalize;
PROCEDURE HandleInterrupt;
VAR
item: ListPlayerChannel;
regMode: SET;
BEGIN
IF ~(31 IN SYSTEM.VAL(SET, PCC.RegRead32(PCCRegStatus))) THEN RETURN END;
PCC.RegWrite32(PCCRegStatus, SYSTEM.VAL(LONGINT, {31}));
inactiveBank := PCC.RegRead32(PCCRegControlSelect) MOD 2;
item := playChannels;
WHILE item # NIL DO
item.channel.PrepareData;
item := item.next;
END;
IF recSlot # NIL THEN
recSlot.PrepareData;
END;
IF (playChannels = NIL) & (recSlot = NIL) THEN
PCC.RegWrite32(PCCRegMode, 0);
ELSE
regMode := SYSTEM.VAL(SET, PCC.RegRead32(PCCRegMode));
INCL(regMode, 1);
PCC.RegWrite32(PCCRegMode, SYSTEM.VAL(LONGINT, regMode));
END;
END HandleInterrupt;
PROCEDURE Init*;
END Init;
PROCEDURE Enable*;
END Enable;
PROCEDURE Disable*;
END Disable;
PROCEDURE NofNativeFrequencies*():LONGINT;
BEGIN
RETURN NofNativeFreq;
END NofNativeFrequencies;
PROCEDURE GetNativeFrequeny*(nr : LONGINT):LONGINT;
BEGIN
RETURN NativeFreqTab[nr].hz;
END GetNativeFrequeny;
PROCEDURE NofSamplingResolutions*():LONGINT;
BEGIN
RETURN 2;
END NofSamplingResolutions;
PROCEDURE GetSamplingResolution*(nr : LONGINT):LONGINT;
BEGIN
ASSERT((nr >= 0) & (nr < 2));
IF nr = 0 THEN
RETURN 16;
ELSE
RETURN 8;
END;
END GetSamplingResolution;
PROCEDURE NofSubChannelSettings*():LONGINT;
BEGIN
RETURN 2;
END NofSubChannelSettings;
PROCEDURE GetSubChannelSetting*(nr : LONGINT):LONGINT;
BEGIN
ASSERT((nr >= 0) & (nr < 2));
IF nr = 0 THEN
RETURN 1;
ELSE
RETURN 2;
END;
END GetSubChannelSetting;
PROCEDURE NofWaveFormats*():LONGINT;
BEGIN
RETURN 1;
END NofWaveFormats;
PROCEDURE GetWaveFormat*(nr : LONGINT):LONGINT;
BEGIN
ASSERT(nr = 0);
RETURN SoundDevices.FormatPCM;
END GetWaveFormat;
PROCEDURE OpenPlayChannel*(VAR channel : SoundDevices.Channel; samplingRate, samplingResolution, nofSubChannels, format : LONGINT; VAR res : LONGINT);
VAR
playSlotPair: LONGINT;
playChannel: PlayerChannel;
BEGIN {EXCLUSIVE}
IF Logging THEN
KernelLog.String("YMF754 - Opening PlayerChannel");
END;
playSlotPair := 0;
WHILE (playSlotPair < NofPlaySlotPairs) & (playSlotPair IN playSlotPairsUsed) DO
INC(playSlotPair);
END;
IF playSlotPair = NofPlaySlotPairs THEN
IF Logging THEN
KernelLog.String(" - rejected: no more channels available"); KernelLog.Ln;
END;
res := SoundDevices.ResNoMoreChannels;
channel := NIL;
RETURN;
END;
CheckChannelParam(FALSE, samplingRate, samplingResolution, nofSubChannels, format, res);
IF res = SoundDevices.ResOK THEN
NEW(playChannel, SELF, samplingRate, samplingResolution, nofSubChannels, playSlotPair);
IF Logging THEN
KernelLog.String(" number ");
KernelLog.Int(playSlotPair, 0);
KernelLog.String(" - done");
KernelLog.Ln;
END;
channel := playChannel;
StartPCIAudio;
ELSE
IF Logging THEN
KernelLog.String(" - rejected: invalid parameters"); KernelLog.Ln;
END;
channel := NIL;
END;
END OpenPlayChannel;
PROCEDURE OpenRecordChannel*(VAR channel : SoundDevices.Channel; samplingRate, samplingResolution, nofSubChannels, format : LONGINT; VAR res : LONGINT);
BEGIN {EXCLUSIVE}
IF Logging THEN
KernelLog.String("YMF754 - Opening RecordChannel");
END;
IF recSlot # NIL THEN
IF Logging THEN
KernelLog.String(" - rejected: RecordChannel currently in use"); KernelLog.Ln;
END;
res := SoundDevices.ResNoMoreChannels;
channel := NIL;
RETURN;
END;
CheckChannelParam(TRUE, samplingRate, samplingResolution, nofSubChannels, format, res);
IF res = SoundDevices.ResOK THEN
NEW(recSlot, SELF, samplingRate, samplingResolution, nofSubChannels);
IF Logging THEN
KernelLog.String(" - done"); KernelLog.Ln;
END;
channel := recSlot;
StartPCIAudio;
ELSE
IF Logging THEN
KernelLog.String(" - rejected: invalid parameters"); KernelLog.Ln;
END;
channel := NIL;
END;
END OpenRecordChannel;
PROCEDURE StartPCIAudio;
BEGIN
IF ~ODD(PCC.RegRead32(PCCRegMode)) THEN
PCC.RegWrite32(PCCRegMode, 3);
END;
END StartPCIAudio;
PROCEDURE CheckChannelParam(onlyNativeFreq: BOOLEAN; VAR sRate, sResolution, nofSubCh, format, res: LONGINT);
VAR i: LONGINT;
BEGIN
res := SoundDevices.ResOK;
IF onlyNativeFreq THEN
i := 0;
WHILE (i < NofNativeFreq) & (NativeFreqTab[i].hz # sRate) DO INC(i) END;
IF i < NofNativeFreq THEN
sRate := i;
ELSE
res := SoundDevices.ResUnsupportedFrequency;
END;
ELSE
IF (sRate < 8000) OR (sRate > 48000) THEN
res := SoundDevices.ResUnsupportedFrequency;
END;
END;
IF sResolution = 16 THEN
sResolution := 0;
ELSIF sResolution = 8 THEN
sResolution := 1;
ELSE
res := SoundDevices.ResUnsupportedSamplingRes;
END;
IF (nofSubCh # 1) & (nofSubCh # 2) THEN
res := SoundDevices.ResUnsupportedSubChannels;
END;
IF format # SoundDevices.FormatPCM THEN
res := SoundDevices.ResUnsupportedFormat;
END;
END CheckChannelParam;
PROCEDURE PlayerChannelListAdd(channel: PlayerChannel);
VAR item: ListPlayerChannel;
BEGIN
ASSERT(channel # NIL);
NEW(item);
item.channel := channel;
item.next := playChannels;
playChannels := item;
END PlayerChannelListAdd;
PROCEDURE PlayerChannelListRemove(channel: PlayerChannel);
VAR item: ListPlayerChannel;
BEGIN {EXCLUSIVE}
item := playChannels;
IF item = NIL THEN
RETURN;
END;
IF item.channel = channel THEN
playChannels := item.next;
RETURN;
END;
WHILE (item.next # NIL) & (item.next.channel # channel) DO
item := item.next;
END;
IF item.next # NIL THEN
item.next := item.next.next;
END;
END PlayerChannelListRemove;
PROCEDURE RegisterMixerChangeListener*(mixChangedProc : MixerChangedProc);
VAR item: ListMixerChangedProc;
BEGIN {EXCLUSIVE}
ASSERT(mixChangedProc # NIL);
NEW(item);
item.proc := mixChangedProc;
item.next := mixerChannelListeners;
mixerChannelListeners := item;
IF Logging THEN
KernelLog.String("YMF754 - MixerChangeListener registered"); KernelLog.Ln;
END;
END RegisterMixerChangeListener;
PROCEDURE UnregisterMixerChangeListener*(mixChangedProc : MixerChangedProc);
VAR item: ListMixerChangedProc;
BEGIN {EXCLUSIVE}
IF Logging THEN
KernelLog.String("YMF754 - Unregistering MixerChangeListener");
END;
item := mixerChannelListeners;
IF item = NIL THEN
IF Logging THEN
KernelLog.String(" - failed: no listener was registered"); KernelLog.Ln;
END;
RETURN;
END;
IF item.proc = mixChangedProc THEN
mixerChannelListeners := item.next;
IF Logging THEN
KernelLog.String(" - done"); KernelLog.Ln;
END;
RETURN;
END;
WHILE (item.next # NIL) & (item.next.proc # mixChangedProc) DO
item := item.next;
END;
IF item.next # NIL THEN
item.next := item.next.next;
IF Logging THEN
KernelLog.String(" - done"); KernelLog.Ln;
END;
ELSE
IF Logging THEN
KernelLog.String(" - failed: listener was not registered"); KernelLog.Ln;
END;
END;
END UnregisterMixerChangeListener;
PROCEDURE GetMixerChannel*(channelNr : LONGINT; VAR channel : SoundDevices.MixerChannel);
VAR name: NameStr;
BEGIN
IF (channelNr >= 0) & (channelNr < NofMixerChannels) THEN
channel := mixerChannels[channelNr];
IF Logging THEN
channel.GetName(name);
KernelLog.String("YMF754 - GetMixerChannel (");
KernelLog.String(name);
KernelLog.String(")");
KernelLog.Ln;
END;
ELSE
channel := NIL;
END;
END GetMixerChannel;
PROCEDURE GetNofMixerChannels*() : LONGINT;
BEGIN
RETURN NofMixerChannels;
END GetNofMixerChannels;
END Driver;
VAR
NativeFreqTab: NativeFreqTable;
DriverTab: Plugins.Table;
PROCEDURE FillNativeFreqTable;
BEGIN
NativeFreqTab[0].hz := 8000;
NativeFreqTab[0].valRec := 24575;
NativeFreqTab[0].valLpfK := 18B00000H;
NativeFreqTab[0].valLpfQ := 32020000H;
NativeFreqTab[1].hz := 11025;
NativeFreqTab[1].valRec := 17832;
NativeFreqTab[1].valLpfK := 20900000H;
NativeFreqTab[1].valLpfQ := 31780000H;
NativeFreqTab[2].hz := 16000;
NativeFreqTab[2].valRec := 12287;
NativeFreqTab[2].valLpfK := 2B980000H;
NativeFreqTab[2].valLpfQ := 31380000H;
NativeFreqTab[3].hz := 22050;
NativeFreqTab[3].valRec := 8915;
NativeFreqTab[3].valLpfK := 35A00000H;
NativeFreqTab[3].valLpfQ := 31C80000H;
NativeFreqTab[4].hz := 32000;
NativeFreqTab[4].valRec := 6143;
NativeFreqTab[4].valLpfK := 40000000H;
NativeFreqTab[4].valLpfQ := 33D00000H;
NativeFreqTab[5].hz := 44100;
NativeFreqTab[5].valRec := 4457;
NativeFreqTab[5].valLpfK := 40000000H;
NativeFreqTab[5].valLpfQ := 40000000H;
NativeFreqTab[6].hz := 48000;
NativeFreqTab[6].valRec := 4095;
NativeFreqTab[6].valLpfK := 40000000H;
NativeFreqTab[6].valLpfQ := 40000000H;
END FillNativeFreqTable;
PROCEDURE GetPhysicalAdr(adr: SYSTEM.ADDRESS; size: SYSTEM.SIZE): Machine.Address32;
VAR physadr: Machine.Address32;
BEGIN
physadr := Machine.Ensure32BitAddress (Machine.PhysicalAdr(adr, size));
ASSERT(physadr # Machine.NilAdr);
ASSERT(physadr MOD 4 = 0);
RETURN physadr;
END GetPhysicalAdr;
PROCEDURE ScanPCI(vendor, device: LONGINT; name: Plugins.Name; CntrlInst1E: BOOLEAN);
VAR
len, res, reg, index: LONGINT;
bus, dev, fct: LONGINT;
base, irq: LONGINT;
d: Driver;
BEGIN
index := 0;
WHILE (index < 10) & (PCI.FindPCIDevice(device, vendor, index, bus, dev, fct) = PCI.Done) DO
res := PCI.ReadConfigDword(bus, dev, fct, PCI.Adr0Reg, base); ASSERT(res = PCI.Done);
ASSERT(~ODD(base));
DEC(base, base MOD 16);
res := PCI.ReadConfigByte(bus, dev, fct, PCI.IntlReg, irq); ASSERT(res = PCI.Done);
res := PCI.ReadConfigByte(bus, dev, fct, PCIRegDS1EControl, reg); ASSERT(res = PCI.Done);
IF ODD(reg) THEN
DEC(reg);
res := PCI.WriteConfigByte(bus, dev, fct, PCIRegDS1EControl, reg); ASSERT(res = PCI.Done);
END;
res := PCI.WriteConfigByte(bus, dev, fct, PCIRegDS1EControl, reg + 1); ASSERT(res = PCI.Done);
res := PCI.WriteConfigByte(bus, dev, fct, PCIRegDS1EControl, reg); ASSERT(res = PCI.Done);
len := Strings.Length(name);
name[len] := "#";
INC(len);
name[len] := CHR(ORD("0") + index);
INC(len);
name[len] := 0X;
NEW(d, name, base, irq, CntrlInst1E);
INC(index)
END
END ScanPCI;
PROCEDURE Init;
BEGIN
FillNativeFreqTable;
DriverTab := NIL;
IF Logging THEN
KernelLog.String("Scanning for devices..."); KernelLog.Ln;
END;
ScanPCI(1073H, 0004H, "YMF724", FALSE);
ScanPCI(1073H, 000AH, "YMF740", FALSE);
ScanPCI(1073H, 000CH, "YMF740C", TRUE);
ScanPCI(1073H, 000DH, "YMF724F", TRUE);
ScanPCI(1073H, 0010H, "YMF744", TRUE);
ScanPCI(1073H, 0012H, "YMF754", TRUE);
IF Logging THEN
KernelLog.String("Scanning finished."); KernelLog.Ln;
END;
END Init;
PROCEDURE Install*;
END Install;
PROCEDURE Close*;
VAR
i: LONGINT;
BEGIN
IF Logging THEN
KernelLog.String("Unloading driver module..."); KernelLog.Ln;
END;
IF DriverTab # NIL THEN
FOR i := 0 TO LEN(DriverTab^) - 1 DO
IF DriverTab[i] IS Driver THEN
DriverTab[i](Driver).Finalize;
END;
END;
END;
IF Logging THEN
KernelLog.String("Unloading finished."); KernelLog.Ln;
END;
END Close;
BEGIN
ASSERT(BufferSizeMS <= 10000);
Modules.InstallTermHandler(Close);
Init;
END YMF754.
Aos.Call YMF754.Install ~
System.Free YMF754 ~
Installation
add YMF754.Install to Configuration.XML, section 'Autostart' to load driver at system startup.