REM File: Dndsic5 for Dndbbs Version v5.0a Release r2.0 Tweak t6.0a

' get include files
REM $INCLUDE: 'DNDSIC.INC'

REM $DYNAMIC
DEFINT A-Z

' playwav constants
CONST PlayBuffer = 32767
DIM SHARED SpeakerVolume AS INTEGER

' playwav variables
COMMON SHARED BasePort%
COMMON SHARED Channel%
COMMON SHARED WavBuffer() AS STRING * PlayBuffer

REM file: FAT32.BI - FAT32 structure

TYPE FAT32Type
 Size1 AS STRING * 2
 Version AS STRING * 2
 SectorsPerCluster AS STRING * 4
 BytesPerSector AS STRING * 4
 FreeClusters AS STRING * 4
 TotalClusters AS STRING * 4
 FreeSectors AS STRING * 4
 TotalSectors AS STRING * 4
 FreeUnits AS STRING * 4
 TotalUnits AS STRING * 4
 Reserved AS STRING * 8
END TYPE

DECLARE SUB FreeSpace (T%, T$)

REM Plays .wav files for blind or sight impaired people.

REM Dmaplay.bas code was adapted from Dma.zip written by Mike Huff (1996).
REM Which was modified by Martin Rampersad.
REM And was rewritten into Playwav.bas,
REM Program downloaded from http://www.ocf.berkeley.edu/~horie

SUB Playwav(Tmp1,Tmp1$)
 On Local Error Goto Play.Error

 ' select .wav file
 If Tmp1>0 Then
 Select Case Tmp1
 Case 1
    Tmp1$="Laser.wav"
 Case 2
    Tmp1$="Ringin.wav"
 Case 3
    Tmp1$="Carrier.wav"
 Case 4
    Tmp1$="Boing.wav"
 Case 5
    Tmp1$="Bang.wav"
 Case 6
    Tmp1$="Connect.wav"
 Case 7
    Tmp1$="Complete.wav"
 Case 8
    Tmp1$="Gunshot.wav"
 Case 9
    Tmp1$="Ricochet.wav"
 Case 10 ' a
    Tmp1$="Whoosh.wav"
 Case 11 ' b
    Tmp1$="Glass.wav"
 Case 12 ' c
    Tmp1$="Cashreg.wav"
 Case Else
    Error 92
    Exit Sub
 End Select
 Endif

 ' locate file
 IF Tmp1$ = Nul THEN
    EXIT SUB
 END IF
 Tmp2$ = Tmp1$
 IF DIR$(Tmp2$) = Nul THEN
    EXIT SUB
 END IF

 ' Parse BLASTER environment variable
 Call GetBLASTER
 If BasePort% = 0% Then
    Error 92
    Exit Sub
 Endif

 ' reopen input file
 X = FREEFILE
 IF X = 0 THEN
    EXIT SUB
 END IF
 OPEN Tmp2$ FOR BINARY AS #X

 ' create buffer for file.
 REDIM WavBuffer(1 TO 1) AS STRING * PlayBuffer

 ' turn on speaker and adjust volume.
 Call AdjustVolume(SpeakerVolume)

 ' get buffer from file skipping header.
 GET #X, 44, WavBuffer(1)

 ' store memory address of sound buffer.
 Segment& = VARSEG(WavBuffer(1))
 Offset& = VARPTR(WavBuffer(1))

 ' store length of sound file minus header.
 Length& = LOF(X) - 44

 ' Adjust length if needed to 32k
 IF Length& > 32767 THEN Length& = 32767

 ' call the sound buffer player.
 CALL DMAPlay(Segment&, Offset&, Length&)

 Close #X
 Erase WavBuffer
 EXIT SUB
Play.Resume:
 Exit Sub
Play.Error:
 Resume Play.Resume
END SUB

' plays the contents of the buffer.
SUB DMAPlay(Segment&, Offset&, Length&)
 Freq2& = 10000
 Length& = Length& - 1
 Page% = 0
 MemLoc& = Segment& * 16 + Offset&
 SELECT CASE Channel%
 CASE 0
    PgPort% = &H87
    AddPort% = &H0
    LenPort% = &H1
    ModeReg% = &H48
 CASE 1
    PgPort% = &H83
    AddPort% = &H2
    LenPort% = &H3
    ModeReg% = &H49
 CASE 2
    PgPort% = &H81
    AddPort% = &H4
    LenPort% = &H5
    ModeReg% = &H4A
 CASE 3
    PgPort% = &H82
    AddPort% = &H6
    LenPort% = &H7
    ModeReg% = &H4B
 CASE ELSE
    EXIT SUB
 END SELECT
 OUT &HA, &H4 + Channel%
 OUT &HC, &H0
 OUT &HB, ModeReg%
 OUT AddPort%, MemLoc& AND &HFF
 OUT AddPort%,(MemLoc& AND &HFFFF&) \ &H100
 IF MemLoc& AND 65536 THEN
    Page% = Page% + 1
 END IF
 IF MemLoc& AND 131072 THEN
    Page% = Page% + 2
 END IF
 IF MemLoc& AND 262144 THEN
    Page% = Page% + 4
 END IF
 IF MemLoc& AND 524288 THEN
    Page% = Page% + 8
 END IF
 OUT PgPort%, Page%
 OUT LenPort%, Length& AND &HFF
 OUT LenPort%, (Length& AND &HFFFF&) \ &H100
 OUT &HA, Channel%

 TimeConst% = 256 - 1000000 \ Freq2&
 V% = &H40
 Call WriteDSP(V%)

 V% = TimeConst%
 Call WriteDSP(V%)

 V% = &H14
 Call WriteDSP(V%)

 V% = Cint(Length& AND &HFF)
 Call WriteDSP(V%)

 V% = Cint((Length& AND &HFFFF&) \ &H100)
 Call WriteDSP(V%)
END SUB

' parse the BLASTER environment string and return settings.
SUB GetBlaster
 REM SET BLASTER=A220 I5 D1 H5 P330 T3 V9
 Var$ = Environ$("BLASTER")
 IF Var$ = Nul THEN ' default values
    BasePort% = &H220
    Channel% = &H1
    SpeakerVolume = 13
    EXIT SUB
 END IF
 Var% = INSTR(Var$, "A") ' base IO port
 If Var% Then ' A220
    BasePort% = VAL("&H" + MID$(Var$, Var% + 1, 3))
 Endif
 Var% = INSTR(Var$, "D") ' 8-bit DMA channel
 If Var% Then ' D1
    Channel% = VAL(MID$(Var$, Var% + 1, 1))
 Endif
 SpeakerVolume=13
 Var% = INSTR(Var$, "V") ' speaker volume
 If Var% Then ' V1
    SpeakerVolume = VAL(MID$(Var$, Var% + 1))
 Endif
 Var$ = Environ$("BLASTERVOLUME")
 IF Len(Var$) THEN ' default volume
    SpeakerVolume=Int(Val(Var$)+.5)
 Endif
END SUB

' turn on speaker and adjust volume.
SUB AdjustVolume(Loudness%)
 ' Turn on speaker.
 V% = &HD1
 Call WriteDSP(V%)
 ' Adjust the volume.
 V% = Loudness% + Loudness% * 16 ' right/left
 OUT BasePort% + 4, &H22
 OUT BasePort% + 5, V% AND &HFF
END SUB

' writes a byte to the DSP (digital signal processor)
SUB WriteDSP(byte2%)
 DO: LOOP WHILE INP(BasePort% + 12) AND &H80
 OUT BasePort% + 12, byte2%
END SUB

' reads a byte from the DSP (digital signal processor)
SUB ReadDSP (byte2%)
 DO: LOOP WHILE INP(BasePort% + 14) AND &H80
 byte2% = INP(BasePort% + 10)
END SUB

' alarm menu
Sub AlarmMenu (Var$)
   VarX=Instr(Var$,"#")
   If VarX Then
      Var1$=Mid$(Var$,VarX+1)
      Var1$=Ltrim$(Rtrim$(Var1$))
      Var$=Left$(Var$,VarX-1)
      Var$=Ltrim$(Rtrim$(Var$))
      Select Case Ucase$(Var$)
      Case "CLEAR", "TYPE", "MUSIC"
         TempQ=Int(Val(Var1$)+.5)
         If TempQ>0 And TempQ<=Max.Alarms Then
            Eat$ = Nul
         Else
            Var$="HELP"
         Endif
      Case "MESSAGE" ' Message#n,s
         TempQ=0 : TempQX=0 : TempQ$=""
         VarX=Instr(Var1$,",")
         If VarX=0 Then
            VarX=Instr(Var1$,";")
         Endif
         If VarX Then
            Temp1$=Left$(Var1$,VarX-1) ' #
            TempQ$=Mid$(Var1$,VarX+1) ' #n,s
            TempQ$=Rtrim$(Ltrim$(TempQ$))
            If TempQ$=Quote$+Quote$ Then
               TempQX=-1
            Endif
            If TempQ$="" Then
               TempQX=-1
            Endif
            TempQ=Int(Val(Temp1$)+.5)
         Else
            TempQ=Int(Val(Var1$)+.5)
         Endif
         If TempQ>0 And TempQ<=Max.Alarms Then
            Eat$ = Nul
         Else
            Var$="HELP"
         Endif
      Case "BEEP" ' Beep#n,x
         TempQ=0 : TempQX!=0! : TempQ$=Nul
         VarX=Instr(Var1$,",")
         If VarX=0 Then
            VarX=Instr(Var1$,";")
         Endif
         If VarX Then
            Temp1$=Left$(Var1$,VarX-1) ' #
            TempQ$=Mid$(Var1$,VarX+1) ' #n,x
            TempQ=Int(Val(Temp1$)+.5)
            If TempQ$="" Then
               Var$="HELP"
            Endif
         Else
            TempQ=Int(Val(Var1$)+.5)
            If TempQ>0 And TempQ<=Max.Alarms Then
               Eat$ = Nul
            Else
               Var$="HELP"
            Endif
         Endif
      Case "SET" ' Set#n,x
         TempQ=0 : TempQX!=0!
         VarX=Instr(Var1$,",")
         If VarX=0 Then
            VarX=Instr(Var1$,";")
         Endif
         If VarX Then
            Temp1$=Left$(Var1$,VarX-1) ' #
            Temp2$=Mid$(Var1$,VarX+1) ' #n,x
            TempQ=Int(Val(Temp1$)+.5)
            TempQX!=Int(Val(Temp2$)+.5)
            TempZ=0
            If TempQ>0 And TempQ<=Max.Alarms Then
               If TempQX!>=0! And TempQX!<=86400! Then ' time
                  TempZ=-1
                  If TempQX!=0! Then TempQX!=-1!
               Endif
            Endif
            If TempZ=0 Then
               Var$="HELP"
            Endif
         Else
            TempQ=Int(Val(Var1$)+.5)
            If TempQ>0 And TempQ<=Max.Alarms Then
               Eat$ = Nul
            Else
               Var$="HELP"
            Endif
         Endif
      Case "COLOR" ' Color#n,c
         TempQ=0 : TempQX=0
         VarX=Instr(Var1$,",")
         If VarX Then
            Temp1$=Left$(Var1$,VarX-1) ' #
            Temp2$=Mid$(Var1$,VarX+1) ' #n,c
            TempQ=Int(Val(Temp1$)+.5)
            TempQX=Int(Val(Temp2$)+.5)
            TempZ=0
            If TempQ>0 And TempQ<=Max.Alarms Then
               If TempQX>=0 And TempQX<=7 Then ' color
                  TempZ=-1
                  If TempQX=0 Then TempQX=-1
               Endif
            Endif
            If TempZ=0 Then
               Var$="HELP"
            Endif
         Else
            TempQ=Int(Val(Var1$)+.5)
            If TempQ>0 And TempQ<=Max.Alarms Then
               Eat$ = Nul
            Else
               Var$="HELP"
            Endif
         Endif
      Case "LIST" ' list#na-b
         VarX=Instr(Var1$,"-")
         If VarX Then
            Temp1$=Left$(Var1$,VarX-1)
            Temp2$=Mid$(Var1$,VarX+1)
            If Len(Temp1$) Then
               TempQ=Int(Val(Temp1$)+.5)
            Else
               TempQ=1
            Endif
            If Len(Temp2$) Then
               TempQX=Int(Val(Temp2$)+.5)
            Else
               TempQX=Max.Alarms
            Endif
         Else
            TempQ=Int(Val(Var1$)+.5)
            TempQX=Int(Val(Var1$)+.5)
         Endif
         TempZ=0
         If TempQ>0 And TempQ<=Max.Alarms Then
            If TempQX>0 And TempQX<=Max.Alarms Then
               If TempQX>=TempQ Then
                   TempZ=-1
                Endif
             Endif
          Endif
          If TempZ=0 Then
             Var$="HELP"
          Endif
      Case Else
         Var$="HELP"
      End Select
   Endif
   Select Case UCase$(Var$)
      Case "HELP"
         VarZ = -1
         Help.File$ = Current.Directory + "alarms.doc"
         CALL List.Help.File(Help.File$)
      Case "CLEAR"
         If TempQ Then
            If Alarm.Flags(TempQ) Then
               Call Clear.Alarm(TempQ)
               Strng3="Alarm"+Str$(TempQ)+" cleared." : Call IO.O
            Else
               Strng3="Alarm not set." : Call IO.O
            Endif
         Else
            Temp5=0
            For VarZZ = 1 To Max.Alarms
               If Alarm.Flags(VarZZ) Then
                  Temp5=Temp5+1
               Endif
               Call Clear.Alarm(VarZZ)
            Next
            If Temp5 Then
               Strng3 = "All alarms cleared." : Call IO.O
            Else
               Strng3 = "No alarms set." : Call IO.O
            Endif
         Endif
      Case "COUNT"
         VarZ = -1
         Temp5 = 0
         For VarZZ = 1 To Max.Alarms
            If Alarm.Flags(VarZZ) Then
               Temp5 = Temp5 + 1
            End If
         Next
         If Temp5 = 0 Then
            Strng3 = "No alarms are set." : Call IO.O
         Else
            Strng3 = "There are" + Str$(Temp5) + " alarms set." : Call IO.O
         End If
      Case "LIST"
         Temp5 = 0: TempC = 0
         VarZ = -1
         VarZ1 = 0
         TempQ1 = 1 : TempQ2 = Max.Alarms
         If TempQ Then TempQ1 = TempQ : TempQ2 = TempQX
         For VarZZ = TempQ1 To TempQ2
            If Alarm.Flags(VarZZ) Then
               VarZ1 = -1
               Var2$ = "Alarm" + Str$(VarZZ) + " set to" + Str$(Alarm.Time(VarZZ)) + " seconds."
               Elapsed! = Timer - Alarm.Start(VarZZ)
               If Elapsed! < 0! Then Elapsed! = Elapsed! + 86400!
               Remaining! = Alarm.Time(VarZZ) - Elapsed!
               Var3$ = LTrim$(Str$(Remaining!))
               If InStr(Var3$, ".") Then
                  Var3$ = Left$(Var3$, InStr(Var3$, ".") - 1)
               End If
               Var2$ = Var2$ + " (" + Var3$ + " seconds remaining)"
               If Alarm.Type(VarZZ) = 0 Then
                  Var2$ = Var2$ + " [time display on]"
               Else
                  Var2$ = Var2$ + " [time display off]"
               End If
               If Alarm.Beep(VarZZ) >= 0 Then
                  If Alarm.Beep(VarZZ) > 0 Then
                     Var2$ = Var2$ + " {beeps"+Str$(Alarm.Beep(VarZZ))+"}"
                  Else
                     Var2$ = Var2$ + " {beep on}"
                  Endif
               Else
                  Var2$ = Var2$ + " {beep off}"
               End If
               Strng3 = Var2$ : Call IO.O
               Strng3 = ""
               If Alarm.Color(VarZZ)>0 Then
                  Select Case Alarm.Color(VarZZ)
                  Case 9 
                     Strng3="blue"
                  Case 10
                     Strng3="green"
                  Case 11
                     Strng3="cyan"
                  Case 12 
                     Strng3="red"
                  Case 13 
                     Strng3="magenta"
                  Case 14 
                     Strng3="yellow"
                  Case 15 
                     Strng3="white"
                  Case Else ' unknown
                     Strng3="<n/a>"
                  End Select
                  Strng3 = " (color is"+Str$(Alarm.Color(VarZZ)-8)+" : "+Strng3 +")"
               Endif
               If Len(Rtrim$(Alarm.Mess(VarZZ))) Then
                  Strng3 = Strng3 + " (message set)"
               Endif
               If Local.Mode Then
                  If Alarm.Music(VarZZ) Then
                     Strng3 = Strng3 + " (music on)"
                  Endif
               Endif
               If Len(Strng3) Then
                  Strng3 = " Set:" + Strng3
                  Call IO.O
               Endif
               Temp5 = Temp5 + 1
            End If
         Next
         If Temp5 Then
            Call Key.Prompt2
         Else
            Strng3 = "No alarms set." : Call IO.O
         End If
      Case "BEEP"
         Temp0=0
         If TempQ Then
            VarZZ=TempQ
         Else
            Strng3 = "Enter alarm(1-" + LTrim$(Str$(Max.Alarms)) + ")? "
            Call IO.I : Var$ = Out9
            VarZZ = Int(Val(Var$)+.5)
         Endif
         If VarZZ >= 1 And VarZZ <= Max.Alarms Then
            If Alarm.Flags(VarZZ) Then
               Temp0=0
               If Len(TempQ$) Then
                  Var$=TempQ$
                  VarZZ2=Int(Val(TempQ$)+.5)
               Else
                  Strng3 = "Enter number of beeps(on/off/<n>)? "
                  Call IO.I : Var$ = Out9
                  VarZZ2 = Int(Val(Var$)+.5)
               Endif
               Select Case UCase$(Var$)
                  Case "ON"
                     Alarm.Beep(VarZZ) = 0
                     Strng3 = "Alarm beep on." : Call IO.O
                  Case "OFF"
                     Alarm.Beep(VarZZ) = -1
                     Strng3 = "Alarm beep off." : Call IO.O
                  Case Else
                     If VarZZ2 >= 1 And VarZZ2 <= Max.Beeps Then
                        Alarm.Beep(VarZZ) = VarZZ2
                        Strng3 = "Alarm beep on." : Call IO.O
                        Strng3 = " Set to"+Str$(VarZZ2)+" beeps." : Call IO.O
                     Else
                        Strng3 = "Alarm beep not set. Value must be 1 to"+Str$(Max.Beeps)+"." : Call IO.O
                     End If
               End Select
            Else
               Strng3 = "Alarm not set." : Call IO.O
            End If
         Else
            Strng3 = "Unknown alarm." : Call IO.O
         End If
      Case "MESSAGE"
         Temp0=0
         If TempQ Then
            VarZZ=TempQ
         Else
            Strng3 = "Enter alarm(1-" + LTrim$(Str$(Max.Alarms)) + ")? "
            Call IO.I : Var$ = Out9
            VarZZ = Int(Val(Var$)+.5)
         Endif
         If VarZZ >= 1 And VarZZ <= Max.Alarms Then
            If Alarm.Flags(VarZZ) Then
               Temp0=0
               If TempQX Then
                  Var$=""
               Else
                  If Len(TempQ$) Then
                     Var$=TempQ$
                  Else
                     Strng3 = "Enter text message? "
                     Call IO.I : Var$ = Out9
                  Endif
               Endif
               If Var$ = "" Then
                  Strng3 = "Text cleared."
               Else
                  Strng3 = "Text set."
               Endif
               Call IO.O
               Alarm.Mess(VarZZ) = Var$
            Else
               Strng3 = "Alarm not set." : Call IO.O
            End If
         Else
            Strng3 = "Unknown alarm." : Call IO.O
         End If
      Case "MUSIC"
         Temp0=0
         TempZ=-1
         If Local.Mode=0 Then
            Strng3 = "Only works in local mode. Set anyway(y/n)? "
            Call IO.I
            If Lcase$(Out9)="y" Then
               Eat$=Nul
            Else
               TempZ=0
            Endif
         Endif
         If TempZ Then
            If TempQ Then
               VarZZ=TempQ
            Else
               Strng3 = "Enter alarm(1-" + LTrim$(Str$(Max.Alarms)) + ")? "
               Call IO.I : Var$ = Out9
               VarZZ = Int(Val(Var$)+.5)
            Endif
            If VarZZ >= 1 And VarZZ <= Max.Alarms Then
               If Alarm.Flags(VarZZ) Then
                  Alarm.Music(VarZZ) = Not Alarm.Music(VarZZ)
                  If Alarm.Music(VarZZ) = 0 Then
                     Strng3 = "Alarm music off." : Call IO.O
                  Else
                     Strng3 = "Alarm music on." : Call IO.O
                  End If
               Else
                  Strng3 = "Alarm not set." : Call IO.O
               End If
            Else
               Strng3 = "Unknown alarm." : Call IO.O
            Endif
         End If
      Case "TYPE"
         Temp0=0
         If TempQ Then
            VarZZ=TempQ
         Else
            Strng3 = "Enter alarm(1-" + LTrim$(Str$(Max.Alarms)) + ")? "
            Call IO.I : Var$ = Out9
            VarZZ = Int(Val(Var$)+.5)
         Endif
         If VarZZ >= 1 And VarZZ <= Max.Alarms Then
            If Alarm.Flags(VarZZ) Then
               Alarm.Type(VarZZ) = Not Alarm.Type(VarZZ)
               If Alarm.Type(VarZZ) = 0 Then
                  Strng3 = "Alarm time display on." : Call IO.O
               Else
                  Strng3 = "Alarm time display off." : Call IO.O
               End If
            Else
               Strng3 = "Alarm not set." : Call IO.O
            End If
         Else
            Strng3 = "Unknown alarm." : Call IO.O
         End If
      Case "SET"
         Temp0=0
         If TempQ Then
            VarZZ=TempQ
         Else
            Strng3 = "Enter alarm(1-" + LTrim$(Str$(Max.Alarms)) + ")? "
            Call IO.I : Var$ = Out9
            VarZZ = Int(Val(Var$)+.5)
         Endif
         If VarZZ >= 1 And VarZZ <= Max.Alarms Then
            Temp0=0
            If TempQX! Then
               If TempQX!=-1! Then
                  VarZZ2!=0!
               Else
                  VarZZ2!=TempQX!
               Endif
            Else
               Strng3 = "Enter alarm time(seconds)? "
               Call IO.I : Var2$ = Out9
               VarZZ2! = Int(Val(Var2$)+.5)
            Endif
            If VarZZ2! = 0! Then
               Call Clear.Alarm(VarZZ)
               Strng3 = "Alarm" + Str$(VarZZ) + " cleared." : Call IO.O
            Else
               If VarZZ2! > 0! And VarZZ2! <= 86400! Then
                  Call Clear.Alarm(VarZZ)
                  Alarm.Flags(VarZZ) = -1
                  Alarm.Start(VarZZ) = Timer
                  Alarm.Time(VarZZ) = VarZZ2!
                  Strng3 = "Alarm" + Str$(VarZZ) + " set." : Call IO.O
               Else
                  Strng3 = "Illegal alarm time." : Call IO.O
               End If
            End If
         Else
            Strng3 = "Unknown alarm." : Call IO.O
         End If
      Case "COLOR"
         Temp0=0
         If TempQ Then
            VarZZ=TempQ
         Else
            Strng3 = "Enter alarm(1-" + LTrim$(Str$(Max.Alarms)) + ")? "
            Call IO.I : Var$ = Out9
            VarZZ = Int(Val(Var$)+.5)
         Endif
         StartColor:
         If VarZZ >= 1 And VarZZ <= Max.Alarms Then
            If Alarm.Flags(VarZZ) Then
               Temp0=0
               If TempQX Then
                  If TempQX=-1 Then
                     VarZZ2=0 : Var2$=Nul
                  Else
                     VarZZ2=TempQX : Var2$=Nul
                  Endif
               Else
                  Strng3 = "Enter alarm color(?=Help, 0-clear, 1-7)? "
                  Call IO.I : Var2$ = Out9
                  VarZZ2 = Int(Val(Var2$)+.5)
               Endif
               If Var2$ = "?" Then
                  Strng3 = "Foreground intensity:" : Call IO.O
                  Strng3 = "  0  -  Clear color" : Call IO.O
                  Strng3 = "  1  -  Blue" : Call IO.O
                  Strng3 = "  2  -  Green" : Call IO.O
                  Strng3 = "  3  -  Cyan" : Call IO.O
                  Strng3 = "  4  -  Red" : Call IO.O
                  Strng3 = "  5  -  Magenta" : Call IO.O
                  Strng3 = "  6  -  Yellow" : Call IO.O
                  Strng3 = "  7  -  White" : Call IO.O
                  Call Modem.ANSI(4,33,14)
                  Call ScreenANSI(4,33,14)
                  Call Key.Prompt2
                  Goto StartColor
               End If
               If VarZZ2 = 0 Then
                  Alarm.Color(VarZZ) = 0
                  Strng3 = "Alarm" + Str$(VarZZ) + " color cleared." : Call IO.O
               Else
                  If VarZZ2 >= 1 And VarZZ2 <= 7 Then
                     Alarm.Color(VarZZ) = VarZZ2 + 8
                     Strng3 = "Alarm" + Str$(VarZZ) + " color set." : Call IO.O
                  Else
                     Strng3 = "Illegal alarm color." : Call IO.O
                  End If
               End If
            Else
               Strng3 = "Alarm not set." : Call IO.O
            Endif
         Else
            Strng3 = "Unknown alarm." : Call IO.O
         End If
      Case Else
         Strng3 = "Unknown ALARM function." : Call IO.O
   End Select
End Sub

' alarm timer trap
'  VarX = 1 normal input loops.
'  VarX = 0 tight prime loops.
Sub CheckAlarms(VarX)
   Static Var As Single
   If Var! = 0! Then
      Var! = Timer
   Endif
   ' check timer every second
   ElapsedTime! = Timer - Var!
   If ElapsedTime! < 0! Then
      ElapsedTime! = ElapsedTime! + 86400!
   End If
   If ElapsedTime! >= 1! Then
      TimeElapsed = True
   Else
      TimeElapsed = False
   End If
   If TimeElapsed = 0 Then
      Exit Sub
   Endif

   ' check alarms
   For VarZZ = 1 To Max.Alarms
      If Alarm.Time(VarZZ) = 0! Then
         Alarm.Flags(VarZZ) = 0
      End If
   Next
   For VarZZ = 1 To Max.Alarms
      VarZ5 = Alarm.Flags(VarZZ)
      VarZ2! = Alarm.Start(VarZZ)
      VarZ3! = Alarm.Time(VarZZ)
      If VarZ5 Then
         If VarZ2! > 0! And VarZ3! > 0! Then
            ElapsedTime! = Timer - VarZ2!
            If ElapsedTime! < 0! Then
               ElapsedTime! = ElapsedTime! + 86400!
            End If
            If ElapsedTime! >= VarZ3! Then
               TimeElapsed = True
            Else
               TimeElapsed = False
            End If
            If TimeElapsed Then
               VarZ = -1
               Alarm.Flags(VarZZ) = False
               If Alarm.Beep(VarZZ) = 0 Then
                  If MakeBeep Then
                     Sound Freq,Duration
                     Call Put.Modem(Chr$(7))
                  Endif
               End If
               If Alarm.Beep(VarZZ) > 0 Then
                  For VarZZ2 = 1 To Alarm.Beep(VarZZ)
                     If MakeBeep Then
                        Call Put.Modem(Chr$(7))
                        If Lost.Carrier Then
                           Exit Sub
                        Endif
                        ' trap ctrl-c
                        Allow.Break=True
                        Break=False
                        Call Check.Break
                        VarXX=Break
                        Allow.Break=False
                        Break=False
                        If VarXX Then
                           Exit For
                        Endif
                        ' trap keyboard
                        If StdinEnabled=0 Then
                           If Len(Inkey$) Then
                              Exit For
                           Endif
                        Endif
                        Sound Freq,Duration
                        Sleep 1
                     Endif
                  Next
               End If
               If Alarm.Music(VarZZ) Then
                  If Local.Mode Then
                     VarZ$ = "o3 L8 E D+ E D+ E o2 B o3 D C L2 o2 A"
                     Play VarZ$
                  Endif
               End If
               If VarX=1 Then
                  Call IO.O
               Endif
               If Alarm.Color(VarZZ) Then
                  TempZ=Alarm.Color(VarZZ)
                  Call Convert.Color2(TempZ)
               Else
                  Call Modem.ANSI(4,33,14)
                  Call ScreenANSI(4,33,14)
               End If
               If Alarm.Type(VarZZ) = 0 Then
                  Strng3 = "Alarm" + Str$(VarZZ) + " notice! It is now " + Date$ + " " + Time$ : Call IO.O
               Else
                  Strng3 = "Alarm" + Str$(VarZZ) + " notice!" : Call IO.O
               End If
               If Len(Rtrim$(Alarm.Mess(VarZZ))) Then
                  Strng3 = "  Message: "+Alarm.Mess(VarZZ) : Call IO.O
               Endif
               Call Modem.ANSI(4,37,15)
               Call ScreenANSI(4,37,15)
               If VarX = 0 Then
                  ' sleep in tight prime loops.
                  Sleep 1
               Endif
               If VarX Then
                  Carriage.Input=True
                  Strng3 = Prompt$
                  Call IO.O
               Endif
            End If
         End If
      End If
   Next
End Sub

' convert basic color code to ansi color code.
Sub Convert.Color2(VarX)
 On Local Error Goto ErrorRoutine28
 VarF=37
 VarF2=VarX
 Select Case VarX
 Case 9 ' blue
    VarF=34
 Case 10 ' green
    VarF=32
 Case 11 ' cyan
    VarF=36
 Case 12 ' red
    VarF=31
 Case 13 ' magenta
    VarF=35
 Case 14 ' yellow
    VarF=33
 Case 15 ' white
    VarF=37
 Case Else
    VarF=37
 End Select
 Call Modem.ANSI(4,VarF,VarF2)
 Call ScreenANSI(4,VarF,VarF2)
ErrorResume28:
 Exit Sub
ErrorRoutine28:
 Resume ErrorResume28
End Sub

Sub Clear.Alarm(TempQ)
 On Local Error Goto ErrorRoutine29
 If TempQ>0 And TempQ<=Max.Alarms Then
    Alarm.Flags(TempQ)=0
    Alarm.Start(TempQ)=0!
    Alarm.Time(TempQ)=0!
    Alarm.Type(TempQ)=0
    Alarm.Beep(TempQ)=0
    Alarm.Color(TempQ)=0
    Alarm.Music(TempQ)=0
    Alarm.Mess(TempQ)=""
 Endif
ErrorResume29:
 Exit Sub
ErrorRoutine29:
 Resume ErrorResume29
End Sub

' get drive freespace (modified 12/09/2022)
' input: T% = 1  freespace
'        T% = 2  totalspace
'        T% = 3  usedspace
' output: T$ = diskspace
SUB FreeSpace (T%, T$)
 ON LOCAL ERROR GOTO DriveError
 DIM ASCIIZ AS STRING * 260

 ' declare structures
 DIM FAT32Struc AS FAT32Type

 ' get drive letter
 T# = 0#
 D$ = LEFT$(T$, 1)
 D$ = UCASE$(D$)
 IF D$ >= "A" AND D$ <= "Z" THEN
    V = ASC(D$) - 64
 ELSE
    V = 3
 END IF

 ' get FAT32 drive space
 ASCIIZ = CHR$(V + 64) + ":\" + CHR$(0)
 InregsX.AX = &H7303
 InregsX.DS = VARSEG(ASCIIZ)
 InregsX.DX = VARPTR(ASCIIZ)
 InregsX.ES = VARSEG(FAT32Struc)
 InregsX.DI = VARPTR(FAT32Struc)
 InregsX.CX = LEN(FAT32Struc)
 CALL InterruptX(&H21, InregsX, OutregsX)
 IF (OutregsX.Flags AND &H1) = &H0 THEN
    IF T% = 1 THEN ' free
       GOSUB Get.Free
    END IF
    IF T% = 2 THEN ' total
       GOSUB Get.Total
    END IF
    IF T% = 3 THEN ' used
       GOSUB Get.Total
       Total# = T#
       GOSUB Get.Free
       Free# = T#
       T# = Total# - Free#
    END IF
    IF T# THEN
       T$ = LTRIM$(STR$(T#))
       EXIT SUB
    END IF
 END IF

 ' check disk space.
 InregsX.AX = &H3600
 InregsX.DX = V
 CALL InterruptX(&H21, InregsX, OutregsX)
 ' check drive exists.
 IF OutregsX.AX = &HFFFF THEN
    T# = 0#
 ELSE
    ' Note: FAT16 only reports up to 2 GB.
    IF T% = 1 THEN ' free
       GOSUB Get.Free16
    END IF
    IF T% = 2 THEN ' total
       GOSUB Get.Total16
    END IF
    IF T% = 3 THEN ' used
       GOSUB Get.Total16
       Total# = T#
       GOSUB Get.Free16
       Free# = T#
       T# = Total# - Free#
    END IF
 END IF
DriveResume:
 IF T# = 0# THEN
    T# = 256 * 1024 ^ 2
 END IF
 T$ = LTRIM$(STR$(T#))
 EXIT SUB
Get.Free:
 Bytes# = CDBL(ASC(MID$(FAT32Struc.BytesPerSector, 1, 1)))
 Bytes# = Bytes# + CDBL(ASC(MID$(FAT32Struc.BytesPerSector, 2, 1))) * 256#
 Bytes# = Bytes# + CDBL(ASC(MID$(FAT32Struc.BytesPerSector, 3, 1))) * 65536#
 Bytes# = Bytes# + CDBL(ASC(MID$(FAT32Struc.BytesPerSector, 4, 1))) * 16777216#
 Sectors# = CDBL(ASC(MID$(FAT32Struc.FreeSectors, 1, 1)))
 Sectors# = Sectors# + CDBL(ASC(MID$(FAT32Struc.FreeSectors, 2, 1))) * 256#
 Sectors# = Sectors# + CDBL(ASC(MID$(FAT32Struc.FreeSectors, 3, 1))) * 65536#
 Sectors# = Sectors# + CDBL(ASC(MID$(FAT32Struc.FreeSectors, 4, 1))) * 16777216#
 T# = Bytes# * Sectors#
 RETURN
Get.Total:
 Bytes# = CDBL(ASC(MID$(FAT32Struc.BytesPerSector, 1, 1)))
 Bytes# = Bytes# + CDBL(ASC(MID$(FAT32Struc.BytesPerSector, 2, 1))) * 256#
 Bytes# = Bytes# + CDBL(ASC(MID$(FAT32Struc.BytesPerSector, 3, 1))) * 65536#
 Bytes# = Bytes# + CDBL(ASC(MID$(FAT32Struc.BytesPerSector, 4, 1))) * 16777216#
 Sectors# = CDBL(ASC(MID$(FAT32Struc.TotalSectors, 1, 1)))
 Sectors# = Sectors# + CDBL(ASC(MID$(FAT32Struc.TotalSectors, 2, 1))) * 256#
 Sectors# = Sectors# + CDBL(ASC(MID$(FAT32Struc.TotalSectors, 3, 1))) * 65536#
 Sectors# = Sectors# + CDBL(ASC(MID$(FAT32Struc.TotalSectors, 4, 1))) * 16777216#
 T# = Bytes# * Sectors#
 RETURN
Get.Free16:
 ' free space on drive in bytes is AX * BX * CX adjusted signed-int
 T1# = OutregsX.AX: IF T1# < 0# THEN T1# = T1# + 65536#
 T2# = OutregsX.BX: IF T2# < 0# THEN T2# = T2# + 65536#
 T3# = OutregsX.CX: IF T3# < 0# THEN T3# = T3# + 65536#
 T# = T1# * T2# * T3#
 RETURN
Get.Total16:
 ' total space on drive in bytes is AX * CX * DX adjusted signed-int
 T1# = OutregsX.AX: IF T1# < 0# THEN T1# = T1# + 65536#
 T2# = OutregsX.CX: IF T2# < 0# THEN T2# = T2# + 65536#
 T3# = OutregsX.DX: IF T3# < 0# THEN T3# = T3# + 65536#
 T# = T1# * T2# * T3#
 RETURN
DriveError:
 RESUME DriveResume
END SUB


