{$M 32000,100000,655000}
{$I-,R-,V-,S-,B-,G+}
uses dsp,crt,playDMA,dos,safe_IO,crt2,graph;

const MaxIns = 31;
   Octaves : ARRAY[1..36] OF WORD =     { Tuning-Tabelle fr 3 Oktaven }
      (856,808,762,720,678,640,604,570,538,508,480,453,
       428,404,381,360,339,320,302,285,269,254,240,226,
       214,202,190,180,170,160,151,143,135,127,120,113);
    SpeedFact = 1000;

type
   MODInstrumentType = RECORD
      SampName : ARRAY[0..21] OF CHAR;  { Name des Instruments       }
      SampLen  : WORD;                  { L„nge des Instr. in Worten }
      SampTune : BYTE;                  { FineTune-Einstellung       }
      SampAmp  : BYTE;                  { Lautst. des Instrumentes   }
      SampRepS : WORD;                  { Repeat-Start in Worten     }
      SampRepL : WORD;                  { Repeat-L„nge in Worten     }
      END;

   MODHeaderType = RECORD
      MODName  : ARRAY[0..19] OF CHAR;                  { MOD-Filename      }
      MODInstr : ARRAY[1..MaxIns] OF MODInstrumentType; { Instrumente       }
      MODLen   : BYTE;                                  { L„nge des Songs   }
      MODMisc  : BYTE;                                  { CIAA-Speed        }
      MODPattr : ARRAY[1..128] OF BYTE;                 { Patternliste      }
      MODSign  : ARRAY[1..4] OF CHAR;                   { Erkennungs-String }
      END;

   MODNoteType    = ARRAY[1..4] OF BYTE;        { Eine Note  = 4 Bytes   }

   MODPatternLine = RECORD                      { Eine Patternzeile ist  }
      Channel1, Channel2,                       { fr 4 Kan„le ausgelegt }
      Channel3, Channel4  : MODNoteType;
      END;

   MODPatternType = array[1..64] of MODPatternLine;

    dataType = array[1..64000] of byte;

var i:integer;
    data: pointer; { Der Buffer }
    Buffer1,Buffer2: pointer;
    StopPlayer,ReadyForExit: boolean;
    UseBuffer: word;
    OldInt15: pointer;
    FileName: string;
    ModF: file;
    ModHeader: MODHeaderType;
    SampleNames: array[1..MaxIns] of string[22];
    Samples: array[1..MaxIns] of pointer;
    SampleSizes: array[1..MaxIns] of word;
    SampleVols: array[0..MaxIns] of byte; { Volume (0..63) }
    SampleRepS,SampleRepL: array[0..MaxIns] of word;
    Arrangement: array[1..128] of byte;
    Patterns: array[1..128] of ^MODPatternType;
    TotPatterns: byte;
    OctArray: array[0..855] of byte;       { Fr schnellere Notenum-Funktion }
    BufferSize: word;
    BufferLim,BufferCtr: word;
    AktPattern,AktLine: word;
    PatternNum: byte;
    MixAnz: byte;
    AdditVal,ctr: word;
    DataSeg,DataOfs: word;
    Channel1Seg,Channel1Ofs,
    Channel2Seg,Channel2Ofs,
    Channel3Seg,Channel3Ofs,
    Channel4Seg,Channel4Ofs: word;
    Channel1Size,Channel2Size,
    Channel3Size,Channel4Size: word;
    Channel1Inst,Channel2Inst,
    Channel3Inst,Channel4Inst: word;
    Channel1Note,Channel2Note,
    Channel3Note,Channel4Note: word;
    Channel1Vol,Channel2Vol,
    Channel3Vol,Channel4Vol: byte;
    Channel1RepS,Channel1RepL,
    Channel2RepS,Channel2RepL,
    Channel3RepS,Channel3RepL,
    Channel4RepS,Channel4RepL: word;
    Channel1RepLim,Channel2RepLim,
    Channel3RepLim,Channel4RepLim: word;
    Channel1Rep,Channel2Rep,
    Channel3Rep,Channel4Rep: byte;
    SpeedListVK,
    SpeedListNK: array[1..1000] of word;
    Channel1VK,Channel1NK,
    Channel2VK,Channel2NK,
    Channel3VK,Channel3NK,
    Channel4VK,Channel4NK: word;
    Channel1SpVK,Channel1SpNK,  { Die "Speedkonstanten" fr die Kan„le }
    Channel2SpVK,Channel2SpNK,
    Channel3SpVK,Channel3SpNK,
    Channel4SpVK,Channel4SpNK: word;
    Speed: byte;
    DataAddr: longint;
    AddrAndFFFF: word;
    FSize: word;
    Freq,FreqDiv50: word;
    ch: char;
    OldLine,OldLine2: byte;
    Channel1Peak,Channel2Peak,Channel3Peak,Channel4Peak: byte;
    PeakLimit,PeakCtr,OPeakCtr,PeakCtr2: byte;
    OldPattern: byte;
    s: string;
    result: word;
    PatternBreak: boolean;
    addr: longint; { Fr 'NewInt15 }
    IntNo: byte;
    t1,t2: word; {..}
    WriteInstrs: boolean;
    OldInt1C: pointer;
    gd,gm: integer;
    grmode: boolean;

    oldosci: array[1..640] of byte;
    oscipos: word;
    olddrwnbuf: word;
    max,min,med: byte;
    channel1buf,channel2buf,channel3buf,channel4buf: pointer;
    channelbufs: array[1..4] of pointer;
    Channel1BSeg,Channel2BSeg,Channel3BSeg,Channel4BSeg: word;
    Channel1BPos,Channel2BPos,Channel3BPos,Channel4BPos: word;
    ChannelOsciPos: array[1..4] of word;
    oldchannelosci: array[1..4,1..640] of byte;
    maxc,minc,medc: array[1..4] of byte;
    totc: array[1..4] of longint;

procedure sbFehler;
begin
 fehler('DSP kann nicht beschrieben werden');
end;

procedure schreib_dsp(wert : Byte); assembler;
asm
 mov cx,10000 { cx = Counter }
 mov dx,BASE
 add dx,0Ch
 @Loop1:
  inc bx
  in al,dx
  and al,80h
  cmp al,0
  je @OK1
  loop @Loop1
  call sbFehler
 @OK1:
 mov al,wert
 out dx,al
end;

procedure Schreib_dsp_asm; assembler; { 'Wert' in ah }
asm
 mov cx,10000 { cx = Counter }
 mov dx,BASE
 add dx,0Ch
 @Loop1:
  inc bx
  in al,dx
  and al,80h
  cmp al,0
  je @OK1
  loop @Loop1
  call sbFehler
 @OK1:
 xchg ah,al
 out dx,al
end;

FUNCTION NoteName(Period : WORD):String;
CONST
   NNames : ARRAY[0..11] OF String[2] =
      ('C-','C#','D-','D#','E-','F-','F#','G-','G#','A-','A#','H-');
VAR
   WorkStr : String;
   NCount  : BYTE;
BEGIN
   NCount := 1;
   NoteName := '???';
   IF (Period = 0) THEN BEGIN
      NCount := 37;
      NoteName := '---';
      END;
   WHILE (NCount <= 36) DO BEGIN
      IF (Period = Octaves[NCount]) THEN BEGIN
         Dec(Ncount);
         Str((NCount DIV 12)+1,WorkStr);
         NoteName := NNames[NCount-(NCount DIV 12)*12]+WorkStr;
         NCount := 37;
         END;
      Inc(NCount);
      END;
   END;

function NoteNum(val: word):byte;
var i,n:byte;
begin
 n := 0;
 for i := 1 to 36 do
  if Octaves[i] = val then n := i;
 NoteNum := n;
end;

procedure NoteNum_asm; assembler; { selbe Funktion wie oben, allerdings fr Assemblergebrauch:
                                 in: bx = val
                                 out: ax = nummer }
asm
 xor ah,ah
 mov al,ds:[offset octarray+bx-1]
end;



procedure ReadModFile;
var ctr,ctr2,i,_SSeg,_SOfs,SSize: word;
    SamplesTotal,SampleOfs,FSize: LongInt;
begin
 for ctr := 1 to MaxIns do begin
  Samples[ctr] := nil;
  SampleSizes[ctr] := 0;
 end;

 Close(ModF);
 if IOResult <> 0 then ;
 Reset(ModF,1);
 if IOResult <> 0 then begin
  writeln('Ungltiger Dateiname');
  halt;
 end;
 FSize := FileSize(ModF);
 BlockRead(ModF,ModHeader,SizeOf(ModHeader));

 SamplesTotal := 0;
 for ctr := 1 to MaxIns do begin   
  Inc(SamplesTotal,Swap(ModHeader.MODInstr[ctr].SampLen)*2);
  SampleVols[ctr] := ModHeader.MODInstr[ctr].SampAmp;
  SampleRepS[ctr] := Swap(ModHeader.MODInstr[ctr].SampRepS)*2;
  SampleRepL[ctr] := Swap(ModHeader.MODInstr[ctr].SampRepL)*2;
  if SampleRepL[ctr] < 10 then SampleRepL[ctr] := 0;
 end;

 SampleOfs := FSize-SamplesTotal;
 Seek(ModF,SampleOfs);

 for ctr := 1 to MaxIns do       { Samples laden }
 with ModHeader.MODInstr[ctr] do begin
  i := 0;
  SampleNames[ctr] := '';
  while SampName[i] <> #0 do begin
   SampleNames[ctr] := SampleNames[ctr]+SampName[i];
   Inc(i);
  end;
  for i := Length(SampleNames[ctr]) to 22 do
   SampleNames[ctr] := SampleNames[ctr]+' ';

  SSize := Swap(Samplen)*2;
  SampleSizes[ctr] := SSize;
  if SSize > 0 then begin
   GetMem(Samples[ctr],SSize);
   FillChar(Samples[ctr]^,SSize,0);
   BlockRead(ModF,Samples[ctr]^,SSize);
   _SSeg := Seg(Samples[ctr]^);
   _SOfs := Ofs(Samples[ctr]^);
(*   for ctr2 := 1 to SSize do begin
    if Mem[SSeg:SOfs] <= 0 then Mem[SSeg:SOfs] := 128-Abs(Mem[SSeg:SOfs])
     else Mem[SSeg:SOfs] := Mem[SSeg:SOfs] + 128;
     Inc(SOfs);
    end;
    *)
    asm
     push ds
     mov cx,SSize
     mov si,_SOfs
     mov es,_SSeg
     mov di,_SOfs
     mov ds,_SSeg
    @Loop1:
      lodsb
      sub al,128
      stosb
      loop @Loop1
     pop ds
    end;
  end;
 end;

 Move(MODHeader.MODPattr,Arrangement,128);
 TotPatterns := ModHeader.MODLen;
 Seek(ModF, SizeOf(MODHeader));  { -- Pattern einlesen -- }
 for ctr := 1 to ModHeader.ModLen do begin
  New(Patterns[ctr]);
  BlockRead(ModF,Patterns[ctr]^,SizeOf(Patterns[ctr]^));
 end;
 Close(modF);
end;

procedure InitSpeedList;
var i:integer;
begin
 for i := 1 to 1000 do begin
  SpeedListVK[i] := Trunc((60*60/i*1000)/freq);
  SpeedListNK[i] := Round(Frac((60*60/i*1000)/freq)*SpeedFact);
                 { z.B. VK = 1, NK = 233 -> SpeedList[x] = 1.233 }
 end;
end;

procedure InitOctArray;
var i:word;
begin
 FillChar(OctArray,sizeof(octarray),0);
 for i := 0 to 855 do
  if NoteNum(i+1) <> 0 then
   OctArray[i] := Notenum(i+1);
end;

procedure InitChannelVars;
begin
 DataSeg := Seg(data^);
 DataOfs := Ofs(data^);
 UseBuffer := 0;

 Channel1VK := 0; Channel1NK := 0;
 Channel2VK := 0; Channel2NK := 0;
 Channel3VK := 0; Channel3NK := 0;
 Channel4VK := 0; Channel4NK := 0;

 Channel1Inst := 0; Channel1Note := 0; Channel1Size := 0;
 Channel2Inst := 0; Channel2Note := 0; Channel2Size := 0;
 Channel3Inst := 0; Channel3Note := 0; Channel3Size := 0;
 Channel4Inst := 0; Channel4Note := 0; Channel4Size := 0;

 Channel1Vol := 0; Channel2Vol := 0;
 Channel3Vol := 0; Channel4Vol := 0;

 Channel1RepS := 0; Channel1RepL := 0; Channel1Rep := 0; Channel1RepLim := 0;
 Channel2RepS := 0; Channel2RepL := 0; Channel2Rep := 0; Channel2RepLim := 0;
 Channel3RepS := 0; Channel3RepL := 0; Channel3Rep := 0; Channel3RepLim := 0;
 Channel4RepS := 0; Channel4RepL := 0; Channel4Rep := 0; Channel4RepLim := 0;

 Channel1SpVK := 0; Channel1SpNK := 0; Channel1Seg := 0; Channel1Ofs := 0;
 Channel2SpVK := 0; Channel2SpNK := 0; Channel2Seg := 0; Channel2Ofs := 0;
 Channel3SpVK := 0; Channel3SpNK := 0; Channel3Seg := 0; Channel3Ofs := 0;
 Channel4SpVK := 0; Channel4SpNK := 0; Channel4Seg := 0; Channel4Ofs := 0;
end;

procedure IncPositions; assembler;
asm

   @Channel1:
     mov bx,Channel1VK
     add bx,2
     cmp bx,Channel1Size
     jnb @SetToZero1
    @OneOK1:
     cmp Channel1Rep,0
     jz @NoRep1
     mov ax,Channel1RepLim
     cmp Channel1VK,ax
     jb @NoRep1
     mov ax,Channel1RepS
     mov Channel1VK,ax
    @NoRep1:
     mov ax,Channel1SpVK
     add Channel1VK,ax

     mov ax,Channel1SpNK
     add Channel1NK,ax

     mov ax,speedFact
     cmp Channel1NK,ax
     jbe @NothingToIncrease1
    @DoIt1:
     inc Channel1VK
     sub Channel1NK,ax

     jmp @NothingToIncrease1

   @SetToZero1:
    mov bx,channel1size
    mov channel1vk,bx
    cmp Channel1RepL,0
    jz @NothingToIncrease1
    mov Channel1Rep,1
    mov ax,channel1RepS
    mov Channel1VK,ax

 @NothingToIncrease1:

   @Channel2:
     mov bx,Channel2VK
     add bx,2
     cmp bx,Channel2Size
     jnb @SetToZero2
    @OneOK2:
     cmp Channel2Rep,0
     jz @NoRep2
     mov ax,Channel2RepLim
     cmp Channel2VK,ax
     jb @NoRep2
     mov ax,Channel2RepS
     mov Channel2VK,ax
    @NoRep2:
     mov ax,Channel2SpVK
     add Channel2VK,ax

     mov ax,Channel2SpNK
     add Channel2NK,ax

     mov ax,speedFact
     cmp Channel2NK,ax
     jbe @NothingToIncrease2
    @DoIt2:
     inc Channel2VK
     sub Channel2NK,ax
     jmp @NothingToIncrease2

   @SetToZero2:
    mov bx,channel2size
    mov channel2vk,bx
    cmp Channel2RepL,0
    jz @NothingToIncrease2
    mov Channel2Rep,1
    mov ax,channel2RepS
    mov Channel2VK,ax

 @NothingToIncrease2:

   @Channel3:
     mov bx,Channel3VK
     add bx,2
     cmp bx,Channel3Size
     jnb @SetToZero3
    @OneOK3:
     cmp Channel3Rep,0
     jz @NoRep3
     mov ax,Channel3RepLim
     cmp Channel3VK,ax
     jb @NoRep3
     mov ax,Channel3RepS
     mov Channel3VK,ax
    @NoRep3:
     mov ax,Channel3SpVK
     add Channel3VK,ax

     mov ax,Channel3SpNK
     add Channel3NK,ax

     mov ax,speedFact
     cmp Channel3NK,ax
     jbe @NothingToIncrease3
    @DoIt3:
     inc Channel3VK
     sub Channel3NK,ax

     jmp @NothingToIncrease3

   @SetToZero3:
    mov bx,channel3size
    mov channel3vk,bx
    cmp Channel3RepL,0
    jz @NothingToIncrease3
    mov Channel3Rep,1
    mov ax,channel3RepS
    mov Channel3VK,ax

 @NothingToIncrease3:

   @Channel4:
     mov bx,Channel4VK
     add bx,2
     cmp bx,Channel4Size
     jnb @SetToZero4
    @OneOK4:
     cmp Channel4Rep,0
     jz @NoRep4
     mov ax,Channel4RepLim
     cmp Channel4VK,ax
     jb @NoRep4
     mov ax,Channel4RepS
     mov Channel4VK,ax
    @NoRep4:
     mov ax,Channel4SpVK
     add Channel4VK,ax

     mov ax,Channel4SpNK
     add Channel4NK,ax

     mov ax,speedFact
     cmp Channel4NK,ax
     jbe @NothingToIncrease4
    @DoIt4:
     inc Channel4VK
     sub Channel4NK,ax

     jmp @NothingToIncrease4

   @SetToZero4:
    mov bx,channel4Size
    mov channel4vk,bx
    cmp Channel4RepL,0
    jz @NothingToIncrease4
    mov Channel4Rep,1
    mov ax,channel4RepS
    mov Channel4VK,ax

 @NothingToIncrease4:

end;

procedure AddSamples; assembler;

asm
 mov AdditVal,0
 mov MixAnz,4
 xor ah,ah

 @Channel1:
   cmp Channel1Seg,0
   jz @OK1

  @ThreeOK1:
  mov es,Channel1Seg
  mov di,Channel1Ofs
  add di,Channel1VK

  mov al,es:[di]
  mov bl,Channel1Vol
  mul bl
  shr ax,6

  add AdditVal,ax
{  mov es,Channel1BSeg
  mov di,Channel1BPos
  stosb
  inc Channel1BPos}
 @OK1:

{ --------------------------------------------------------------- }

 @Channel2:
   cmp Channel2Seg,0
   jz @OK2
  @ThreeOK2:
  mov es,Channel2Seg
  mov di,Channel2Ofs
  add di,Channel2VK

  mov al,es:[di]
  mov bl,Channel2Vol
  mul bl
  shr ax,6

  add AdditVal,ax
{  mov es,Channel2BSeg
  mov di,Channel2BPos
  stosb
  inc Channel2BPos}

 @OK2:

{ --------------------------------------------------------------- }

 @Channel3:
   cmp Channel3Seg,0
   jz @OK3

  @ThreeOK3:
  mov es,Channel3Seg
  mov di,Channel3Ofs
  add di,Channel3VK

  mov al,es:[di]
  mov bl,Channel3Vol
  mul bl
  shr ax,6

  add AdditVal,ax
{  mov es,Channel3BSeg
  mov di,Channel3BPos
  stosb
  inc Channel3BPos
 }
 @OK3:

{ --------------------------------------------------------------- }

 @Channel4:
   cmp Channel4Seg,0
   jz @OK4

  @ThreeOK4:
  mov es,Channel4Seg
  mov di,Channel4Ofs
  add di,Channel4VK

  mov al,es:[di]
  mov bl,Channel4Vol
  mul bl
  shr ax,6

  add AdditVal,ax
{  mov es,Channel4BSeg
  mov di,Channel4BPos
  stosb
  inc Channel4BPos
 }
 @OK4:

end;

procedure GetNotes; assembler;
var LineSeg,LineOfs: word;
    bval1: byte;
    val1: word;
    flag1: byte;
    c1b1,c1b2,c1b3,c1b4,
    c2b1,c2b2,c2b3,c2b4,
    c3b1,c3b2,c3b3,c3b4,
    c4b1,c4b2,c4b3,c4b4: byte;
    aktspeed: byte;
asm
  cmp PatternBreak,1
  jz @IncPattern
  cmp aktline,64
  jnz @IncLine

 @IncPattern:
  mov PatternBreak,0
  mov si,offset Arrangement
  xor ah,ah
  mov al,patternNum
  add si,ax
  lodsb
  inc al
  mov aktpattern,ax
  mov aktline,1
  inc patternNum
  jmp @Weiter1

 @IncLine:
  inc aktline
 @Weiter1:

  mov si,offset patterns
  mov ax,aktpattern
  dec ax
  shl ax,2
  add si,ax
  lodsw
  mov bx,aktline
  dec bx
  shl bx,4
  add ax,bx
  mov LineOfs,ax
  lodsw
  mov lineSeg,ax

  { ----- Channel 1 -------- }
  mov es,lineseg
  mov di,lineofs
  mov al,byte ptr es:[di]
  mov c1b1,al
  mov al,byte ptr es:[di+1]
  mov c1b2,al
  mov al,byte ptr es:[di+2]
  mov c1b3,al
  mov al,byte ptr es:[di+3]
  mov c1b4,al

  mov al,byte ptr es:[di+4]
  mov c2b1,al
  mov al,byte ptr es:[di+5]
  mov c2b2,al
  mov al,byte ptr es:[di+6]
  mov c2b3,al
  mov al,byte ptr es:[di+7]
  mov c2b4,al

  mov al,byte ptr es:[di+8]
  mov c3b1,al
  mov al,byte ptr es:[di+9]
  mov c3b2,al
  mov al,byte ptr es:[di+10]
  mov c3b3,al
  mov al,byte ptr es:[di+11]
  mov c3b4,al

  mov al,byte ptr es:[di+12]
  mov c4b1,al
  mov al,byte ptr es:[di+13]
  mov c4b2,al
  mov al,byte ptr es:[di+14]
  mov c4b3,al
  mov al,byte ptr es:[di+15]
  mov c4b4,al
  mov aktspeed, 0FFh
 @Channel1:
  mov flag1,1
  mov ah,c1b1
  and ah,0F0h
  mov al,c1b3
  shr al,4
  add al,ah
  cmp al,0
  jnz @OK1
  mov al,c1b3
  and al,0Fh
  cmp al,0
  jz @NextInst1
  mov flag1,0
  jmp @Effects1
 @OK1:

  xor ah,ah
  mov Channel1Rep,0
  push ax
  mov si,offset SampleVols
  add si,ax
  lodsb
  mov Channel1Vol,al
  pop ax
  mov Channel1Inst,ax
  dec ax
  shl ax,1
  mov si,offset SampleSizes
  add si,ax
  lodsw
  dec ax
  mov Channel1Size,ax

 @Effects1:
  mov bl,c1b3   { auf Effekte berprfen }
  and bl,0Fh
  cmp bl,12
  jnz @NoVolSet1
  mov al,c1b4
  mov channel1vol,al
  jmp @VolSet1
 @NoVolSet1:
 @VolSet1:
  cmp bl,10     { Vol-Slide }
  jnz @NoVolSlide1
  cmp c1b4,0Fh
  jg @SlideUp1
  mov al,c1b4
  sub channel1vol,al
  jmp @NoVolSlide1
 @SlideUp1:
  mov al,c1b4
  shr al,4
  add channel1vol,al
 @NoVolSlide1:
  cmp bl,13
  jne @NoPatternBreak1
  mov PatternBreak,1
 @NoPatternBreak1:

  cmp bl,15
  jnz @NoSpeedSet1
  mov al,c1b4
  cmp al,aktspeed
  jnb @NoSpeedSet1
  mov aktspeed,al
 @NoSpeedSet1:    { ---- }
  cmp flag1,0
  jz @NextInst1

  mov si,offset SampleRepS
  mov bx,channel1inst
  shl bx,1
  add si,bx
  lodsw
  mov Channel1RepS,ax
  mov si,offset SampleRepL
  add si,bx
  lodsw
  mov Channel1RepL,ax
  mov bx,Channel1RepS
  add ax,bx
  mov Channel1RepLim,ax

  mov ah,c1b1
  mov al,c1b2
  and ah,0Fh
  cmp ax,0              {!!!!!!!!!}
  jz @NextInst1
  mov Channel1Note,ax
  mov channel1VK,1
  mov channel1NK,0


  mov ax,Channel1Inst   { Sample-Segs/Ofs ausrechnen }
  dec ax
  shl ax,2
  mov si,offset Samples
  add si,ax
  lodsw
  mov Channel1Ofs,ax
  lodsw
  mov Channel1Seg,ax

  mov ax,Channel1Note   { Speedkonstanten ausrechnen }
  shl ax,1
  mov si,offset SpeedListVK
  add si,ax
  lodsw
  mov Channel1SpVK,ax

  mov ax,Channel1Note
  shl ax,1
  mov si,offset SpeedListNK
  add si,ax
  lodsw
  mov Channel1SpNK,ax

 @NextInst1:         { -- Ready! -- }

 @Channel2:
  mov flag1,1
  mov ah,c2b1
  and ah,0F0h
  mov al,c2b3
  shr al,4
  add al,ah
  cmp al,0
  jnz @OK2
  mov al,c2b3
  and al,0Fh
  cmp al,0
  jz @NextInst2
  mov flag1,0
  jmp @Effects2
 @OK2:

  xor ah,ah
  mov Channel2Rep,0
  push ax
  mov si,offset SampleVols
  add si,ax
  lodsb
  mov Channel2Vol,al
  pop ax
  mov Channel2Inst,ax
  dec ax
  shl ax,1
  mov si,offset SampleSizes
  add si,ax
  lodsw
  dec ax
  mov Channel2Size,ax

 @Effects2:
  mov bl,c2b3   { auf Effekte berprfen }
  and bl,0Fh
  cmp bl,12
  jnz @NoVolSet2
  mov al,c2b4
  mov channel2vol,al
  jmp @VolSet2
 @NoVolSet2:
 @VolSet2:
  cmp bl,10     { Vol-Slide }
  jnz @NoVolSlide2
  cmp c2b4,0Fh
  jg @SlideUp2
  mov al,c2b4
  sub channel2vol,al
  jmp @NoVolSlide2
 @SlideUp2:
  mov al,c2b4
  shr al,4
  add channel2vol,al
 @NoVolSlide2:

  cmp bl,13
  jne @NoPatternBreak2
  mov PatternBreak,1
 @NoPatternBreak2:

  cmp bl,15
  jnz @NoSpeedSet2
  mov al,c2b4
  cmp al,aktspeed
  jnb @NoSpeedSet2
  mov aktspeed,al
 @NoSpeedSet2:    { ---- }
  cmp flag1,0
  jz @NextInst2
  mov si,offset SampleRepS
  mov bx,channel2inst
  shl bx,1
  add si,bx
  lodsw
  mov Channel2RepS,ax
  mov si,offset SampleRepL
  add si,bx
  lodsw
  mov Channel2RepL,ax
  mov bx,Channel2RepS
  add ax,bx
  mov Channel2RepLim,ax

  mov ah,c2b1
  mov al,c2b2
  and ah,0Fh
  cmp ax,0              {!!!!!!!!!}
  jz @NextInst2
  mov Channel2Note,ax
  mov channel2VK,1
  mov channel2NK,0

  mov ax,Channel2Inst   { Sample-Segs/Ofs ausrechnen }
  dec ax
  shl ax,2
  mov si,offset Samples
  add si,ax
  lodsw
  mov Channel2Ofs,ax
  lodsw
  mov Channel2Seg,ax

  mov ax,Channel2Note   { Speedkonstanten ausrechnen }
  shl ax,1
  mov si,offset SpeedListVK
  add si,ax
  lodsw
  mov Channel2SpVK,ax

  mov ax,Channel2Note
  shl ax,1
  mov si,offset SpeedListNK
  add si,ax
  lodsw
  mov Channel2SpNK,ax
 @NextInst2:

 @Channel3:
  mov flag1,1
  mov ah,c3b1
  and ah,0F0h
  mov al,c3b3
  shr al,4
  add al,ah
  cmp al,0
  jnz @OK3
  mov al,c3b3
  and al,0Fh
  cmp al,0
  jz @NextInst3
  mov flag1,0
  jmp @Effects3
 @OK3:

  xor ah,ah
  mov Channel3Rep,0
  push ax
  mov si,offset SampleVols
  add si,ax
  lodsb
  mov Channel3Vol,al
  pop ax
  mov Channel3Inst,ax
  dec ax
  shl ax,1
  mov si,offset SampleSizes
  add si,ax
  lodsw
  dec ax
  mov Channel3Size,ax

 @Effects3:
  mov bl,c3b3   { auf Effekte berprfen }
  and bl,0Fh
  cmp bl,12
  jnz @NoVolSet3
  mov al,c3b4
  mov channel3vol,al
  jmp @VolSet3
 @NoVolSet3:
 @VolSet3:
  cmp bl,10     { Vol-Slide }
  jnz @NoVolSlide3
  cmp c3b4,0Fh
  jg @SlideUp3
  mov al,c3b4
  sub channel3vol,al
  jmp @NoVolSlide3
 @SlideUp3:
  mov al,c3b4
  shr al,4
  add channel3vol,al
 @NoVolSlide3:
  cmp bl,13
  jne @NoPatternBreak3
  mov PatternBreak,1
 @NoPatternBreak3:

  cmp bl,15
  jnz @NoSpeedSet3
  mov al,c3b4
  cmp al,aktspeed
  jnb @NoSpeedSet3
  mov aktspeed,al
 @NoSpeedSet3:    { ---- }
  cmp flag1,0
  jz @NextInst3
  mov si,offset SampleRepS
  mov bx,channel3inst
  shl bx,1
  add si,bx
  lodsw
  mov Channel3RepS,ax
  mov si,offset SampleRepL
  add si,bx
  lodsw
  mov Channel3RepL,ax
  mov bx,Channel3RepS
  add ax,bx
  mov Channel3RepLim,ax

  mov ah,c3b1
  mov al,c3b2
  and ah,0Fh
  cmp ax,0              {!!!!!!!!!}
  jz @NextInst3
  mov Channel3Note,ax
  mov channel3VK,1
  mov channel3NK,0

  mov ax,Channel3Inst   { Sample-Segs/Ofs ausrechnen }
  dec ax
  shl ax,2
  mov si,offset Samples
  add si,ax
  lodsw
  mov Channel3Ofs,ax
  lodsw
  mov Channel3Seg,ax

  mov ax,Channel3Note   { Speedkonstanten ausrechnen }
  shl ax,1
  mov si,offset SpeedListVK
  add si,ax
  lodsw
  mov Channel3SpVK,ax

  mov ax,Channel3Note
  shl ax,1
  mov si,offset SpeedListNK
  add si,ax
  lodsw
  mov Channel3SpNK,ax

 @NextInst3:

 @Channel4:
  mov flag1,1
  mov ah,c4b1
  and ah,0F0h
  mov al,c4b3
  shr al,4
  add al,ah
  cmp al,0
  jnz @OK4
  mov al,c4b3
  and al,0Fh
  cmp al,0
  jz @NextInst4
  mov flag1,0
  jmp @Effects4
 @OK4:

  xor ah,ah
  mov Channel4Rep,0
  push ax
  mov si,offset SampleVols
  add si,ax
  lodsb
  mov Channel4Vol,al
  pop ax
  mov Channel4Inst,ax
  dec ax
  shl ax,1
  mov si,offset SampleSizes
  add si,ax
  lodsw
  dec ax
  mov Channel4Size,ax

 @Effects4:
  mov bl,c4b3   { auf Effekte berprfen }
  and bl,0Fh
  cmp bl,12
  jnz @NoVolSet4
  mov al,c4b4
  mov channel4vol,al
  jmp @VolSet4
 @NoVolSet4:
 @VolSet4:
  cmp bl,10     { Vol-Slide }
  jnz @NoVolSlide4
  cmp c4b4,0Fh
  jg @SlideUp4
  mov al,c4b4
  sub channel4vol,al
  jmp @NoVolSlide4
 @SlideUp4:
  mov al,c4b4
  shr al,4
  add channel4vol,al
 @NoVolSlide4:

  cmp bl,13
  jne @NoPatternBreak4
  mov PatternBreak,1
 @NoPatternBreak4:

  cmp bl,15
  jnz @NoSpeedSet4
  mov al,c4b4
  cmp al,aktspeed
  jnb @NoSpeedSet4
  mov aktspeed,al

 @NoSpeedSet4:    { ---- }
  cmp flag1,0
  jz @NextInst4
  mov si,offset SampleRepS
  mov bx,channel4inst
  shl bx,1
  add si,bx
  lodsw
  mov Channel4RepS,ax
  mov si,offset SampleRepL
  add si,bx
  lodsw
  mov Channel4RepL,ax
  mov bx,Channel4RepS
  add ax,bx
  mov Channel4RepLim,ax

  mov ah,c4b1
  mov al,c4b2
  and ah,0Fh
  cmp ax,0              {!!!!!!!!!}
  jz @NextInst4
  mov Channel4Note,ax
  mov channel4VK,1
  mov channel4NK,0

  mov ax,Channel4Inst   { Sample-Segs/Ofs ausrechnen }
  mov si,offset SpeedListVK
  dec ax
  shl ax,2
  mov si,offset Samples
  add si,ax
  lodsw
  mov Channel4Ofs,ax
  lodsw
  mov Channel4Seg,ax

  mov ax,Channel4Note   { Speedkonstanten ausrechnen }
  shl ax,1
  mov si,offset SpeedListVK
  add si,ax
  lodsw
  mov Channel4SpVK,ax

  mov ax,Channel4Note
  shl ax,1
  mov si,offset SpeedListNK
  add si,ax
  lodsw
  mov Channel4SpNK,ax

 @NextInst4:

 cmp aktspeed,0FFh
 je @NoSpeedSet
  mov al,aktspeed
  mov speed,al
  xor bh,bh
  mov ax,freqDiv50
  mov bl,speed
  mul bx
  mov bufferlim,ax

@NoSpeedSet:

 cmp Channel1Vol,65
 jb @Vol1OK
 mov Channel1Vol,0
@Vol1OK:
 cmp Channel2Vol,65
 jb @Vol2OK
 mov Channel2Vol,0
@Vol2OK:
 cmp Channel3Vol,65
 jb @Vol3OK
 mov Channel3Vol,0
@Vol3OK:
 cmp Channel4Vol,65
 jb @Vol4OK
 mov Channel4Vol,0
@Vol4OK:

end;


procedure Mix; assembler;
asm
  mov channel1bpos,0
  mov channel2bpos,0
  mov channel3bpos,0
  mov channel4bpos,0

  mov ctr,-1
  mov cx,buffersize
 @BigLoop:
  push cx
  inc ctr
  inc bufferctr
  mov ax,bufferctr
  cmp ax,bufferlim
  jz @NewNote
  jmp @IncVars
 @NewNote:
  mov bufferctr,0
  call GetNotes
  mov al,totPatterns
  mov bl,patternnum
  cmp bl,al
  jbe @IncVars
  mov ctr,-1
  mov StopPlayer,1
  jmp @ToBigLoop
 @IncVars:
  call IncPositions
 call AddSamples

 mov di,DataOfs
 mov es,DataSeg
 add di,ctr

 mov ax,additval
 mov cl,MixAnz
 div cl
 stosb

@ToBigLoop:
 pop cx
 loop @BigLoop
end;

{ -------------------------------------------------------------------------
  -------------------------------------------------------------------------
  ------------------------------------------------------------------------- }

procedure IRQ7Sperren;
var b: byte;
begin
 b := port[$21];
 port[$21] := b or 32;
end;

procedure IRQ7Freigeben;
var b: byte;
begin
 b := port[$21];
 port[$21] := b and (not 32);
end;

procedure Enable; assembler;
asm
 sti;
end;

procedure Disable; assembler;
asm
 cli;
end;

procedure SetOldInt15;
begin
 SetIntVec(IntNo,OldInt15);
end;

procedure SetAddrs;
begin
 if Usebuffer = 0 then begin
  addr := (Longint(Seg(Buffer2^)) shl 4)+Ofs(Buffer2^);
  DataSeg := Seg(Buffer2^);
  DataOfs := Ofs(Buffer2^);
  Data := Buffer2;
 end
 else begin
  addr := (Longint(Seg(Buffer1^)) shl 4)+Ofs(Buffer1^);
  DataSeg := Seg(Buffer1^);
  DataOfs := Ofs(buffer1^);
  Data := buffer1;
 end;
 t1 := addr and $FFFF;
 t2 := addr shr 16;
end;

procedure NewInt15; interrupt;
var b: byte;
    addr: longint;
    t: word;
    dummy: Pointer;
begin
 b := port[$22E];
 port[$20] := $20;

 Inc(PeakCtr);
 if StopPlayer then begin
  IRQ7Sperren;
  Disable;
  SetIntVec(13,OldInt15);
  Enable;
  IRQ7Freigeben;
  ReadyForExit := true;
  Exit;
 end;
 ReadyForExit := false;
 if UseBuffer = 0 then UseBuffer := BufferSize
 else UseBuffer := 0;

 addr := (Longint(Seg(data^)) shl 4 ) + UseBuffer;
 port[$0A] := $05;
 port[$0C] := 0;
 port[$0B] := $49;
 t := addr and $FFFF;
 port[$02] := t and $FF;
 port[$02] := t shr 8;
 t := addr shr 16;
 port[$83] := t;
 port[$03] := (BufferSize-1) and $FF;
 port[$03] := (BufferSize-1) shr 8;
 port[$0A] := $01;

 schreib_dsp($14);
 schreib_dsp((BufferSize-1) and $FF);
 schreib_dsp((BufferSize-1) shr 8);

 if UseBuffer = 0 then begin
  DataSeg := Seg(buffer2^);
  DataOfs := Ofs(buffer2^);
 end
 else begin
  DataSeg := Seg(buffer1^);
  DataOfs := Ofs(buffer1^);
 end;
 Mix;
 OsciPos := 0;
end;

procedure StartMOD_DMA;
var addr: longint;
    t: word;
begin
 UseBuffer := 0;
 ReadyForExit := false;
 StopPlayer := false;
 DataSeg := Seg(Buffer1^);
 DataOfs := Ofs(Buffer1^);
 PatternNum := 1;
 Mix;
 addr := (Longint(Seg(Data^)) shl 4 ) + UseBuffer;
 IRQ7Sperren;
 Disable;
 GetIntVec(13,OldInt15);
 SetIntVec(13,@NewInt15);
 schreib_dsp($40);
 schreib_dsp(256-(1000000 div Freq));
 port[$0A] := $05;
 port[$0C] := 0;
 port[$0B] := $49;
 t := addr and $FFFF;
 port[$02] := t and $FF;
 port[$02] := t shr 8;
 t := addr shr 16;
 port[$83] := t;
 port[$03] := (BufferSize-1) and $FF;
 port[$03] := (BufferSize-1) shr 8;
 port[$0A] := $01;
 Enable;
 IRQ7Freigeben;
 schreib_dsp($14);
 schreib_dsp((BufferSize-1) and $FF);
 schreib_dsp((BufferSize-1) shr 8);

 if UseBuffer = 0 then begin
  DataSeg := Seg(buffer2^);
  DataOfs := Ofs(buffer2^);
 end
 else begin
  DataSeg := Seg(buffer1^);
  DataOfs := Ofs(buffer1^);
 end;
 Mix;
end;

procedure StopMOD;
begin
 StopPlayer := true;
end;

function MODPlaying:boolean;
begin
 MODPlaying := ReadyForExit = false;
end;

{ --------------------- Bildschirmroutinen ------------------------
  (sind nicht fr den Player notwendig)
}

procedure BildschirmMaske;
var s1,s2,s3: string;

 procedure WriteInstrTab;
 var i: byte;
     color,color2: byte;
 begin
  TextBackGround(Black);
  color := Cyan;
  color2 := Darkgray;
  for i := 1 to 16 do begin
   GotoXY(5,8+i);
   if SampleSizes[i] > 1 then
    TextColor(Color)
   else TextColor(Color2);
   write('#',i,': ',SampleNames[i]);
  end;
  for i := 1 to 15 do begin
   GotoXY(35,8+i);
   if SampleSizes[i+16] > 1 then
    TextColor(Color)
   else TextColor(Color2);
   write('#',i+16,': ',SampleNames[i+16]);
  end;
  TextColor(LightGray);
 end;

begin
 if (not grMode) then begin
  TextColor(LightGray); TextBackGround(black);
  ClrScr;
  TextColor(Yellow); TextBackGround(Blue);
  FSplit(FileName,s1,s2,s3);
  write('MODPlay v1.0      by Daniel Lichtenberger                           ',s2+s3:12);
  TextColor(Cyan); TextBackGround(Black);
  Rahmen(2,3,45,8,Cyan,EinfachLin,EinfachLin,EinfachLin,EinfachLin);
  TextColor(LightGray); TextBackGround(Black);
  GotoXY(50,7);
  write('Samplefrequenz: ',freq/1000:2:1,' khz');
  GotoXY(50,6);
  write('Speed:   /50 Sek/Note');
  GotoXY(50,4);
  write('Pattern # ');
  GotoXY(50,5);
  write('Akt. Notenzeile:');
  WriteInstrTab;
 end
 else begin
  gd := Detect;
  InitGraph(gd,gm,'C:\BP\BGI');
  SetColor(Lightgray);
  Rectangle(149,300-100,150+321,300+100);
 end;
 OldLine := 255;
 FillChar(OldOsci,640,0);
 FillChar(ChannelOsciPos,SizeOf(ChannelOsciPos),0);
 OldDrwnBuf := 0;
 OsciPos := 0;
end;

procedure WriteInstrBox;
var i:byte;
begin
 if WriteInstrs then begin
  GotoXY(3,4);
  if Channel1Inst > 0 then
   write('#',Channel1Inst,': ',SampleNames[Channel1Inst])
  else write('                          ');

  GotoXY(3,5);
  if Channel2Inst > 0 then
   write('#',Channel2Inst,': ',SampleNames[Channel2Inst])
  else write('                          ');

  GotoXY(3,6);
  if Channel3Inst > 0 then
   write('#',Channel3Inst,': ',SampleNames[Channel3Inst])
  else write('                          ');

  GotoXY(3,7);
  if Channel4Inst > 0 then
   write('#',Channel4Inst,': ',SampleNames[Channel4Inst])
  else write('                          ');
 end
 else
 begin;
  GotoXY(3,4);
  if Channel1Inst > 0 then
   write('#',Channel1Inst)
  else write('                          ');

  GotoXY(3,5);
  if Channel2Inst > 0 then
   write('#',Channel2Inst)
  else write('                          ');

  GotoXY(3,6);
  if Channel3Inst > 0 then
   write('#',Channel3Inst)
  else write('                          ');

  GotoXY(3,7);
  if Channel4Inst > 0 then
   write('#',Channel4Inst)
  else write('                          ');
 end;
end;

procedure WriteNotes;
var i:byte;
begin
 GotoXY(3,10);
 Delline;
 if AktLine = 1 then DelLine;
 GotoXY(3,23);

 with Patterns[AktPattern]^[AktLine] do
  write(NoteName((Channel1[1] AND $0F)*256+(Channel1[2])),' (',((Channel1[1] AND $F0)+(Channel1[3] SHR 4)):2,')  ³ ',
        NoteName((Channel2[1] AND $0F)*256+(Channel2[2])),' (',((Channel2[1] AND $F0)+(Channel2[3] SHR 4)):2,')  ³ ',
        NoteName((Channel3[1] AND $0F)*256+(Channel3[2])),' (',((Channel3[1] AND $F0)+(Channel3[3] SHR 4)):2,')  ³ ',
        NoteName((Channel4[1] AND $0F)*256+(Channel4[2])),' (',((Channel4[1] AND $F0)+(Channel4[3] SHR 4)):2,') ');
end;

procedure DrawPeak;
var i:byte;
    PeakChar: char;
begin;
  PeakChar := #22;
  if Channel1Peak >= 2 then Dec(Channel1Peak,2)
   else Channel1Peak := 0;
  if Channel2Peak >= 2 then Dec(Channel2Peak,2)
   else Channel2Peak := 0;
  if Channel3Peak >= 2 then Dec(Channel3Peak,2)
   else Channel3Peak := 0;
  if Channel4Peak >= 2 then Dec(Channel4Peak,2)
   else Channel4Peak := 0;
 if AktLine <> OldLine2 then
 with Patterns[Aktpattern]^[AktLine] do begin
  OldLine2 := AktLine;
  if (Channel1[1] AND $F0)+(Channel1[3] SHR 4) <> 0 then
   Channel1Peak := PeakLimit;
  if (Channel2[1] AND $F0)+(Channel2[3] SHR 4) <> 0 then
   Channel2Peak := PeakLimit;
  if (Channel3[1] AND $F0)+(Channel3[3] SHR 4) <> 0 then
   Channel3Peak := PeakLimit;
  if (Channel4[1] AND $F0)+(Channel4[3] SHR 4) <> 0 then
   Channel4Peak := PeakLimit;
 end;

 GotoXY(31,4);
 if Channel1Peak > 0 then begin
 TextColor(Green);
 for i := 1 to Channel1Peak-1 do write(PeakChar);
  TextColor(Red);
  write(PeakChar);
 end;
 for i := Channel1Peak to PeakLimit do write(' ');

 GotoXY(31,5);
 if Channel2Peak > 0 then begin
 TextColor(Green);
 for i := 1 to Channel2Peak-1 do write(PeakChar);
  TextColor(Red);
  write(PeakChar);
 end;
 for i := Channel2Peak to PeakLimit do write(' ');

 GotoXY(31,6);
 if Channel3Peak > 0 then begin
 TextColor(Green);
 for i := 1 to Channel3Peak-1 do write(PeakChar);
  TextColor(Red);
  write(PeakChar);
 end;
 for i := Channel3Peak to PeakLimit do write(' ');

 GotoXY(31,7);
 if Channel4Peak > 0 then begin
 TextColor(Green);
 for i := 1 to Channel4Peak-1 do write(PeakChar);
  TextColor(Red);
  write(PeakChar);
 end;
 for i := Channel4Peak to PeakLimit do write(' ');

 TextColor(Lightgray);
end;

procedure WriteAktPattern;
begin
 GotoXY(59,4);
 write(Patternnum,' (',aktpattern,')       ');
 GotoXY(67,5);
 write(AktLine,'  ')
end;

procedure put(x,y:integer;farbe:byte); assembler;

asm
 mov ax,80
 mul y
 mov bx,ax
 mov ax,x
 mov cl,8
 div cl
 mov cl,ah
 and ah,0
 add bx,ax
 or ah,128
 shr ah,cl
 mov dx,3CEh
 mov al,8
 out dx,ax
 mov ax,285h
 out dx,ax
 mov ax,0A000h
 mov es,ax
 mov al,es:[bx]
 mov al,farbe
 mov es:[bx],al
end;

procedure ScrType1;
 procedure DrawChannelOscis;
 var i,k:integer;
 begin
  SetViewPort(10,10,150+3*140,120,true);
  ClearViewPort;
  for k := 1 to 4 do begin
   SetViewPort(10+140*(k-1),10,150+140*(k-1),120,true);
{   for i := 1 to 140 do begin
    if oldchannelosci[k,i*2] >= medc[k] then
     PutPixel(i,60+(oldchannelosci[k,i*2]-medc[k]),black)
    else PutPixel(i,60-(medc[k]-oldchannelosci[k,i*2]),black);
   end;}


   Move(Mem[Seg(channelbufs[k]^):Ofs(channelbufs[k]^)+channeloscipos[k]],oldchannelosci[k],280);
   maxc[k] := 0; minc[k] := $FF; totc[k] := 0;
   for i := 1 to 140 do begin
    oldchannelosci[k,i*2] := oldchannelosci[k,i*2] shr 2;
    if oldchannelosci[k,i*2] > maxc[k] then maxc[k] := oldchannelosci[k,i*2]
    else if oldchannelosci[k,i*2] < minc[k] then minc[k] := oldchannelosci[k,i*2];
    Inc(totc[k],oldchannelosci[k,i*2]);
   end;
   medc[k] := (totc[k] div 140)-minc[k];
    for i := 1 to 140 do
     Dec(oldchannelosci[k,i*2],minc[k]);

    for i := 1 to 140 do begin
     if oldchannelosci[k,i*2] >= medc[k] then
      Put(i,60+(oldchannelosci[k,i*2]-medc[k]),green)
     else Put(i,60-(medc[k]-oldchannelosci[k,i*2]),green);
    end;
   Inc(channelOsciPos[k],880);
   if channelOsciPos[k] > (BufferSize-320) then channelOsciPos[k] := 0;
   if aktline <> oldline then begin
    channelOsciPos[k] := 0;
   end;
  end;
 end;

 procedure DrawOscis;
 var i: integer;
     tot: longint;
 begin
 { DrawChannelOscis;
  SetViewPort(150,300-99,150+320,300+99,true);}
  for i := 1 to 320 do begin
   if oldosci[i*2] >= med then
    Put(150+i,201+100+(oldosci[i*2]-med),black)
   else Put(150+i,201+100-(med-oldosci[i*2]),black);
  end;

  if useBuffer = 0 then
   Move(Mem[Seg(buffer1^):Ofs(buffer1^)+oscipos],oldosci,640)
  else Move(Mem[Seg(buffer2^):Ofs(buffer2^)+oscipos],oldosci,640);
  max := 0; min := $FF; tot := 0;
  for i := 1 to 640 do begin
   if oldosci[i] > max then max := oldosci[i]
   else if oldosci[i] < min then min := oldosci[i];
   Inc(tot,oldosci[i]);
  end;
{  asm
   mov si,offset oldosci
   mov cx,639
   xor ah,ah
  @MaxMinLoop:
   lodsb
   add word ptr tot,ax
   cmp al,max
   jg @NewMax
   cmp al,min
   jb @NewMin
   loop @MaxMinLoop
   jmp @Ende
  @NewMax:
   mov max,al
   loop @MaxMinLoop
   jmp @Ende
  @NewMin:
   mov min,al
   loop @MaxMinLoop
 @Ende:
  end;}
  med := (tot div 640)-min;
  for i := 1 to 640 do
   Dec(oldosci[i],min);
  for i := 1 to 320 do begin
   if oldosci[i*2] >= med then
    Put(150+i,201+100+(oldosci[i*2]-med),green)
   else Put(150+i,201+100-(med-oldosci[i*2]),green);
  end;
  Inc(OsciPos,880);
  if OsciPos > (BufferSize-640) then OsciPos := 0;
{  SetViewPort(0,0,639,479,true);}
 end;

 procedure DrawInstr;
 begin
  SetFillStyle(SolidFill,Black);
  Bar(0,0,200,50);
  SetColor(LightBlue);
  if Channel1Inst > 0 then
   OutTextXY(10,5,SampleNames[Channel1Inst]);
  if Channel2Inst > 0 then
   OutTextXY(10,15,SampleNames[Channel2Inst]);
  if Channel3Inst > 0 then
   OutTextXY(10,25,SampleNames[Channel3Inst]);
  if Channel4Inst > 0 then
   OutTextXY(10,35,SampleNames[Channel4Inst]);
 end;

begin
 if (not GrMode) then begin
  if peakCtr <> OPeakCtr then begin
   OPeakCtr := PeakCtr;
   DrawPeak;
  end;
  if AktLine = OldLine then exit;
  OldLine := AktLine;
  WriteInstrBox;
 { WriteNotes;}
  WriteAktPattern;
  GotoXY(57,6);
  write(Speed:2);
 { writeln(Channel1VK,'/si',Channel1Size,'/rpl',Channel1RepL,'/svk',Channel1SpVK,'/not',Channel1Note,'          ');
  writeln(Channel2VK,'/si',Channel2Size,'/rpl',Channel2RepL,'/svk',Channel2SpVK,'/not',Channel2Note,'          ');
  writeln(Channel3VK,'/si',Channel3Size,'/rpl',Channel3RepL,'/svk',Channel3SpVK,'/not',Channel3Note,'          ');
  writeln(Channel4VK,'/si',Channel4Size,'/rpl',Channel4RepL,'/svk',Channel4SpVK,'/not',Channel4Note,'          ');}
 end
 else {if aktline <> oldline then} begin
   DrawOscis;
   if aktline <> oldline then begin
    DrawInstr;
    oldline := aktline;
    OsciPos := 0;
   end;
 end;
end;

procedure ChainInt(Oldint:pointer);
inline($5B/$58/$89/$EC/$87/$46/$10/$87/$5E/$0E/$5D/$07/$1F/$5F/$5E/
       $5A/$59/$CB);

procedure Int1C; interrupt;
begin
 ScrType1;
{ ChainInt(OldInt1C);}
 port[$20] := $20;
end;

procedure InitInt1C;
var tmr: word;
begin
 tmr := longint(1093180) div longint(50);
 asm cli end;
 port[$40] := tmr and $FF;
 port[$40] := tmr shr 8;
 asm sti end;
 GetIntVec(8,OldInt1C);
 SetIntVec(8,@Int1C);
end;

begin
 if ParamCount < 1 then begin
  writeln('Keine Dateinamen angegeben');
  halt;
 end;
 IntNo := 13;
 if ParamCount >= 2 then
  Val(ParamStr(2),Freq,result)
 else Freq := 15000;
 if ParamCount >= 3 then begin
  Val(ParamStr(3),IntNo,result);
  Inc(IntNo,8);
 end;
 WriteInstrs := ParamStr(3) <> ' /W';
 BufferSize := 5120;
 FreqDiv50 := Freq div 50;
 Speed := 6;
 BufferLim := (freq div 50)*Speed;
 BufferCtr := BufferLim-1;
 OldPattern := $FF;
 writeln('Initialisiere Arrays...');
 InitChannelVars;
 InitSpeedlist;
 InitOctArray;
 GetMem(buffer1,buffersize);
 GetMem(buffer2,buffersize);
{ GetMem(channel1buf,buffersize div 1);
 GetMem(channel2buf,buffersize div 1);
 GetMem(channel3buf,buffersize div 1);
 GetMem(channel4buf,buffersize div 1);
 channelbufs[1] := channel1buf; channelbufs[2] := channel2buf;
 channelbufs[3] := channel3buf; channelbufs[4] := channel4buf;
 Channel1BSeg := Seg(Channel1Buf^); Channel2BSeg := Seg(Channel2Buf^);
 Channel3BSeg := Seg(Channel3Buf^); Channel4BSeg := Seg(Channel4Buf^);
 for i := 1 to 4 do Fillchar(channelbufs[i]^,buffersize,0);}
 DataSeg := Seg(Buffer1^);
 DataOfs := Ofs(Buffer1^);
 Data := Ptr(Seg(buffer1^),Ofs(Buffer1^));
{ GetMem(data,buffersize*2);
 buffer1 := Ptr(Seg(data^),0);
 buffer2 := Ptr(Seg(data^),bufferSize);}
 FillChar(buffer1^,buffersize,0);
 FillChar(buffer2^,buffersize,0);
{ FreeMem(data,bufferSize*2);}

 init_dsp;
 FileName := StrUpCase(ParamStr(1));
 Assign(ModF, ParamStr(1));
 writeln('SB initialisieren...');
 schreib_dsp($0D1);                   (* Speaker an                  *)
 writeln('Lade MOD...   (freier Speicher: ',MemAvail div 1024, ' KB)');
 ReadMODFile;
 AktPattern := Arrangement[1]+1;
 AktLine := 0;
 KillCursor;
 i := 0;
 grMode := ParamStr(3) <> 'T';
 BildSchirmMaske;
 PeakLimit := 13;
 Channel1Peak := 0; Channel2Peak := 0; Channel3Peak := 0; Channel4Peak := 0;
 PeakCtr := 0; OPeakCtr := 255; PeakCtr2 := 0;
 OldLine2 := 255;
 DirectVideo := true;
{ GetIntVec($1C,OldInt1C);
 SetIntVec($1C,@Int1C);}
 StartMOD_DMA;
 InitInt1C;
 repeat
  if keypressed then begin
   StopMOD;
   repeat until not MODPlaying;
  end;
{  ScrType1;}

  Inc(i);
 until not MODPlaying;
 RestoreCursor;
 schreib_dsp($0D3);                   (* Speaker aus                 *)
 init_dsp;
 while keypressed do readkey;
 SetIntVec(8,OldInt1C);
 port[$40] := 0;
 port[$40] := 0;
 ClrScr;
 {$I-} CloseGraph; {$I+}
end.