{
PASDVT.TPU - TP(6.0) unit for interfacing to DEMOVT.EXE // ARM 12/93,4/94
(based on original VTASM.INC by JCAB)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Note: VTASM.INC nomenclature sounded a bit clumsy to me, so I decided
not to follow it too closely O:-)
The equivalence between VTASM.INC procedures and PASDVT.TPU ones is
as follows:
PASDVT.TPU VTASM.INC
========== =========
VT_Init InitMusic
VT_Poll CallMusic
VT_AutoOff VTDisconnectTimer
VT_AutoOn VTConnectTimer
VT_Timer VTGetTickCounter
VT_Start VTBeginSync
VT_SyncStart VTBeginSync + VTWaitForStart
VT_GoTo VTJumpPos
VT_GetSem VTCheckSemaphore (*)
VT_SetSem VTSetSemaphore
VT_Resync VTMiddleSync
VT_SetVolume VTSetSoundVolume
VT_GetVolume VTGetSoundVolume
VT_Delay (no equivalent)
(*) vtasm.inc's VTCheckSemaphore compares semaphore bx with value al,
while pasdvt.tpu's VT_GetSem simply returns the value of the semaphore
and leaves any comparison up to you.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
4/94 -- New functions:
VT_QueryCh -- returns TRUE if note played since last query.
VT_ChStatus -- returns channel's tone, instrument, and volume.
VT_ChannelCount -- returns number of channels
VT_CurrentPos -- returns current pattern/note
VT_Abort -- tells DVT to quit on exit
}
UNIT PASDVT;
Interface
{ ==============================================================
T H E P R O C ' S }
function VT_Init : boolean; { detect and initialize DEMOVT }
procedure VT_Poll; { ò 50Hz DEMOVT manual polling }
procedure VT_AutoOff; { switch to manual polling }
procedure VT_AutoOn; { switch to auto (IRQ0) polling }
function VT_Timer : longint; { get music timer count (50Hz) }
procedure VT_Start; { setup to start playing }
procedure VT_SyncStart; { Like start, but then waits 1/2 sec till music sounds }
procedure VT_GoTo( pattern, note : byte ); { jump to given pattern/note within score }
function VT_GetSem( sem : byte ) : byte; { get semaphore "sem" value }
procedure VT_SetSem( sem, va : byte ); { set semaphore "sem" to "va" }
procedure VT_Resync( sem, pattern, note : byte ); { wait for sync point }
procedure VT_SetVolume( level : byte ); { set volume level }
function VT_GetVolume : byte; { get volume level }
procedure VT_Delay( h : word ); { delay h hundredths of a second }
{ while still updating music }
function VT_QueryCh( ChanNo : byte ) : boolean; { TRUE if new note }
procedure VT_ChStatus( ChanNo : byte; var per : word; var inst, volu : byte);
function VT_ChannelCount : byte; { get # of channels }
procedure VT_CurrentPos( var pat, note : byte ); { current pattern/note }
procedure VT_Abort;
{ ==============================================================
...AND THINGS FOR ALL YOU SHOW-OFF DO-IT-YOURSELF TYPES ;->
}
Type
TChanData =
RECORD
Period : WORD;
Ins : BYTE;
Vol : BYTE;
END;
TChansData = ARRAY[1..32] OF TChanData;
TChansTrig = ARRAY[1..32] OF BOOLEAN;
TYPE
TVTRunInfo =
RECORD
{ Out }
Semaphores : ARRAY[0..255] OF BYTE;
ChansTrig : TChansTrig;
NumChannels : BYTE;
CtrlEntryPoint : POINTER;
TickCounter : LONGINT;
RegEntryPoint : POINTER;
ChansData : TChansData;
Pos : BYTE;
Seq : BYTE;
fill2 : ARRAY[1..81] OF BYTE;
{ In }
fill3 : ARRAY[1..3] OF BYTE;
JumpNewPos : BOOLEAN;
JumpPosSeq : BYTE;
JumpPosNote : BYTE;
Volume : BYTE;
Abort : BOOLEAN;
fill4 : ARRAY[1..248] OF BYTE;
END;
VT_PInfo = ^TVTRunInfo;
VT_RInfo = TVTRunInfo;
VTIdString = ARRAY [0..255] OF BYTE;
var
VT_Info : VT_PInfo; { points to VT_RInfo record within DEMOVT }
AppIDFound : ^VTIdString; { dunno, ask JCAB... ;-) }
Implementation
const
VTOK : boolean = False; { = True if DEMOVT installed and initialized }
var
VTControl : procedure ( command : word );
{ VTInfo^.VTCtrlEntry for quick access }
{ (hope DEMOVT never changes it!) }
procedure CLI; inline( $fa );
procedure STI; inline( $fb );
{ // VT_init }
function VT_Init : boolean; assembler;
Const
MagicAX = $5654; {'VT'}
MagicBX = $5472; {'Tr'}
MagicCX = $6163; {'ac'}
MagicXorBX = $6B65; {'ke'}
MagicXorCX = $7220; {'r '}
asm
mov ax, MagicAX
mov bx, MagicBX
mov cx, MagicCX
xor di,di
mov es, di
int 2fh
xor dl,dl
and ax,ax
jnz @no
cmp bx, MagicBX xor MagicXorBX
jne @no
cmp cx, MagicCX xor MagicXorCX
jne @no
inc dl { DEMOVT detected! }
mov [word ptr AppIdFound+2], es
mov [word ptr AppIdFound ], di { save this... but for what ? }
les di, [es:di-4]
mov [word ptr VT_Info+2], es
mov [word ptr VT_Info], di
les di, [es:di+256+33] { read VTCtrlEntry vector }
mov [word ptr VTControl+2], es
mov [word ptr VTControl], di { ...and copy it to VTControl }
@no:
xor ah, ah
mov al, dl
mov [VTOK], al
end;
{ // VT_Poll }
procedure VT_Poll;
begin
if VTOK then VTControl( 2 );
end;
{ // VT_AutoOff }
procedure VT_AutoOff;
begin
if VTOK then VTControl( 1 );
end;
{ // VT_AutoOn }
procedure VT_AutoOn;
begin
if VTOK then VTControl( 0 );
end;
{ // VT_Timer }
function VT_Timer : longint;
begin
if VTOK then begin
CLI;
VT_Timer := VT_Info^.TickCounter;
STI;
end else
VT_Timer := 0;
end;
{ // VT_Start }
procedure VT_Start;
begin
if VTOK then VTControl( 3 );
end;
{ // VT_SyncStart }
procedure VT_SyncStart;
begin
if VTOK then begin
VTControl( 3 );
CLI;
VT_Info^.TickCounter := 0;
STI;
repeat VT_Poll until VT_Timer >= 25; { 25/50ths = 1/2 second }
VT_Info^.TickCounter := 0;
end;
end;
{ // VT_GoTo }
procedure VT_GoTo( pattern, note : byte );
begin
if VTOK then with VT_Info^ do begin
JumpNewPos := TRUE;
JumpPosSeq := pattern;
JumpPosNote := note;
end;
end;
{ // VT_GetSem }
function VT_GetSem( sem : byte ) : byte;
begin
if VTOK then
VT_GetSem := VT_Info^.Semaphores[ sem ]
else
VT_GetSem := 0;
end;
{ // VT_SetSem }
procedure VT_SetSem( sem, va : byte );
begin
if VTOK then VT_Info^.Semaphores[ sem ] := va;
end;
{ // VT_Sync }
procedure VT_Resync( sem, pattern, note : byte );
begin
if VTOK then begin
if VT_GetSem( sem ) = 0 then VT_Goto( pattern, note );
inc( sem );
repeat VT_Poll until VT_GetSem( sem ) <> 0;
end;
end;
{ // VT_SetVolume }
procedure VT_SetVolume( level : byte );
begin
if VTOK then VT_Info^.Volume := level;
end;
{ // VT_GetVolume }
function VT_GetVolume : byte;
begin
if VTOK then
VT_GetVolume := VT_Info^.Volume
else
VT_GetVolume := 0;
end;
{ // VT_Delay }
procedure VT_Delay( h : word );
var l : longint;
begin
if VTOK then begin
l := VT_Timer + h shr 1;
repeat VT_Poll until VT_Timer >= l;
end;
end;
{ // VT_QueryCh }
function VT_QueryCh( ChanNo : byte ) : boolean;
begin
VT_QueryCh := false;
if VTOK then
if VT_Info^.ChansTrig[ ChanNo ] then begin
VT_QueryCh := true;
VT_Info^.ChansTrig[ ChanNo ] := false;
end;
end;
{ // VT_ChStatus }
procedure VT_ChStatus( ChanNo : byte; var per : word; var inst, volu : byte);
begin
if VTOK then
with VT_Info^.ChansData[ ChanNo ] do begin
per := period;
inst := ins;
volu := vol;
end
else begin
per := 0;
inst := 0;
volu := 0;
end;
end;
{ // VT_Channels }
function VT_ChannelCount : byte;
begin
if VTOK then
VT_ChannelCount := VT_Info^.NumChannels
else
VT_ChannelCount := 0;
end;
{ // VT_CurrentPos }
procedure VT_CurrentPos( var pat, note : byte );
begin
if VTOK then
with VT_Info^ do begin
pat := seq;
note := pos;
end
else begin
pat := 0;
note := 0;
end;
end;
{ // VT_Abort }
procedure VT_Abort;
begin
if VTOK then VT_Info^.Abort := true;
end;
END.