Aula Macedonia


Curso de Programación Multimedia Bajo DOS


Artículo realizado por
José Antonio Suárez.





Código fuente para los capítulos 10,11 y 12.


VOC.H
 

///////////////////////
// Variables globales //
///////////////////////

WORD BPORT=0x210; WORD XPORT=0x216; WORD WPORT=0x21c;
WORD RPORT=0x21a; WORD APORT=0x21e; BYTE READY=0xaa;
WORD longitud=0;  WORD alojado;     WORD frecuencia;

// Rutinas de E/S

   // puerto,valor
void EscribeEnPuerto(WORD,BYTE);
   // puerto
BYTE LeeDePuerto(WORD);
   // valor
void EscribeEnDSP(BYTE);
 

// Rutinas de la Sound Blaster
void ResetearSB(void);
BOOL DetectarSB(void);
void ErrorSB(void);
void SpeakerOn(void);
void SpeakerOff(void);

// Rutinas de los ficheros .VOC y .SMP
void CargaVoc(char [12]);
void CargaSmp(char [12]);
// frecuencia
void VocOn(WORD);
 
 
 
 
 
 

VOC.C

#include "voc.h"

void EscribeEnPuerto(WORD puerto,BYTE valor)
{
 asm{
  mov al,valor
  mov dx,puerto
  out dx,al
 }
}

BYTE LeeDePuerto(WORD puerto)
{
 BYTE result;
 asm{

  mov dx,puerto
  in al,dx
  mov byte ptr result,al

 }
 return result;
}

void EscribeEnDSP(BYTE valor)
{
 test1:
 asm{
  mov dx,WPORT
  in al,dx
  and al,0x80
  cmp al,0
  jnz test1
  mov al,byte ptr valor
  out dx,al
 }
}

void ResetearSB(void)
{
 EscribeEnPuerto(XPORT,1);
 delay(5);
 EscribeEnPuerto(XPORT,0);
}

// Si se encuentra la Sound Blaster, devuelve un 1. Un 0 si no se encuentra
BOOL DetectarSB(void)
{
 BYTE estado,cont;

 estado=0;
 while ((estado!=READY)&&(BPORT<0x270))
 {
  // Reseteo de la Sound Blaster
  EscribeEnPuerto(XPORT,1);
  // Como m¡nimo hay que esperar 3,3 milisegundos.5 para asegurarnos
  delay(5);
  EscribeEnPuerto(XPORT,0);
  cont=0;
  while ((estado!=READY)&&(cont<100))
  {
 estado=LeeDePuerto(RPORT);
 cont++;
  }
  // Se buscan los puertos de la Sound Blaster
  if (estado!=READY)
  {
 BPORT+=0x10;
 XPORT+=0x10;
 WPORT+=0x10;
 RPORT+=0x10;
 APORT+=0x10;
  }
 }
 if (BPORT!=0x270)
 {
  while ((LeeDePuerto(WPORT)&128)!=0);
  EscribeEnPuerto(WPORT,0xd1);
  // Activa la salida digital mediante el DAC
  SpeakerOn();

  return (1);
 }
 return (0);
}

void SpeakerOn(void)
{
 EscribeEnDSP(0xd1);
}

void SpeakerOff(void)
{
 EscribeEnDSP(0xd3);
}

void ErrorSB(void)
{
 printf("\nNo se ha encontrado la Sound Blaster\n");
 exit (0);
}

void CargaVoc(char nombre[12])
{
 FILE *ficherovoc;

 ficherovoc=fopen(nombre,"rb");
 if (!ficherovoc)
 {
  printf("\nNo se encuentra el fichero %s",nombre);
  exit (0);
 }
 fseek(ficherovoc,0L,SEEK_END);
 // 32:20 de cabecera global,12 de cabecera de bloque.100 para evitar el
 // chasquido del final
 longitud=ftell(ficherovoc)-32-100;
 fseek(ficherovoc,30,0);
 frecuencia=getc(ficherovoc);
 frecuencia=1000000L/(256l-(frecuencia));

 fseek(ficherovoc,32L,SEEK_SET);
 fread(zona5,longitud,1,ficherovoc);
 fclose(ficherovoc);
}

void VocOn(WORD voc)
{
 WORD dmaoffset,pagina,segmento,desplazamiento;
 BYTE tamanoalta,tamanobaja,canaldma=1;
 WORD tiempo;

 segmento=FP_SEG(zona5);
 desplazamiento=FP_OFF(zona5);
 asm{
  // Transforma la direcci¢n
  mov ax,word ptr segmento
  mov cl,4
  shl ax,cl
  add ax,word ptr desplazamiento
  mov word ptr dmaoffset,ax
  mov ax,word ptr desplazamiento
  mov cl,4
  shr ax,cl
  add ax,word ptr segmento
  mov cl,12
  shr ax,cl
  mov word ptr pagina,ax
  mov dx,0xa
  // Configura el DMA
  mov al,5
  out dx,al
  mov dx,0xc
  xor al,al
  out dx,al
  mov dx,0xb
  mov al,0x49
  out dx,al
  mov ax,word ptr dmaoffset
  mov dx,2
  out dx,al
  xchg ah,al
  out dx,al
  mov dx,0x83
  mov al,byte ptr pagina
  out dx,al
  mov dx,3
  mov ax,word ptr longitud
  // Se indica al DMA cu ntos bytes debe transmitir
  dec ax
  out dx,al
  xchg ah,al
  out dx,al
  mov dx,0xa
  mov al,byte ptr canaldma
  out dx,al
  // Habilita el canal de DMA
  mov bx,word ptr longitud
  mov byte ptr tamanobaja,bl
  mov byte ptr tamanoalta,bh
 }
 if (voc==0) tiempo=256-(1000000/8000);
 else tiempo=256-(1000000/voc);
 // Espera para que la SB se reajuste a lo indicado en Ensamblador, arriba
 delay(100);
 // Establece la frecuencia del DSP
 EscribeEnDSP(0x40);
 EscribeEnDSP(tiempo);
 EscribeEnDSP(0x14);
 // Empieza la transferencia
 EscribeEnDSP(tamanobaja);
 EscribeEnDSP(tamanoalta);
}
 
 

EJEMPLO DE USO

void main(void)
{

 if ((zona5=malloc(65535))==NULL) printf("\nNo hay memoria base suficiente");

 if (!DetectarSB())
 {
  exit (0);
 }
 printf("\nSound Blaster detectada e inicializada. Pulse una tecla para la reproducci¢n");
 getch();

 CargaVoc("1.voc");
 VocOn(0);
 getch();
 CargaVoc("1.smp");
 VocOn(5000);
 getch();
 CargaVoc("jd1.voc");
 VocOn(0);
 getch();
 CargaVoc("jd2.voc");
 VocOn(0);
 getch();
 CargaVoc("jd3.voc");
 VocOn(0);
 getch();
 CargaVoc("jd4.voc");
 VocOn(0);
 getch();

 free(zona5);
}
  SBMOD.H
 

// Detecta e inicializa la interrupción y el puerto DMA de la Sound Blaster.
//   Si no hay una tarjeta Sound Blaster instalada devuelve 0 (error), en
//   caso contrario devuelve -1.
extern int far SBMOD_DetectSB(void);
// Carga en memoria el módulo <fileName>.
//   Si no encuentra el fichero o no hay memoria suficiente para ubicarlo
//   devuelve 0 (error), en caso contrario devuelve -1.
extern int far SBMOD_LoadModule(char far *fileName);
// Descarga de la memoria el módulo actual.
extern void far SBMOD_FreeModule(void);
// Comienza la ejecución background (de fondo) del módulo actual.
extern void far SBMOD_Play(void);
// Detiene la ejecución del m¢dulo actual.
extern void far SBMOD_Stop(void);
 
 
 

CARGAMOD.ASM
 

SBMOD_FreeModule  EndP
 

ClearModInfo    Proc    Near

                pusha
                push    es
                mov     ax,ds
                mov     es,ax
                lea     di,[ModInfo]
                mov     cx,Size ModInfoRec
                cld
                xor     ax,ax
                rep     stosb
                pop     es
                popa
                ret

ClearModInfo    EndP
 

                End
 

SB.ASM

 
 

      DosSeg                          ; Ordenaci¢n de segmentos DOS

      Jumps                           ; Para resolver los saltos
                ; fuera de rango

      .Model  large,C                 ; Modelo de memoria Large de C

      .286                            ; Instrucciones 80286

      .Stack  0400h                   ; Pila de 1Kb
 

;--------------------------------------------------------------------------
;               Variables y procedimientos globales
;--------------------------------------------------------------------------

      Global  SBMOD_DetectSB:Proc
      Global  SBMOD_Play:Proc, SBMOD_Stop:Proc

      Global  SbPoll:Proc, StartPlaying:Proc, StopPlaying:Proc
      Global  SbAddr:Word, SbIrq:Word, DmaBuffer:Word
      Global  GetSamples:Proc, MixSpeed:Word

DmaBufSize      equ     2048
 

;--------------------------------------------------------------------------
;               Datos
;--------------------------------------------------------------------------

      .Data

SbAddr          dw      220h
SbIrq           dw      7

DmaFlag         dw      ?
DmaBuffer       dw      ?
DmaHandler      dd      ?
TimerHandler    dd      ?

DoubleBuffer    db      2*DmaBufSize dup (?)
 
 

;--------------------------------------------------------------------------
;               C¢digo
;--------------------------------------------------------------------------

      .Code

;--------------------------------------------------------------------------
; SbOut:  Env¡a un byte a la Sound Blaster
;  In:
;   DX -  Puerto de escritura de la Sound Blaster
;--------------------------------------------------------------------------

SbOut           Macro   Data
      Local   Esperar
Esperar:        in      al,dx
      or      al,al
      js      Esperar
      mov     al,Data
      out     dx,al
      EndM

;--------------------------------------------------------------------------
; SbIrqHandler:  Manejador de la interruci¢n de la Sound Blaster
;--------------------------------------------------------------------------

SbIrqHandler    Proc

      push    ax
      push    dx
      push    ds

      mov     ax,@Data
      mov     ds,ax

      mov     dx,[SbAddr]
      add     dx,0Eh
      in      al,dx

      sub     dx,02h
      SbOut   14h
      SbOut   0FFh
      SbOut   0FFh

      mov     al,20h
      out     20h,al

      pop     ds
      pop     dx
      pop     ax
      iret

SbIrqHandler    EndP
 
 

;--------------------------------------------------------------------------
; SbPoll:  Polling de la Sound Blaster
;--------------------------------------------------------------------------

SbPoll          Proc

      pusha
      push    ds
      push    es

      mov     ax,@Data
      mov     ds,ax

      in      al,03h
      mov     cl,al
      in      al,03h
      mov     ch,al

      mov     ax,[DmaFlag] ;Hacemos la primera parte del proceso
      test    ax,ax
      jne     SegundaParte

      cmp     cx,DmaBufSize/2
      jae     Fin
      mov     si,[DmaBuffer]
      mov     cx,DmaBufSize/2
      call    GetSamples Pascal,ds,si,cx
      inc     [DmaFlag]
      jmp     Fin

SegundaParte:   cmp     cx,DmaBufSize/2 ;Ahora la segunda
      jb      Fin
      mov     si,[DmaBuffer]
      mov     cx,DmaBufSize/2
      add     si,cx
      call    GetSamples Pascal,ds,si,cx
      dec     [DmaFlag]

Fin:            pop     es
      pop     ds
      popa
      iret     ;<==== IRET

SbPoll          EndP
 
 

;--------------------------------------------------------------------------
; SbInit: Inicializa el Driver de la Sound Blaster
;--------------------------------------------------------------------------

SBMOD_Play      Proc Far

      pusha
      push    ds
      push    es

      mov     ax,@Data
      mov     ds,ax

      mov     [MixSpeed],18000

      call    StartPlaying

      cli

      in      al,21h
      push    ax

      mov     al,11111111b
      out     21h,al

      mov     [DmaFlag],0     ;Establecemos el buffer
      mov     ax,offset DoubleBuffer
      mov     [DmaBuffer],ax
      mov     dx,ds
      mov     bx,dx
      shr     dh,4
      shl     bx,4
      add     ax,bx
      adc     dh,0
      mov     cx,ax
      neg     cx
      cmp     cx,DmaBufSize
      jae     PonerDma
      add     [DmaBuffer],cx
      add     ax,cx
      adc     dh,0

PonerDma:       mov     bx,ax
      mov     cx,DmaBufSize
      dec     cx

      mov     al,05h
      out     0Ah,al

      xor     al,al
      out     0Ch,al

      mov     al,bl
      out     02h,al
      mov     al,bh
      out     02h,al
      mov     al,dh
      out     83h,al

      mov     al,cl
      out     03h,al
      mov     al,ch
      out     03h,al

      mov     al,59h
      out     0Bh,al

      mov     al,01h
      out     0Ah,al

      mov     di,[DmaBuffer]   ; Se borra el buffer
      mov     cx,DmaBufSize
      mov     ax,ds
      mov     es,ax
      mov     al,80h
      cld
      rep     stosb

      xor     ax,ax            ; Se establece la IRQ
      mov     es,ax

      mov     bx,[SbIrq]
      add     bx,08h
      shl     bx,2

      mov     ax,es:[bx]
      mov     dx,es:[bx+2]
      mov     Word Ptr [DmaHandler],ax
      mov     Word Ptr [DmaHandler+2],dx

      mov     ax,offset SbIrqHandler
      mov     dx,seg SbIrqHandler
      mov     es:[bx],ax
      mov     es:[bx+2],dx

      mov     ax,es:[70h]     ; Ahora se configura el Timer
      mov     dx,es:[72h]
      mov     Word Ptr [TimerHandler],ax
      mov     Word Ptr [TimerHandler+2],dx

      mov     ax,offset SbPoll
      mov     dx,seg SbPoll
      mov     es:[70h],ax
      mov     es:[72h],dx

      mov     dx,[SbAddr]     ; Reseteamos el DSP
      add     dx,06h
      mov     al,1
      out     dx,al
      in      al,dx
      in      al,dx
      in      al,dx
      in      al,dx
      xor     al,al
      out     dx,al
      mov     cx,100
Esperar:        mov     dx,[SbAddr]     ; Se espera hasta que la SB conteste
      add     dx,0Eh
      in      al,dx
      or      al,al
      js      GetID
      loop    Esperar
      jmp     Exit
GetId:    mov     dx,[SbAddr]   ; Ya sabemos que est  la SB
      add     dx,0Ah        ; Ahora hay que encontrar el puerto BASE
      in      al,dx
      cmp     al,0AAh
SbOk:     je      SbOk
      loop    Esperar
      jmp     Exit

      mov     dx,[SbAddr] ; Ya se ha detectado el hardware
      add     dx,0Ch
      SbOut   0D1h
      mov     ax,1000
      mul     ax
      div     [MixSpeed]
      neg     al
      mov     ah,al
      mov     dx,[SbAddr] ; Convertimos la Frecuencia de Muestreo
      add     dx,0Ch      ; para que la SB la entienda
      SbOut   40h         ; Aqu¡ se establece
      SbOut   ah

      SbOut   14h        ; Se comienza la transferencia y se deja
      SbOut   0FFh       ; al DMA y al DSP que se comuniquen ellos
      Sbout   0FFh       ; solos sin mediaci¢n de la CPU

Exit:     pop     ax
      mov     cx,[SbIrq]
      mov     ah,1
      shl     ah,cl
      not     ah
      and     al,ah
      out     21h,al

      sti

      pop     es
      pop     ds
      popa
      ret

SBMOD_Play      EndP
 
 

;--------------------------------------------------------------------------
; SbDone:  Descarga el Driver de la Sound Blaster
;--------------------------------------------------------------------------

SBMOD_Stop      Proc Far

      pusha
      push    ds
      push    es

      mov     ax,@Data
      mov     ds,ax

      cli

      in      al,21h
      push    ax

      mov     al,11111111b
      out     21h,al

      xor     ax,ax
      mov     es,ax

      mov     bx,[SbIrq]
      add     bx,08h
      shl     bx,2

      mov     ax,Word Ptr [DmaHandler]
      mov     dx,Word ptr [DmaHandler+2]
      mov     es:[bx],ax
      mov     es:[bx+2],dx

      mov     ax,Word Ptr [TimerHandler]
      mov     dx,Word ptr [TimerHandler+2]
      mov     es:[70h],ax
      mov     es:[72h],dx

      mov     dx,[SbAddr]
      add     dx,0Ch
      SbOut   0D0h
      SbOut   0D3h

      pop     ax
      mov     cx,[SbIrq]
      mov     ah,1
      shl     ah,cl
      or      al,ah
      out     21h,al

      sti

      call    StopPlaying

      pop     es
      pop     ds
      popa
      ret

SBMOD_Stop      EndP
 

;--------------------------------------------------------------------------
; DetectSB:        Para detectar la presencia de la SB
;--------------------------------------------------------------------------

SBMOD_DetectSB  Proc Far

      ; Al salir: AX=0 si error, -1 si no error.

      push    ds
      push    es

      mov     ax,@Data          ; DS apunta a mi segmento de datos
      mov     ds,ax

      mov     bx,210h           ; Scaneo de puerto
              ; 210h, 220h, .. 260h

ReseteaDSP:     mov     dx,bx             ; Intento resetear el DSP.
      add     dx,06h
      mov     al,1
      out     dx,al
      in      al,dx
      in      al,dx
      in      al,dx
      in      al,dx
      xor     al,al
      out     dx,al
      add     dx,08h
      mov     cx,100
EsperarIdentif: in      al,dx
      or      al,al
      js      GetID2
      loop    EsperarIdentif

      jmp     SiguientPuerto
GetId2:    sub     dx,04h
      in      al,dx
      cmp     al,0AAh
      je      Encontrado
      add     dx,04h
      loop    EsperarIdentif

SiguientPuerto: add     bx,10h            ; Si no hay respuesta,
      cmp     bx,260h           ; se intenta con el siguiente puerto
      jbe     ReseteaDSP
      jmp     Fallo

Encontrado:   mov     [SbAddr],bx       ; Se encontr¢ la SB

      cli        ; Ahora se busca la IRQ de la SB

      in      al,21h            ; Salvamos el IMR.
      mov     bl,al

      mov     al,11111111b      ; Desactivamos todas las IRQ
      out     21h,al

      xor     ax,ax             ; Las provamos IRQ:2,3,5,7.
      mov     es,ax
              ; Primero las guardamos para luego
      mov     ax,es:[28h]       ; IRQ2
      mov     dx,es:[2Ah]
      push    ax
      push    dx

      mov     ax,es:[2Ch]       ; IRQ3
      mov     dx,es:[2Eh]
      push    ax
      push    dx

      mov     ax,es:[34h]       ; IRQ5
      mov     dx,es:[36h]
      push    ax
      push    dx

      mov     ax,es:[3Ch]       ; IRQ7
      mov     dx,es:[3Eh]
      push    ax
      push    dx
                ; Ahora las cambiamos
      mov     ax,offset TrapIrq2  ; IRQ2
      mov     es:[28h],ax
      mov     es:[2Ah],cs

      mov     ax,offset TrapIrq3  ; IRQ3
      mov     es:[2Ch],ax
      mov     es:[2Eh],cs

      mov     ax,offset TrapIrq5  ; IRQ5
      mov     es:[34h],ax
      mov     es:[36h],cs

      mov     ax,offset TrapIrq7  ; IRQ7
      mov     es:[3Ch],ax
      mov     es:[3Eh],cs

      mov     al,bl               ; Y habilitamos las IRQs 2,3,5,7.
      and     al,01010011b
      out     21h,al

      sti

      mov     [SbIrq],0           ; Borramos el nivel de la IRQ

      mov     dx,[SbAddr]         ; Le decimos a la SB que genere
      add     dx,0Ch              ; una IRQ
EsperarSb:      in      al,dx
      or      al,al
      js      EsperarSb
      mov     al,0F2h
      out     dx,al

      xor     cx,cx               ; Esperamos hasta que el nivel IRQ
EsperarIRQ:     cmp     [SbIrq],0           ; cambie, o se acabe el tiempo
      jne     IrqOk
      loop    EsperarIRQ

IrqOk:    mov     al,bl        ; La IRQ est  bien y restauramos el IMR
      out     21h,al

      cli                         ; Se restauran los vectores IRQ

      xor     ax,ax
      mov     es,ax

      pop     dx                  ; IRQ7
      pop     ax
      mov     es:[3Ch],ax
      mov     es:[3Eh],dx

      pop     dx                  ; IRQ5
      pop     ax
      mov     es:[34h],ax
      mov     es:[36h],dx

      pop     dx                  ; IRQ3
      pop     ax
      mov     es:[2Ch],ax
      mov     es:[2Eh],dx

      pop     dx                  ; IRQ2
      pop     ax
      mov     es:[28h],ax
      mov     es:[2Ah],dx

      cli

      cmp     [SbIrq],0           ; Se pregunta si el nive de IRQ
      je      Fallo               ; ha cambiado, si no, falla

      mov     ax,-1       ; Ha habido ‚xito, devolveremos -1
      jmp     DetectSalir

Fallo:          mov     ax,0

DetectSalir:    pop     es
      pop     ds
      ret

SBMOD_DetectSB  EndP

;---------------------------------------------------------------------------
; TrapIrq Para detectar las IRQ
;---------------------------------------------------------------------------

TrapIrq         Proc    Far

      push    dx                      ; General IRQ "trapper"
      push    ds                      ; Usado para la autodetecci¢n
                ; de las IRQ
      mov     dx,@Data
      mov     ds,dx
      mov     [SbIrq],ax              ; Salva el nivel IRQ
      mov     dx,[SbAddr]
      add     dx,0Eh
      in      al,dx                   ; SB reconocida
      mov     al,20h
      out     20h,al                  ; Hardware reconocido
      pop     ds
      pop     dx
      pop     ax
      iret                            ; Fin

      IRP Level,<2,3,5,7>
TrapIrq&Level:  push    ax
      mov     ax,Level
      jmp     TrapIrq
      EndM

TrapIrq         EndP
 

      End
 
 

PONERMOD.ASM
 
 

      Jumps
  .Model  large,C
      .286
 

;--------------------------------------------------------------------------
;               Publico
;--------------------------------------------------------------------------

      Global  StartPlaying: Proc, StopPlaying: Proc

      Global GetSamples: Proc
      Global MixSpeed: Word, ModInfo:Byte
 

;--------------------------------------------------------------------------
;               Constantes
;--------------------------------------------------------------------------

NumTracks       equ     4
DefTempo        equ     6
DefBpm          equ     125
MidCRate        equ     8448
MixBufSize      equ     4096
 

;--------------------------------------------------------------------------
;               Estructuras
;--------------------------------------------------------------------------

TrackInfo       Struc

Samples         dd      ?
Position        dw      ?
Len             dw      ?
Repeat          dw      ?
RepLen          dw      ?
Volume          db      ?
Error           db      ?
Period          dw      ?
Pitch           dw      ?
Effect          dw      ?
PortTo          dw      ?
PortParm        db      ?
VibPos          db      ?
VibParm         db      ?
OldSampOfs      db      ?
Arp             dw      ?,?,?
ArpIndex        dw      ?

TrackInfo       EndS
 
 

;--------------------------------------------------------------------------
;               Datos
;--------------------------------------------------------------------------

      .Data

SinTable        db      0,25,50,74,98,120,142,162,180,197,212,225
      db      236,244,250,254,255,254,250,244,236,225
      db      212,197,180,162,142,120,98,74,50,25

PeriodTable     dw      856,808,762,720,678,640,604,570,538,508,480,453
      dw      428,404,381,360,339,320,302,285,269,254,240,226
      dw      214,202,190,180,170,160,151,143,135,127,120,113

MixSpeed        dw      ?

ModInfo         Label   Byte

OrderLen        db      ?
ReStart         db      ?
Order           db      128 dup (?)
Patterns        dd      ?

SampOfs         dw      31 dup (?)
SampSeg         dw      31 dup (?)
SampLen         dw      31 dup (?)
SampRep         dw      31 dup (?)
SampRepLen      dw      31 dup (?)
SampVol         dw      31 dup (?)

OrderPos        db      ?
Tempo           db      ?
TempoWait       db      ?
Bpm             db      ?
Row             db      ?
BreakRow        db      ?
BpmSamples      dw      ?
BufPtr          dw      ?
BufLen          dw      ?
BufRep          dw      ?
Note            dd      ?
Tracks          TrackInfo NumTracks dup (?)

PitchTable      dw      857 dup (?)
VolTable        db      16640 dup (?)
MixBuffer       db      MixBufSize dup (?)
 
 

;--------------------------------------------------------------------------
;               C¢digo.
;--------------------------------------------------------------------------

      .Code

;--------------------------------------------------------------------------
; BeatTrack:  Procesa el nuevo "beat" en un track
;  In:
;    ds:di -  Informaci¢n de la direcci¢n del Track
;--------------------------------------------------------------------------

BeatTrack       Proc    Near

      mov     dx,[di+Effect]
      test    dx,dx
      je      None
      cmp     dh,00h
      je      Arpeggio
      cmp     dh,01h
      je      PortUp
                cmp     dh,02h
                je      PortDown
                cmp     dh,03h
                je      TonePort
                cmp     dh,04h
                je      Vibrato
                cmp     dh,05h
                je      PortSlide
                cmp     dh,06h
                je      VibSlide
                cmp     dh,0Ah
                je      VolSlide
None:           ret
 

Arpeggio:       mov     bx,[di+ArpIndex]
                mov     ax,[di+Arp+bx]
                mov     [di+Pitch],ax
                add     bx,2
                cmp     bx,6
                jb      SetArpIndex
                xor     bx,bx
SetArpIndex:    mov     [di+ArpIndex],bx
                ret
 

PortUp:         xor     dh,dh
                mov     bx,[di+Period]
                sub     bx,dx
                cmp     bx,113
                jge     NotSmall
                mov     bx,113
NotSmall:       mov     [di+Period],bx
                add     bx,bx
      mov     ax,[PitchTable+bx]
                mov     [di+Pitch],ax
                ret
 

PortDown:       xor     dh,dh
                mov     bx,[di+Period]
                add     bx,dx
                cmp     bx,856
                jle     NotBig
      mov     bx,856
NotBig:         mov     [di+Period],bx
                add     bx,bx
                mov     ax,[PitchTable+bx]
                mov     [di+Pitch],ax
                ret
 

TonePort:       xor     dh,dh
                mov     ax,[di+PortTo]
                mov     bx,[di+Period]
                cmp     bx,ax
                je      NoPort
                jg      PortToUp
PortToDown:     add     bx,dx
                cmp     bx,ax
                jle     SetPort
FixPort:        mov     bx,ax
                jmp     SetPort
PortToUp:       sub     bx,dx
                cmp     bx,ax
                jl      FixPort
SetPort:        mov     [di+Period],bx
                add     bx,bx
                mov     ax,[PitchTable+bx]
                mov     [di+Pitch],ax
NoPort:         ret
 

Vibrato:        mov     dh,dl
                and     dl,0Fh
                shr     dh,4
                shl     dh,2
      add     [di+VibPos],dh
                mov     dh,[di+VibPos]
      mov     bl,dh
                shr     bl,2
                and     bx,1Fh
                mov     al,[SinTable+bx]
                mul     dl
                rol     ax,1
                xchg    al,ah
                and     ah,1
                test    dh,dh
                jns     VibUp
      neg     ax
VibUp:          add     ax,[di+Period]
                mov     bx,ax
                cmp     bx,113
                jge     NoLoVib
                mov     bx,113
NoLoVib:        cmp     bx,856
                jle     NoHiVib
                mov     bx,856
NoHiVib:        add     bx,bx
                mov     ax,[PitchTable+bx]
                mov     [di+Pitch],ax
                ret
 

PortSlide:      call    VolSlide
                mov     dl,[di+PortParm]
                jmp     TonePort
 

VibSlide:       call    VolSlide
                mov     dl,[di+VibParm]
                jmp     Vibrato
 

VolSlide:       mov     dh,dl
                and     dl,0Fh
                shr     dh,4
                mov     al,[di+Volume]
                sub     al,dl
                jge     NoLoVol
                xor     al,al
NoLoVol:        add     al,dh
      cmp     al,64
      jbe     NoHiVol
      mov     al,64
NoHiVol:        mov     [di+Volume],al
      ret

BeatTrack       EndP
 

;--------------------------------------------------------------------------
; GetTrack:   Coge la siguiente nota del patr¢n
;  In:
;    ds:di -  Direcci¢n de la informaci¢n del track
;    es:si -  Direcci¢n de la nota el patr¢n
; Out:
;    es:si -  La nueve direcci¢n de la nota del patr¢n
;--------------------------------------------------------------------------

GetTrack        Proc    Near

                seges   lodsw
                xchg    al,ah
                mov     bl,ah
                and     ah,0Fh
                mov     cx,ax
                seges   lodsw
                xchg    al,ah
                mov     bh,ah
                and     ah,0Fh
                mov     dx,ax
                mov     [di+Effect],dx
                and     bl,0F0h
                shr     bh,4
                or      bl,bh
                je      SetPeriod

SetSample:      xor     bh,bh
                dec     bx
                add     bx,bx
                mov     ax,[SampVol+bx]
                mov     [di+Volume],al
                mov     ax,[SampOfs+bx]
                mov     Word Ptr [di+Samples],ax
                mov     ax,[SampSeg+bx]
                mov     Word Ptr [di+Samples+2],ax
                mov     ax,[SampLen+bx]
                mov     [di+Len],ax
                mov     ax,[SampRep+bx]
                mov     [di+Repeat],ax
                mov     ax,[SampRepLen+bx]
      mov     [di+RepLen],ax

SetPeriod:      test    cx,cx
                je      SetEffect

                mov     [di+PortTo],cx
                cmp     dh,03h
                je      SetEffect

                mov     [di+Period],cx
      mov     bx,cx
                add     bx,bx
                mov     ax,[PitchTable+bx]
                mov     [di+Pitch],ax
                mov     [di+Position],0

SetEffect:      test    dx,dx
                je      InitNone
                cmp     dh,00h
                je      InitArpeggio
                cmp     dh,03h
                je      InitTonePort
                cmp     dh,04h
                je      InitVibrato
                cmp     dh,09h
                je      SampleOfs
                cmp     dh,0Bh
                je      PosJump
                cmp     dh,0Ch
                je      SetVolume
                cmp     dh,0Dh
                je      Break
                cmp     dh,0Fh
                je      SetSpeed
InitNone:       ret
 

InitTonePort:   test    dl,dl
                jne     SetPortParm
                mov     dl,[di+PortParm]
SetPortParm:    mov     [di+PortParm],dl
                mov     [di+Effect],dx
                ret
 

InitVibrato:    mov     al,[di+VibParm]
                mov     ah,al
                and     al,0Fh
                and     ah,0F0h
                test    dl,0Fh
                jne     OkDepth
                or      dl,al
OkDepth:        test    dl,0F0h
                jne     OkRate
                or      dl,ah
OkRate:         mov     [di+VibParm],dl
                mov     [di+Effect],dx
                test    cx,cx
                je      OkPos
                mov     [di+VibPos],0
OkPos:          ret
 

SampleOfs:      test    dl,dl
                jne     SetSampleOfs
                mov     dl,[di+OldSampOfs]
SetSampleOfs:   mov     [di+OldSampOfs],dl
                mov     dh,dl
                xor     dl,dl
                mov     [di+Position],dx
                ret
 

PosJump:        mov     [OrderPos],dl
                mov     [Row],64
                ret
 

SetVolume:      cmp     dl,64
                jbe     OkVol
                mov     dl,64
OkVol:          mov     [di+Volume],dl
                ret
 

Break:          mov     dh,dl
                and     dl,0Fh
                shr     dh,4
                add     dh,dh
                add     dl,dh
      shl     dh,2
                add     dl,dh
                mov     [BreakRow],dl
                mov     [Row],64
                ret
 

SetSpeed:       test    dl,dl
                je      Skip
                cmp     dl,31
      ja      SetBpm
SetTempo:       mov     [Tempo],dl
                mov     [TempoWait],dl
                ret
SetBpm:         mov     [Bpm],dl
                mov     al,103
                mul     dl
                mov     bl,ah
                xor     bh,bh
                mov     ax,[MixSpeed]
                xor     dx,dx
                div     bx
                mov     [BpmSamples],ax
Skip:           ret
 

InitArpeggio:   mov     dh,dl
                and     dl,0Fh
                shr     dh,4
                mov     cx,36
                xor     bx,bx
                mov     ax,[di+Period]
ScanPeriod:     cmp     ax,[PeriodTable+bx]
                jae     SetArp
                add     bx,2
                loop    ScanPeriod
SetArp:         add     dx,dx
                add     dh,bl
                add     dl,bl
                mov     bx,[PeriodTable+bx]
                add     bx,bx
                mov     ax,[PitchTable+bx]
                mov     [di+Arp],ax
                mov     bl,dh
                xor     bh,bh
      mov     bx,[PeriodTable+bx]
                add     bx,bx
                mov     ax,[PitchTable+bx]
                mov     [di+Arp+2],ax
                mov     bl,dl
                xor     bh,bh
                mov     bx,[PeriodTable+bx]
                add     bx,bx
                mov     ax,[PitchTable+bx]
                mov     [di+Arp+4],ax
      mov     [di+ArpIndex],0
      ret

GetTrack        EndP
 

;--------------------------------------------------------------------------
; UpdateTracks:  C¢digo principal para procesar el nuevo "tick"
;--------------------------------------------------------------------------

UpdateTracks    Proc    Near

      dec     [TempoWait]
      je      GetTracks

BeatTracks:     Ofs = offset Tracks
                Rept NumTracks
                  mov   di,Ofs
                  call  BeatTrack
                  Ofs = Ofs + Size TrackInfo
                EndM

                ret

GetTracks:      mov     al,[Tempo]
                mov     [TempoWait],al

                les     si,[Note]
                cmp     [Row],64
                jb      NoPattWrap

                les     si,[Patterns]
                mov     bl,[OrderPos]
                cmp     bl,[OrderLen]
                jb      NoOrderWrap
      mov     bl,[ReStart]
                mov     [OrderPos],bl
                cmp     bl,[OrderLen]
                jae     NoUpdate
NoOrderWrap:    xor     bh,bh
                mov     bl,[Order+bx]
                shl     bx,10
                add     si,bx
                mov     bl,[BreakRow]
                mov     [Row],bl
                xor     bh,bh
                mov     [BreakRow],bh
                shl     bx,4
                add     si,bx
                mov     Word Ptr [Note],si
                mov     Word Ptr [Note+2],es
                inc     [OrderPos]
NoPattWrap:     inc     [Row]

                cld
                Ofs = offset Tracks
                Rept NumTracks
                  mov   di,Ofs
                  call  GetTrack
                  Ofs = Ofs + Size TrackInfo
                EndM

      mov     Word Ptr [Note],si
NoUpdate:       ret

UpdateTracks    EndP
 

;--------------------------------------------------------------------------
; MixTrack:  Mezcla un track en un buffer limpio
;  In:
;   ds:si -  Direcci¢n de la informaci¢n del track
;   ds:di -  Direcci¢n del buffer
;    cx   -  Tama¤o del Buffer
;--------------------------------------------------------------------------

MixTrack        Proc    Near

      cmp     [si+RepLen],2
      ja      MixLooped

MixNonLooped:   les     dx,[si+Samples]
      mov     bx,[si+Position]
      mov     bp,[si+Len]
      push    dx
      push    si
      add     bx,dx
      add     bp,dx
      mov     dx,[si+Pitch]
      mov     al,[si+Volume]
      mov     ah,[si+Error]
      mov     si,bx
      mov     bh,al
      mov     al,dl
      mov     dl,dh
      xor     dh,dh
nlMixSamp:      cmp     si,bp
      jae     nlMixBye
                mov     bl,es:[si]
                mov     bl,[VolTable+bx]
                add     [di],bl
                inc     di
                add     ah,al
                adc     si,dx
                loop    nlMixSamp
nlMixBye:       mov     bx,si
                pop     si
                pop     dx
      sub     bx,dx
                mov     [si+Position],bx
                mov     [si+Error],ah
                ret

MixLooped:      les     dx,[si+Samples]
                mov     bx,[si+Position]
                mov     bp,[si+RepLen]
                mov     [BufRep],bp
                add     bp,[si+Repeat]
      push    dx
                push    si
                add     bx,dx
                add     bp,dx
                mov     dx,[si+Pitch]
                mov     al,[si+Volume]
                mov     ah,[si+Error]
      mov     si,bx
                mov     bh,al
                mov     al,dl
                mov     dl,dh
                xor     dh,dh
lpMixSamp:      cmp     si,bp
                jb      lpMixNow
                sub     si,[BufRep]
lpMixNow:       mov     bl,es:[si]
                mov     bl,[VolTable+bx]
                add     [di],bl
                inc     di
                add     ah,al
                adc     si,dx
                loop    lpMixSamp
lpMixBye:       mov     bx,si
                pop     si
                pop     dx
                sub     bx,dx
                mov     [si+Position],bx
                mov     [si+Error],ah
                ret

MixTrack        EndP
 

;--------------------------------------------------------------------------
; GetSamples:  Devueve el siguiente trozo del sample que se va a tocar
;  In:
;    Buffer  - Direcci¢n del Buffer
;    Count   - Tama¤o del Buffer
;--------------------------------------------------------------------------

GetSamples      Proc Pascal Buffer:DWord, Count:Word

      pusha
      push    ds
      push    es
      cld

      les     di,[Buffer]
      mov     bx,[Count]

NextChunk:      cmp     [BufLen],0
                jne     CopyChunk

                push    bx
                push    di
                push    es

MixChunk:       lea     di,[MixBuffer]
                mov     cx,[BpmSamples]
                mov     [BufPtr],di
                mov     [BufLen],cx

                mov     ax,ds
                mov     es,ax
                mov     al,80h
                rep     stosb

                Ofs = offset Tracks
                Rept NumTracks
                  mov   si,Ofs
                  mov   di,[BufPtr]
                  mov   cx,[BufLen]
                  call  MixTrack
                  Ofs = Ofs + Size TrackInfo
                EndM
                call    UpdateTracks

                pop     es
                pop     di
                pop     bx

CopyChunk:      mov     cx,[BufLen]
      cmp     cx,bx
                jbe     MoveChunk
                mov     cx,bx

MoveChunk:      mov     si,[BufPtr]
                add     [BufPtr],cx
                sub     [BufLen],cx
                sub     bx,cx
                rep     movsb
                test    bx,bx
                jne     NextChunk

                pop     es
      pop     ds
                popa
      ret

GetSamples      EndP
 

;--------------------------------------------------------------------------
; StartPlaying: Inicializa el sistema de sonido
;  In:
;   Informaci¢n del m¢dulo
;--------------------------------------------------------------------------

StartPlaying    Proc

      pusha
      push    ds
      push    es

SetModParms:    mov     [OrderPos],0
      mov     [Tempo],DefTempo
      mov     [TempoWait],DefTempo
      mov     [Bpm],DefBpm
      mov     [Row],64
      mov     [BreakRow],0
      mov     ax,[MixSpeed]
      xor     dx,dx
      mov     bx,24*DefBpm/60
      div     bx
      mov     [BpmSamples],ax

ClearTracks:    mov     di,offset Tracks
      mov     ax,ds
                mov     es,ax
                mov     cx,NumTracks * (Size TrackInfo)
                xor     ax,ax
                cld
                rep     stosb

                mov     [BufPtr],ax
                mov     [BufLen],ax

MakePitch:      mov     ax,MidCRate
                mov     bx,428
                mul     bx
      div     [MixSpeed]
                xor     dh,dh
      mov     dl,ah
                mov     ah,al
                xor     al,al
                mov     cx,857
                xor     bx,bx
                mov     di,offset PitchTable
PitchLoop:      push    ax
                push    dx
                cmp     dx,bx
      jae     NoDiv
      div     bx
NoDiv:          stosw
      pop     dx
      pop     ax
      inc     bx
      loop    PitchLoop

MakeVolume:     mov     cx,16640
      mov     bx,cx
VolLoop:        dec     bx
      mov     al,bl
      imul    bh
      mov     [VolTable+bx],ah
      loop    VolLoop

      pop     es
      pop     ds
      popa
      ret

StartPlaying    EndP
 

;--------------------------------------------------------------------------
; StopPlaying: Descarga todo
;--------------------------------------------------------------------------

StopPlaying     Proc

                ret

StopPlaying     EndP
 

      End
 




AULA MACEDONIA
a
MACEDONIA Magazine